Time, Relativity and Distributed Companies

“The only reason for time is so that everything doesn’t happen at once.”
-Albert Einstein

One of the most difficult parts of communication within a fully distributed company is dealing with timezones. I can’t count the number of times that someone has emailed me asking “Can we have a meeting at 10 a.m.?” The obvious question here is which 10 a.m. are you asking about? Are you asking about your 10 a.m. or my 10 a.m.?  Unfortunately, it feels pedantic to ask the person to clarify what they mean, but it matters if you want everyone to show up at the same moment in time!

As I’ve mentioned before, we use Slack in place of meetings for a lot of internal communication, but we do still need to jump on phone calls from time to time. One of the skills that I’ve had to learn when trying to setup meetings is to be very explicit about the time that you mean. Think it’s easy? Try this quick little quiz. Figure out what time it is right now in the following U.S. cities, without using a map:

  1. Las Vegas
  2. Nashville
  3. New Orleans
  4. Phoenix
  5. Detroit
  6. Cleveland
  7. Louisville
  8. Pittsburgh
  9. Milwaukee
  10. Boise

I’m betting that while you might have fairly reasonable guesses, you’re not 100% positive on all of them. A few of them (like Phoenix) are particularly tricky! My recommendation is that when working with people in different timezones, it’s best if you declare the time and timezone not only for yourself but for the person you’re trying to invite. For example, let’s say that you are located in Austin, Texas and I am located in Portland, Oregon, and I want us to have a meeting at 11 in the morning. (Portland is in the Pacific Time Zone and Austin is in the Central Time Zone). I would probably ask you something like this:

     “Are you available for a call at 11:00 a.m. Pacific (1:00 p.m. Central)?”

By communicating it this way, I’m communicating to you that:

  1. We are not both in the same timezone
  2. I would like to talk to you at 1:00 in the afternoon
  3. It will still be 11:00 in the morning for me

This might seem like an obvious thing, but by being explicit, it helps remove any ambiguity and improves the overall quality of the communication. It also removes any assumption that the person you’re communicating with understands what timezones different cities are located in. The most important skill that you can develop in a distributed organization is your ability to communicate, and one of the easiest ways to improve is to become better at communicating time and timezones when talking to people.

Distributed Companies Are Real

When we first set out to build Silverpine, we didn’t really have much of a plan. All we knew was that the fates had aligned, and that it was our time to set out on our own. From our very first day, we have managed to bootstrap the business which was ultimately very beneficial, however, bootstrapping is hard. Very hard. While we grappled with unknown cashflows and even more unknown project pipelines, we knew we had to scrimp and save and keep our costs as low as we possibly could. One major way we were able to do that was by making Silverpine a “virtual” business in that we had no physical office space. It also didn’t hurt that neither my partner Ryan nor I wanted a commute, so it definitely felt like a win/win situation.

For the first few years of our existence, our staff consisted of only Ryan and myself and an occasional subcontractor or two. Working remote became an unstated, simple to implement company policy that we grew to appreciate implicitly, and the freedom that it lent to us quickly became a de facto benefit. As we grew as a company, however, the true value began to emerge.

When we finally hired our first full time employee, working remotely was still an implied benefit. At the same time, we started noticing a trend that many of the best engineers and developers that we knew were explicitly looking for new positions with significant remote work opportunities. However, when our first employee notified us that she was going to move to a rural area, it truly started to dawn on us what it meant for recruiting and retainment. Suddenly, this quirky company policy, that had just organically happened, had become an important pillar of our company culture.

At that point, Ryan and I decided that we were going to commit to Silverpine being a fully distributed organization. We abandoned any intention of developing a physical footprint and started viewing our evolving company through that lens. As we continued to grow and hire, I had to unlearn some of the things that had been ingrained in me from my time in the corporate world and from my MBA classes. I had to really dig in to understanding the tradeoffs of being distributed, partially because we needed to adopt tools and policies that would work well for remote employees, but also because we needed to be able to speak to our clients about how we were different from similar agencies and ultimately, why our distributed nature would benefit them.

For a long time, whenever a prospective client would ask us where we were located, I would make some sort of joke that we were following the “IBM model” even though it wasn’t really an accurate comparison. I would then do some general hand waving about what that meant, but more often than not, I was left with the distinct feeling that we were sometimes viewed as not being a legitimate company. Because of my approach to communicating our structure, I’m certain that we lost more than a couple bids on projects because of this.

Fortunately, as time progressed, many other companies started to legitimize remote work. Companies like Automattic, Basecamp, InVision and Zapier have literally written the book on how to have a remote team, and they have shown that it can work at scale. People have started to notice how these companies operate and thrive, and maybe most importantly, many of the best engineers and developers have started to view remote opportunities as a non-negotiable job requirement. I have run into people time and again at conferences and other work-related events where they explain that having a remote position is often times more important than a salary bump. That means that there is an actual, tangible economic value to a company that embraces remote work.

For Silverpine, we have become better at articulating the legitimacy of our remote nature in a way that better portrays it as a competitive advantage. We talk about the engagement and happiness levels of our employees. We talk about the quality of communication that our team practices on a daily basis. And, we talk about lower base costs which translates to lower project costs. We also occasionally talk about the tools and the processes and the intentionality of it that helps craft our company culture. All of this is important in explaining our story and our organization because there are still plenty of people with an incorrect understanding of remote companies.

I am convinced that the model we stumbled upon (but ultimately embraced) is a blueprint for long term success. It allows us a flexibility and nimbleness that other corporations simply can’t match, and in the ever-changing world that we live in, flexibility is a survival trait. As the Japanese proverb states: “The Bamboo that bends is stronger than the Oak that resists.”

We are definitely still learning and adapting how we function and operate, but I no longer act sheepish or apologize for being a remote company. I am proud of what we are building and what Silverpine has become. (It also doesn’t hurt that our track record is pretty great!) So, if you are thinking about working at a remote company or thinking about adopting remote-friendly policies, don’t approach it as some odd-ball thing. Take some time and read about what/how other companies that are doing it, and recognize that distributed companies are real.

Tools for a Distributed Software Agency

One of the things that I am most proud of is that Silverpine is a 100% distributed company. Often when people find out that we are fully remote, they will ask curiously about what tools we use to work together. This is completely understandable because the importance of having the right tool set is magnified for remote companies. We understand this innately and as such we are constantly evaluating our software stack. The following list represents the software that powers our business. (I have intentionally omitted some of the lower level development tools like Xcode and Android Studio.) The list is broken into four primary classifications: communication, development, project execution, and finance.

Communication

Slack 

Before Slack, we used a hodge lodge of messaging tools like AIM, Google Chat and even old school SMS. It was horrible. Slack is the single most important tool that we use to communicate with each other and with our clients. All of our employees and contractors use it extensively every day, and even though I think that there should be some middle ground in their pricing between the paid and the pro plans, I can’t imagine trying to work remotely without it.

Webex

Let me just preface this by clarifying that I think that every single conference calling platform is terrible. I have used them all. From Zoom.io to Google Hangouts to AT&T Connect, they are just barely workable. Besides the all too common call drops they also all seem to suffer from ridiculous installation processes and byzantine user interfaces (Does this yellow button state mean my microphone is on mute or can they hear me?)

That being said, we have been using Webex for a very long time; not because it is good, but because it is better than the alternatives. And for our enterprise clients, it is somewhat of a known entity so we seem to spend less time per call doing the “can you hear me” dance. I wouldn’t say that I recommend Webex. It’s just what we use.

Dropbox Pro

We have been using Dropbox on personal plans for quite a while, but we recently decided to standardize on Dropbox Pro for file sharing. All of our projects have quite a bit of documentation, graphical assets and other large files that aren’t well suited for source control tools. Dropbox allows us to create per project file drops that we can easily access as well as share with other people when appropriate. We almost switched to Box.com because their pro plans have unlimited storage but ultimately decided it would be less transitional headache to just upgrade our existing Dropbox plans.

G Suite

We have been using Google for our email and calendar services for so long that our silverpinesoftware.com domain is still functioning under the original beta operating agreement. If G Suite disappeared I honestly wouldn’t even know where to start looking for a replacement. File this one under “it just works.”

Development

InVision

One of Silverpine’s guiding design principles is that every user interface needs to have a beautiful “feel” to it, and that you simply can’t judge the feel of an app until you can hold it in your hand and interact with it. Because of this philosophy,  we have refined our development process over time to rely heavily on InVision to prototype the UI and UX of our apps before we ever even start writing code. The amount of time and pain it saves both us and our clients cannot be overstated. If you design for mobile, you really should be using InVision or something like it.

GitHub

If you write software, you should be using a source control platform. If you need a source control platform you should be using GitHub. If you’re using something else, I’m sure you have a reason for it, but it’s probably not a very good reason. (All of our projects use GitHub repositories so when they changed their pricing model to be per user rather than per repository, it made our lives a lot easer.)

Azure DevOps

This one might surprise some people, but a couple years ago we transitioned to what is now known as Microsoft Azure DevOps for our automated build system and have been using it ever since. Prior to Azure DevOps we had used a variety of tools including TestFlight (bought by Apple), Fabric (bought by Twitter, then bought by Google), and BuddyBuild (ran out of money). Due to intense consolidation in that particular sector, we were frequently having to retroactively change our toolset which was both time consuming and costly. A friend of mine who works on the Microsoft tools team encouraged us to give Azure DevOps a try, and we have been extremely happy with that decision. Azure DevOps supports both iOS and Android, is massively configurable, has 24/7 support and most importantly, is backed by one of the largest companies in the world so it won’t be disappearing any time soon. If you need an automated build system and haven’t taken a look at Azure, I highly recommend at least kicking its tires.

Project Execution

Basecamp

For many years, we wandered in the desert of project management, largely piggybacking on whatever project management tools our clients happened to be using at the time. As such, we have used everything from Jira to Asana to Microsoft Excel to track projects and tasks. However, in the past year we have implemented Basecamp as our standard internal project tracking tool. One of the things I like best about Basecamp is that it has clearly been thoughtfully designed. Not only is it powerful, but its design somehow works to ensure that it doesn’t become overly burdensome in the same way that other similarly complex tools do.

Lighthouse

If there was one piece of web software that I would invest internal Silverpine resources on, it would be a lightweight bug tracking tool. There just aren’t many platforms out there that can strike a balance of utility and ease of use that errs on the side of ease of use. For now, Lighthouse foots the bill for us in that regard, however, I’m not sure how much longer it will be around. There hasn’t been any significant development done on it in the 6+ years that we’ve been using it, so I’m not sure I would necessarily recommend it. That being said, it does what we need bug tracking software to do, and it does it well, and I haven’t found a replacement. If you have any personal favorites, please let me know.

Finance

Blinksale

Silverpine is a services business and sending invoices to our customers is literally how we are able to make money. Blinksale is the tool we use to send those invoices and look like we are professionals in the process. While it isn’t a complex tool, it expertly does what we need it to do: send and track professional looking invoices. If you send invoices to clients, you really should be using a tool like Blinksale because people can tell when you don’t.

Quickbooks

Nobody really loves Intuit. They have created not one, but two near monopolies with TurboTax and Quickbooks. However, if you run a business, you need to track your finances in a way that your CPA can help you with your taxes at the end of the year, and if you tell your CPA that you use anything other than Quickbooks, they will not be happy with you and they will very likely take longer to do your taxes which means you will end up with a higher bill from them. That is the reality of Quickbooks and that is why we use it.

Gusto

If you have employees or sub-contractors that you need to pay, you really should be using Gusto. The folks at Gusto are wizards when it comes to dealing with payroll taxes and W-9’s and a great deal more things that I simply don’t have to worry about because we use their service. Not only is the Gusto platform super easy to use, but their customer service team is actually pro-active in notifying us of upcoming tax law changes that might affect us. I am continually in awe of how great Gusto is and cannot say enough good things about them.

 

Unintended Consequences

Disclaimer:
This post comes from my old blog back in 2004. I’m reposting it here so that I don’t lose the content. The source was hand-written HTML so the formatting probably appears a bit off.

About seven years ago I wrote some code to do Mu-Law and A-Law compression. About six years ago, I decided to publish an article along with the source code for it. You can find it here. Anyway, the other day I received an email from someone who had taken it and modified it for what he was doing. In doing so, he found a piece of misinformation that has been in my article since I originally published it. Not a big deal, and I intend to rectify the issue. However, as we chatted over email, I asked him what he was using Mu-Law/A-Law compression for. Here is a clip from our email:

> So if you don’t mind me asking, what are you working on that has 13 bit

> unsigned audio input?

Sure. I am designing a system that monitors lots of radio stations to capture and

detect Emergency Alert System activations – those ugly tones and occasional

voice messages you hear on the radio. The system has some rather incredible

shortcomings for a critical system in 2005. When the emergency management

office triggers an alert they have no way of knowing whether or not radio stations

actually broadcast the alert. Sometimes the system fails – too often. So our

system listens to the stations and sends a report back to Emergency HQ. In

most cases an exception report that shows which stations did not properly

send out the alert. So if the dam is breaking or the nuke is going critical they

can try again, use the phone, send a helicopter or something.

 

Whoa. Code that I originally wrote to compress audio in children’s games is now being used to help monitor Emergency situations. Talk about your unintended consequences!

-Jon

Translating Hardware Exceptions to C++ Exceptions

Disclaimer:
This post comes from my old blog back in 2004. I’m reposting it here so that I don’t lose the content. The source was hand-written HTML so the formatting probably appears a bit off.

No matter how careful of a programmer you are, there will always be times when a hardware
exception will occur in your code. Perhaps it was a third party component that was the
culprit. Perhaps, it was a fellow co-worker that broke something. Or maybe it was
Microsoft itself not playing fair with its documentation and/or implementations.
Whatever the case, it is often very useful to be able to capture a run-time exception
that was generated by the CPU. Sure, you can use a catch(…) to be your fail-safe, but
wouldn’t it be great to be able to convert that exception that was generated by the hardware
into a C++ exception? I created this class in order to do that very thing. In fact,
this class was the basis for my super assert that I created, because I found that I could
cause a hardware exception any time I wanted, and by using this C++ hardware exception container,
I could access each thread’s stack frame at run-time. This would eventually enable me to
perform a stack trace inside of an assert, but I will explain that more in a different
tutorial.

Anyway, I hope that this is useful to someone. I spent a while digging around in the
mire that is Microsoft’s documentation before I put this together. Perhaps this will save
someone else time in the future.

Enjoy.

-BossHogg

#ifndef HARDWARE_EXCEPTION
#define HARDWARE_EXCEPTION 1


enum HWExceptionType
{
	eIllegalMemoryAccess	= EXCEPTION_ACCESS_VIOLATION,
	eUnexpectedBreakpoint 	= EXCEPTION_BREAKPOINT,
	eDataTypeMisalignment 	= EXCEPTION_DATATYPE_MISALIGNMENT,
	eSingleStepInstruction 	= EXCEPTION_SINGLE_STEP,
	eArrayBoundsExceeded	= EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
	eDenormalFloat 		= EXCEPTION_FLT_DENORMAL_OPERAND,
	eFloatDivideByZero 	= EXCEPTION_FLT_DIVIDE_BY_ZERO,
	eFloatInexactResult	= EXCEPTION_FLT_INEXACT_RESULT,
	eFloatInvalidOperation 	= EXCEPTION_FLT_INVALID_OPERATION,
	eFloatOverflow 		= EXCEPTION_FLT_OVERFLOW,
	eFloatStackCorrupted 	= EXCEPTION_FLT_STACK_CHECK,
	eFloatUnderflow 	= EXCEPTION_FLT_UNDERFLOW,
	eIntDivideByZero 	= EXCEPTION_INT_DIVIDE_BY_ZERO,
	eIntOverflow 		= EXCEPTION_INT_OVERFLOW,
	ePrivelegedInstruction 	= EXCEPTION_PRIV_INSTRUCTION,
	eUncontinuableException = EXCEPTION_NONCONTINUABLE_EXCEPTION
};


class HWException
{
     public:
          HWException(HWExceptionType aType,
                      EXCEPTION_POINTERS* pExp):
	       itsCategory(aType),
	       itsPointers(pExp),
	       itsLocation(pExp->ExceptionRecord->ExceptionAddress)
          {
	  }

	  HWExceptionType     GetCategory()  const {return itsCategory;}
	  DWORD		      GetLocation()  const {return itsLocation;}
	  EXCEPTION_POINTERS* GetSysPointer()const {return itsPointers;}

     protected:
          HWExceptionType	itsCategory;
	  DWORD			itsLocation;
	  EXCEPTION_POINTERS*	itsPointers;
};


static void HWTranslateException(unsigned int u, 
                                 EXCEPTION_POINTERS* pExp)
{
	throw HWException((HWExceptionType)u,pExp);
}

#endif



///////////////////////////////////////////////////////////////////////
Example usage:
///////////////////////////////////////////////////////////////////////

#include "windows.h"
#include "HWException.h"


int main()
{
	//Note, setting the exception translator must be done 
	//on a per thread basis.
	_set_se_translator(HWTranslateException);

	try {
		//This will cause an access violation
		char* ptr = NULL;
		*ptr = 5; 	
	}
	catch (HWException& e)
	{
		//We can now know both the type and the
		//memory location of the instruction that
		//caused the exception.  Cool!

		HWExceptionType exceptionType = e.GetCategory();
		DWORD address = e.GetLocation();
	}
	catch (...)
	{
		//If we got here, then it was some other kind
		//of C++ exception...
	}

	return 0;
}


CPU Detection Code

Disclaimer:
This post comes from my old blog back in 2004. I’m reposting it here so that I don’t lose the content. The source was hand-written HTML so the formatting probably appears a bit off.

CPU Detection Code

I dug this code up from a project that I worked on a long time ago. Unfortunately,
it is woefully out of date, especially with any of the latest P4 processors.
Also unfortunately, I don’t have a large suite of machines on which to test this,
however, I have verified a large number of these, but not all.
Also missing from the list are any AMD processors since my old companies didn’t
explicitly support AMD. Oh, well. As always, this code is to be used at your own
expense, and I guess with this particular set of code, that means a little more.
Anyway, I hope someone finds this interesting, and if you have any questions,
feel free to ask.

-BossHogg



#include “windows.h”

bool QueryCPUID();

bool QueryMMX();

bool QueryHyperThreading();

void QueryVendorString(char* string);

bool QuerySerialNumber(char* string);

void GetCPUInfoString(char* string);

unsigned long QueryCacheSize();

unsigned long QueryCPUCount();

unsigned char QueryCPUModel();

unsigned char QueryCPUFamily();

unsigned char QueryCPUStepping();

unsigned char QueryCPUType();

bool Is8086()

{

int is8086=0;

__asm {

pushf

pop ax

mov cx, ax

and ax, 0fffh

push ax

popf

pushf

pop ax

and ax, 0f000h

cmp ax, 0f000h

mov is8086, 0

jne DONE_8086_CHECK

mov is8086, 1

DONE_8086_CHECK:

};

return !!is8086;

}

bool Is80286()

{

int is80286=0;

__asm {

smsw ax

and ax, 1

or cx, 0f000h

push cx

popf

pushf

pop ax

and ax, 0f000h

mov is80286, 1

jz DONE_80286_CHECK

mov is80286, 0

DONE_80286_CHECK:

};

return !!is80286;

}

bool Is80386()

{

int is80386=0;

__asm {

pushfd

pop eax

mov ecx, eax

xor eax, 40000h

push eax

popfd

pushfd

pop eax

xor eax, ecx

mov is80386, 1

jz DONE_80386_CHECK

mov is80386, 0

DONE_80386_CHECK:

};

return !!is80386;

}

bool QueryCPUID()

{

int hasCPUID=0;

__asm

{

pushfd

pop eax

mov ecx, eax

and ecx, 0x00200000

xor eax, 0x00200000

push eax

popfd

pushfd

pop eax

and eax, 0x00200000

xor eax, ecx

mov hasCPUID, eax

};

return !!hasCPUID;

}

bool QueryMMX()

{

bool canDoMMX=false;

__asm

{

mov eax, 1 ; request for feature flags

_emit 0x0F ; CPUID on Pentiums is 0f,a2

_emit 0xA2

test edx, 0x00800000 ; is MMX technology Bit(bit 23)in feature

jz DONE_MMX_CHECK ; flags equal to 1

mov canDoMMX,1

DONE_MMX_CHECK:

};

return canDoMMX;

}

bool QueryHyperThreading()

{

unsigned int regEdx = 0;

unsigned int regEax = 0;

unsigned int vendorId[3] = {0, 0, 0};

if (!QueryCPUID())

return false;

__asm

{

xor eax, eax // call cpuid with eax = 0

cpuid // Get vendor id string

mov vendorId, ebx

mov vendorId + 4, edx

mov vendorId + 8, ecx

mov eax, 1 // call cpuid with eax = 1

cpuid

mov regEax, eax // eax contains family processor type

mov regEdx, edx // edx has info about the availability of hyper-Threading

}

if (((regEax & 0x0F00) == 0x0F00) || (regEax & 0x0F00000))

{

if (vendorId[0] == ‘uneG’ && vendorId[1] == ‘Ieni’ && vendorId[2] == ‘letn’)

{

return !!(regEdx & 0x10000000);

}

}

return false;

}

void QueryVendorString(char* string)

{

char vendorId[12];

__asm{

mov eax, 0 ; request for feature flags

_emit 0x0F ; CPUID on Pentiums is 0f,a2

_emit 0xA2

mov dword ptr vendorId, ebx

mov dword ptr vendorId[+4], edx

mov dword ptr vendorId[+8], ecx

};

memcpy(string,vendorId,12);

string[12]=0;

}

unsigned char QueryCPUStepping()

{

unsigned char _stepping;

__asm{

mov eax, 1 ; request for feature flags

_emit 0x0F ; CPUID on Pentiums is 0f,a2

_emit 0xA2

and eax, 0x0F

mov _stepping, al

};

return _stepping;

}

unsigned char QueryCPUModel()

{

unsigned char _model;

__asm{

mov eax, 1 ; request for feature flags

_emit 0x0F ; CPUID on Pentiums is 0f,a2

_emit 0xA2

shr eax, 4

and eax, 0x0F

mov _model, al

};

return _model;

}

unsigned char QueryCPUFamily()

{

unsigned char _family;

__asm{

mov eax, 1 ; request for feature flags

_emit 0x0F ; CPUID on Pentiums is 0f,a2

_emit 0xA2

shr eax, 8

and eax, 0x0F

mov _family, al

};

return _family;

}

unsigned char QueryCPUType()

{

char _type;

__asm{

mov eax, 1 ; request for feature flags

_emit 0x0F ; CPUID on Pentiums is 0f,a2

_emit 0xA2

shr eax, 12

and eax, 0x03

mov _type, al

};

return _type;

}

unsigned long QueryCPUCount()

{

SYSTEM_INFO info;

GetSystemInfo(&info;);

return info.dwNumberOfProcessors;

}

bool LookUpL2CacheSize(int infoFlag, unsigned long& cacheSize)

{

int cacheFlag = infoFlag;

cacheFlag &= 0xFF;

while (infoFlag > 0)

{

switch (cacheFlag)

{

case 0x80:

case 0x40:

cacheSize = 0;

return true;

case 0x81:

case 0x41:

cacheSize = 128;

return true;

case 0x82:

case 0x42:

cacheSize = 256;

return true;

case 0x83:

case 0x43:

cacheSize = 512;

return true;

case 0x84:

case 0x44:

cacheSize = 1024;

return true;

case 0x85:

case 0x45:

cacheSize = 2048;

return true;

}

infoFlag = infoFlag >> 8;

cacheFlag = infoFlag & 0xFF;

}

return false;

}

unsigned long QueryCacheSize()

{

char cacheRepeatCount=0;

int eaxCacheDescriptor=0;

int ebxCacheDescriptor=0;

int ecxCacheDescriptor=0;

int edxCacheDescriptor=0;

unsigned long cacheSize=0;

__asm{

//Let’s find out the amount of the cache.

//That will help us later on figure out which processor we are running…

mov eax, 2

_emit 0x0f

_emit 0xa2

mov cacheRepeatCount, al

cmp al, 1

je DONE_CACHE_CHECK

REPEAT_CACHE_DETECT:

mov eax, 2

_emit 0x0f

_emit 0xa2

dec cacheRepeatCount

jnz REPEAT_CACHE_DETECT

DONE_CACHE_CHECK:

mov eaxCacheDescriptor, eax

mov ebxCacheDescriptor, ebx

mov ecxCacheDescriptor, ecx

mov edxCacheDescriptor, edx

};

if (LookUpL2CacheSize(eaxCacheDescriptor,cacheSize))

return cacheSize;

if (LookUpL2CacheSize(ebxCacheDescriptor,cacheSize))

return cacheSize;

if (LookUpL2CacheSize(ecxCacheDescriptor,cacheSize))

return cacheSize;

if (LookUpL2CacheSize(edxCacheDescriptor,cacheSize))

return cacheSize;

return 0;

}

bool QuerySerialNumber(char* string)

{

char serialString[32];

bool isValid=false;

unsigned long one=0;

unsigned long two=0;

unsigned long three=0;

if (QueryCPUID())

{

__asm {

mov eax, 0

_emit 0x0f

_emit 0xa2

cmp eax, 3

jl DONE

mov eax, 1

_emit 0x0f

_emit 0xa2

and edx, 0x20000

jz DONE

mov one, eax

mov eax, 3

_emit 0x0f

_emit 0xa2

mov two, edx

mov three, ecx

mov isValid, 1

DONE:

};

}

if (isValid)

{

wsprintf(serialString,”%04x-%04x-%04x-%04x-%04x-%04x”,

one >> 16, one & 0x0000FFFF,

two >> 16, two & 0x0000FFFF,

three >> 16, three & 0x0000FFFF);

}

else

strcpy(serialString,”###DISABLED OR NOT PRESENT###”);

strcpy(string,serialString);

return isValid;

}

//If type==0x10, then it’s a dual processor system…

//Also, if it’s a P2, you need to check the L2 cache size to see if

//it’s a celeron or a real P2. If it’s zero, it’s a Celery.

const int cCPULookupTableSize=28;

#define MakeFingerprint(a,b,c)           ((((unsigned long)a) << 24) | \

(((unsigned long)b) << 16) | (((unsigned long)c) << 8) | ((unsigned long)0))

struct CPUFingerprint

{

//Fingerprint is (family | model | type )

unsigned long fingerprint;

char vendorID[16];

char description[64];

};

CPUFingerprint cpuLookupTable[cCPULookupTableSize]=

{

//The first four entries in the table are reserved for special processors.

{ MakeFingerprint(0x00, 0x00, 0x00), “Unknown vendor”, “Unknown processor”},

{ MakeFingerprint(0x00, 0x00, 0x00), “Unknown vendor”, “Intel8086”},

{ MakeFingerprint(0x00, 0x00, 0x00), “Unknown vendor”, “Intel80286”},

{ MakeFingerprint(0x00, 0x00, 0x00), “Unknown vendor”, “Intel80386”},

//These are the ones that support CPUID, so we start the “real”

//lookup here. Currently, this is offset number 4 in the array.

{ MakeFingerprint(0x04, 0x00, 0x00), “Unknown vendor”, “Intel486 DX”},

{ MakeFingerprint(0x04, 0x01, 0x00), “Unknown vendor”, “Intel486 DX”},

{ MakeFingerprint(0x04, 0x02, 0x00), “Unknown vendor”, “Intel486 SX”},

{ MakeFingerprint(0x04, 0x03, 0x00), “Unknown vendor”, “Intel487 or DX2”},

{ MakeFingerprint(0x04, 0x04, 0x00), “Unknown vendor”, “Intel486 SL”},

{ MakeFingerprint(0x04, 0x05, 0x00), “Unknown vendor”, “IntelSX2”},

{ MakeFingerprint(0x04, 0x07, 0x00), “Unknown vendor”, “Write-Back Enhanced IntelDX2”},

{ MakeFingerprint(0x04, 0x08, 0x00), “Unknown vendor”, “IntelDX4”},

{ MakeFingerprint(0x04, 0x08, 0x00), “Unknown vendor”, “IntelDX4 OverDrive”},

{ MakeFingerprint(0x05, 0x01, 0x00), “Unknown vendor”, “Pentium (60,66)”},

{ MakeFingerprint(0x05, 0x02, 0x00), “Unknown vendor”, “Pentium (75 – 200)”},

{ MakeFingerprint(0x05, 0x01, 0x00), “Unknown vendor”, “Pentium Overdrive (60,66)”},

{ MakeFingerprint(0x05, 0x02, 0x00), “Unknown vendor”, “Pentium Overdrive (75 – 133)”},

{ MakeFingerprint(0x05, 0x03, 0x00), “Unknown vendor”, “Pentium Overdrive for Intel486”},

{ MakeFingerprint(0x05, 0x01, 0x00), “Unknown vendor”, “Pentium Overdrive (60,66)”},

{ MakeFingerprint(0x05, 0x04, 0x00), “Unknown vendor”, “Pentium with MMX (166,200)”},

{ MakeFingerprint(0x05, 0x04, 0x00), “Unknown vendor”, “Pentium MMX Overdrive (75 – 133)”},

{ MakeFingerprint(0x06, 0x01, 0x00), “Unknown vendor”, “PentiumPro”},

{ MakeFingerprint(0x06, 0x03, 0x00), “Unknown vendor”, “PentiumII model 3”},

{ MakeFingerprint(0x06, 0x05, 0x00), “Unknown vendor”, “PentiumII model 5 or Celeron”},

{ MakeFingerprint(0x06, 0x08, 0x00), “Unknown vendor”, “PentiumII or Celeron”},

{ MakeFingerprint(0x0F, 0x00, 0x08), “Unknown vendor”, “Pentium 4” },

{ MakeFingerprint(0x0F, 0x01, 0x08), “Unknown vendor”, “Pentium 4” },

{ MakeFingerprint(0x0F, 0x02, 0x09), “Unknown vendor”, “Pentium 4” }

};

void GetCPUInfoString(char* string)

{

long tableEntry=0;

if (Is8086())

tableEntry=1;

else if (Is80286())

tableEntry=2;

else if (Is80386())

tableEntry=3;

else if (QueryCPUID())

{

unsigned char family=QueryCPUFamily();

unsigned char model=QueryCPUModel();

unsigned char type=QueryCPUType();

//unsigned char stepping=QueryCPUStepping();

unsigned long fingerprint = MakeFingerprint(family, model, type);

//Start the lookup at four to skip the reserved entries…

for (int x=4; x < cCPULookupTableSize; x++)

{

if (fingerprint == cpuLookupTable[x].fingerprint)

tableEntry=x;

}

//Fill in the vendor string…

QueryVendorString(cpuLookupTable[tableEntry].vendorID);

}

unsigned long cacheSize = QueryCacheSize();

bool hasMMX = QueryMMX();

unsigned long cpuCount=QueryCPUCount();

char serialId[64];

QuerySerialNumber(serialId);

bool hyperThreading = QueryHyperThreading();

wsprintf(string,”———————————————————\n”

“Primary cpu = %s\n”

“CPUs present: %d\n”

“Vendor string: %s\n”

“L2 cache size: %d\n”

“MMX capable: %d\n”

“Serial ID: %s\n”

“HyperThread: %d\n”

“———————————————————\n”,

cpuLookupTable[tableEntry].description,

cpuCount,

cpuLookupTable[tableEntry].vendorID,

cacheSize,

hasMMX,

serialId,

hyperThreading);

}

Disclaimer:
This post comes from my old blog back in 2004. I’m reposting it here so that I don’t lose the content. The source was hand-written HTML so the formatting probably appears a bit off.


MCI CD Control

This is the MCI control code that I wrote for my UglyCD
player
. It is fairly full featured, but if you need more, feel free to modify it to your needs. I
have neglected the error checking code, and every call to mciSendCommand should really be checked for
its return value. As usual, this code is usable at your own risk. If you have any questions, you are
always free to ask.

-BossHogg

#ifndef MCI_CONTROL
#define MCI_CONTROL 1

class MCIControl
{
     public:
          MCIControl();
          ~MCIControl();

          int     GetNumberOfTracks();
          void    Resume();
          void    Pause();
          void    Play();
          void    Stop();
          void    OpenDoor();
          void    CloseDoor();

          void    Goto(int track,int minute, int second);
		
          int     GetCurrentTrack();
          int     GetCurrentMinute();
          int     GetCurrentSecond();

     protected:
          void    Init();
          void    SetTimeFormat();
          void    GetPosition(BYTE* track,BYTE* min,BYTE* sec);

     private:
          MCIDEVICEID   itsMCIDevice;
};

#endif

#include “windows.h”

#include “MCIControl.h”

MCIControl::MCIControl() :

itsMCIDevice(0)

{

Init();

SetTimeFormat();

}

MCIControl::~MCIControl()

{

MCI_GENERIC_PARMS Info;

Info.dwCallback=0;

mciSendCommand(itsMCIDevice, MCI_CLOSE, MCI_NOTIFY, DWORD(&Info;));

}

void MCIControl::Resume()

{

MCI_PLAY_PARMS Info;

BYTE track,minute,second;

GetPosition(&track;,&minute;,&second;);

Info.dwTo=0;

Info.dwCallback=0;

Info.dwFrom = MCI_MAKE_TMSF(track,minute,second,0);

mciSendCommand(itsMCIDevice, MCI_PLAY, MCI_FROM|MCI_NOTIFY, DWORD(&Info;));

}

void MCIControl::Pause()

{

MCI_GENERIC_PARMS Info;

Info.dwCallback = 0;

mciSendCommand(itsMCIDevice, MCI_PAUSE, MCI_NOTIFY, DWORD(&Info;));

}

void MCIControl::Goto(int track,int minute, int second)

{

MCI_PLAY_PARMS Info;

Info.dwCallback=0;

Info.dwTo=0;

Info.dwFrom = MCI_MAKE_TMSF(track,minute,second,0);

mciSendCommand(itsMCIDevice, MCI_PLAY, MCI_FROM|MCI_NOTIFY, DWORD(&Info;));

}

void MCIControl::Play()

{

MCI_PLAY_PARMS Info;

Info.dwCallback=0;

Info.dwTo=0;

Info.dwFrom = MCI_MAKE_TMSF(0,0,0,0);

mciSendCommand(itsMCIDevice, MCI_PLAY, MCI_FROM|MCI_NOTIFY, DWORD(&Info;));

}

void MCIControl::Stop()

{

MCI_GENERIC_PARMS Info;

Info.dwCallback = 0;

mciSendCommand(itsMCIDevice, MCI_STOP, MCI_NOTIFY, DWORD(&Info;));

}

void MCIControl::OpenDoor()

{

MCI_STATUS_PARMS Info;

Info.dwCallback=0;

Info.dwItem=0;

Info.dwReturn=0;

Info.dwTrack=0;

mciSendCommand(itsMCIDevice, MCI_SET, MCI_SET_DOOR_OPEN, DWORD(&Info;));

}

void MCIControl::CloseDoor()

{

MCI_STATUS_PARMS Info;

Info.dwCallback=0;

Info.dwItem=0;

Info.dwReturn=0;

Info.dwTrack=0;

mciSendCommand(itsMCIDevice, MCI_SET, MCI_SET_DOOR_CLOSED, DWORD(&Info;));

}

int MCIControl::GetCurrentTrack()

{

BYTE track;

GetPosition(&track;,NULL,NULL);

return track;

}

int MCIControl::GetCurrentMinute()

{

BYTE minute;

GetPosition(NULL,&minute;,NULL);

return minute;

}

int MCIControl::GetCurrentSecond()

{

BYTE second;

GetPosition(NULL,NULL,&second;);

return second;

}

int MCIControl::GetNumberOfTracks()

{

MCI_STATUS_PARMS Info;

Info.dwCallback = 0;

Info.dwReturn = 0;

Info.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;

Info.dwTrack = 0;

mciSendCommand(itsMCIDevice,MCI_STATUS,MCI_STATUS_ITEM,DWORD(&Info;));

return (int)Info.dwReturn;

}

void MCIControl::GetPosition(BYTE* track,BYTE* min,BYTE* sec)

{

MCI_STATUS_PARMS Info;

DWORD MSF;

Info.dwCallback=0;

Info.dwReturn=0;

Info.dwTrack=0;

Info.dwItem = MCI_STATUS_POSITION;

mciSendCommand(itsMCIDevice, MCI_STATUS, MCI_STATUS_ITEM, DWORD(&Info;));

MSF = Info.dwReturn;

if (track)

*track = MCI_MSF_MINUTE(MSF);

if (min)

*min = MCI_MSF_SECOND(MSF);

if (sec)

*sec = MCI_MSF_FRAME(MSF);

}

void MCIControl::SetTimeFormat()

{

MCI_SET_PARMS Info;

Info.dwCallback=0;

Info.dwTimeFormat=MCI_FORMAT_TMSF;

Info.dwAudio=0;

mciSendCommand(itsMCIDevice, MCI_SET, MCI_SET_TIME_FORMAT, DWORD(&Info;));

}

void MCIControl::Init()

{

MCI_OPEN_PARMS Info;

Info.dwCallback=0;

Info.lpstrAlias=0;

Info.lpstrElementName=0;

Info.wDeviceID=0;

Info.lpstrDeviceType=MAKEINTRESOURCE(MCI_DEVTYPE_CD_AUDIO);

mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID, DWORD(&Info;));

itsMCIDevice = Info.wDeviceID;

}

Mu-Law and A-Law Compression Tutorial

Disclaimer:
This post comes from my old blog back in 2004. I’m reposting it here so that I don’t lose the content. The source was hand-written HTML so the formatting probably appears a bit off.

Overview:
What are A-Law and Mu-Law compression? In the simplest terms, they are
standard forms of audio compression for 16 bit sounds. Like most audio
compression techniques, they are lossy, which means that when you expand them
back from their compressed state, they will not be exactly the same as
when you compressed them. The compression is always 2:1, meaning that
audio compressed with either of these algorithms will always be exactly half
of their original size.

Mu-Law and A-Law compression are both logarithmic forms of data compression,
and are extremely similar, as you will see in a minute. One definition of Mu-Law is



          “…a form of logarithmic data compression

          for audio data. Due to the fact that we hear logarithmically,

          sound recorded at higher levels does not require the same

          resolution as low-level sound. This allows us to disregard

          the least significant bits in high-level data. This turns

          out to resemble a logarithmic transformation. The resulting

          compression forces a 16-bit number to be represented as an 8-bit

          number.”


(www-s.ti.com/sc/psheets/spra267/spra267.pdf)

And from the comp.dsp newsgroup FAQ we also get this definition:



          Mu-law (also “u-law”) encoding is a form of logarithmic

          quantization or companding. It’s based on the observation that

          many signals are statistically more likely to be near a low

          signal level than a high signal level. Therefore, it makes

          more sense to have more quantization points near a low level

          than a high level. In a typical mu-law system, linear samples

          of 14 to 16 bits are companded to 8 bits. Most telephone

          quality codecs (including the Sparcstation’s audio codec) use

          mu-law encoded samples.



In simpler terms, this means that sound is represented as a wave, and humans
can only hear audio in the middle of the wave. We can remove data from the upper
and lower frequencies of a sound, and humans will not be able to hear a significant
difference. Both Mu-Law and A-Law take advantage of this, and are able to
compress 16-bit audio in an manner acceptable to human ears.
A-Law and Mu-Law compression appear to have been developed at around the
same time, and basically only differ by the particular logarithmic function
used to determine the translation. When we get to the work of
implementing the algorithms, you will see that the differences are nominal.
The main difference is that Mu-Law attempts to keep the top five bits of precision,
and uses a logarithmic function to determine the bottom three bits, while A-Law
compression keeps the top four bits and uses the logarithmic function to figure out
the bottom four. Both of these algorithms are used as telecommunication standards,
A-Law being used mainly in Europe, and Mu-Law being used in the United States.

DISCLAIMER:

Please understand that I am glossing over several of the details, but recognize that
the entire purpose of this document is to make two extremely useful algorithms much
more accessable to “average” programmers, like myself.

Mu-Law Compression:

As you read this explanation, remember that the purpose of the algorithm is to
compress a 16-bit source sample down to an 8-bit sample. The crux of Mu-Law
functionality is deciding which of the samples need to keep the most of their
precision. Even the “most-important” sample will still lose precision. It
simply becomes a matter of determining how much each sample loses, and minimizing
the loss on samples deemed “more important”.

To generate a compressed Mu-Law sample from an uncompressed sample, the following
algorithm is applied to the 16-bit source sample.

(Please refer to the code listing for Mu-Law compression.)

First, the algorithm first stores off the sign. It then adds in a bias value
which (due to wrapping) will cause high valued samples to lose precision.
The top five most significant bits are pulled out of the sample (which has
been previously biased). Then, the bottom three bits of the compressed byte are
generated using a small look-up table, based on the biased value of the source sample.
The 8-bit compressed sample is then finally created by logically OR’ing together
the 5 most important bits, the 3 lower bits, and the sign when applicable. The bits
are the logically NOT’ed, which I assume is for transmission reasons (although you
might not transmit your sample.)

MuLaw Compresion Code:



const int cBias = 0x84;

const int cClip = 32635;

static char MuLawCompressTable[256] =

{

     0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,

     4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,

     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,

     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,

     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,

     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,

     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,

     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,

     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7

};

unsigned char LinearToMuLawSample(short sample)

{

     int sign = (sample >> 8) & 0x80;

     if (sign)

          sample = (short)-sample;

     if (sample > cClip)

          sample = cClip;

     sample = (short)(sample + cBias);

     int exponent = (int)MuLawCompressTable[(sample>>7) & 0xFF];

     int mantissa = (sample >> (exponent+3)) & 0x0F;

     int compressedByte = ~ (sign | (exponent << 4) | mantissa);

     return (unsigned char)compressedByte;

}

A-Law Compression:

As mentioned earlier, A-Law compression is extremely similar to Mu-Law compression. As you
will see, they differ primarily in the way that they keep precision. The
following is a short synopsis of the encoding algorithm, and the code example follows the
written explanation.
First, the sign is stored off. Then the code branches. If the absolute value of the source
sample is less than 256, the 16-bit sample is simply shifted down 4 bits and converted
to an 8-bit value, thus losing the top 4 bits in the process.
However, if it is more than 256, a logarithmic algorithm is applied to the sample to
determine the precision to keep. In that case, the sample is shifted down to access the
seven most significant bits of the sample. Those seven bits are then used to determine the
precision of the bottom 4 bits. Finally, the top seven bits are shifted back up four bits
to make room for the bottom 4 bits. The two are then logically OR’d together to create the
eight bit compressed sample. The sign is then applied, and the entire compressed sample
is logically XOR’d, again, I assume for transmission reasons.

A-Law Compression Code:



static char ALawCompressTable[128] =

{

     1,1,2,2,3,3,3,3,

     4,4,4,4,4,4,4,4,

     5,5,5,5,5,5,5,5,

     5,5,5,5,5,5,5,5,

     6,6,6,6,6,6,6,6,

     6,6,6,6,6,6,6,6,

     6,6,6,6,6,6,6,6,

     6,6,6,6,6,6,6,6,

     7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7,

     7,7,7,7,7,7,7,7

};

unsigned char LinearToALawSample(short sample)

{

     int sign;

     int exponent;

     int mantissa;

     unsigned char compressedByte;

     sign = ((~sample) >> 8) & 0x80;

     if (!sign)

          sample = (short)-sample;

     if (sample > cClip)

          sample = cClip;

     if (sample >= 256)

     {

          exponent = (int)ALawCompressTable[(sample >> 8) & 0x7F];

          mantissa = (sample >> (exponent + 3) ) & 0x0F;

          compressedByte = ((exponent << 4) | mantissa);

     }

     else

     {

          compressedByte = (unsigned char)(sample >> 4);

     }

     compressedByte ^= (sign ^ 0x55);

     return compressedByte;

}

Decompression:

Now, the most obvious way to decompress a compressed Mu-Law or A-Law sample would
be to reverse the algorithm. But a more efficient method exists. Consider for a moment the
fact that A-Law and Mu-Law both take a 16-bit value and crunch it down to an 8-bit value.
The reverse of that is to take an 8-bit value and turn it into a sixteen bit value. In the
graphics world, it is extremely common to represent 32 and 24 bit values with an eight bit
index into a palette table. So, why not take a page from the world of graphics and use
palettes for the Mu-Law and A-Law compression look up? Sounds good to me. In fact,
these palettes will be smaller than their 24 and 32 bit cousins because we only need to
represent 16 bit values, not 24 and 32. In a nutshell, we will create static lookup
tables to do the reverse conversion from A-Law and Mu-Law. The two differing tables
are presented below. To convert from your compressed sample back to the raw 16-bit
sample, just use your compressed sample as the index into the table, and the corresponding
value in the table is your decompressed 16-bit sample. Obviously, the downside is that
this method requires the memory overhead for the tables, but each table is only 512 bytes.
In this day and age, that’s downright cheap for the absolute fastest decompression!

Decompression Code:



static short MuLawDecompressTable[256] =

{

     -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,

     -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,

     -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,

     -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,

      -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,

      -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,

      -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,

      -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,

      -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,

      -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,

       -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,

       -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,

       -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,

       -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,

       -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,

        -56,   -48,   -40,   -32,   -24,   -16,    -8,     0,

      32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,

      23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,

      15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,

      11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,

       7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,

       5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,

       3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,

       2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,

       1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,

       1372,  1308,  1244,  1180,  1116,  1052,   988,   924,

        876,   844,   812,   780,   748,   716,   684,   652,

        620,   588,   556,   524,   492,   460,   428,   396,

        372,   356,   340,   324,   308,   292,   276,   260,

        244,   228,   212,   196,   180,   164,   148,   132,

        120,   112,   104,    96,    88,    80,    72,    64,

         56,    48,    40,    32,    24,    16,     8,     0

};



static short ALawDecompressTable[256] =

{

     -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,

     -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,

     -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,

     -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,

     -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,

     -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,

     -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,

     -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,

     -344,  -328,  -376,  -360,  -280,  -264,  -312,  -296,

     -472,  -456,  -504,  -488,  -408,  -392,  -440,  -424,

     -88,   -72,   -120,  -104,  -24,   -8,    -56,   -40,

     -216,  -200,  -248,  -232,  -152,  -136,  -184,  -168,

     -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,

     -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,

     -688,  -656,  -752,  -720,  -560,  -528,  -624,  -592,

     -944,  -912,  -1008, -976,  -816,  -784,  -880,  -848,

      5504,  5248,  6016,  5760,  4480,  4224,  4992,  4736,

      7552,  7296,  8064,  7808,  6528,  6272,  7040,  6784,

      2752,  2624,  3008,  2880,  2240,  2112,  2496,  2368,

      3776,  3648,  4032,  3904,  3264,  3136,  3520,  3392,

      22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,

      30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,

      11008, 10496, 12032, 11520, 8960,  8448,  9984,  9472,

      15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,

      344,   328,   376,   360,   280,   264,   312,   296,

      472,   456,   504,   488,   408,   392,   440,   424,

      88,    72,   120,   104,    24,     8,    56,    40,

      216,   200,   248,   232,   152,   136,   184,   168,

      1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,

      1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,

      688,   656,   752,   720,   560,   528,   624,   592,

      944,   912,  1008,   976,   816,   784,   880,   848

};

Some Not-So-Random Oregon Statistics

Recently, I heard a fascinating interview with Larry Krasner, the DA for Philadelphia. It really challenged some of my thoughts on incarceration and inspired me to do a little research, locally. Here are some interesting statistics that I found for Oregon:

  • 2017-2019 Dept of Corrections Budget: $1.76B ($880m annually) Source
  • Average Daily Prison Population: 14,835 Source
  • Average cost per inmate: $59,319 ($880m / 14,835)

So who is in our prison system? Here are some other things I found: Source

  • White 74.5% (vs 76.4% of normal state population)
  • Hispanic 12.1% (vs 12.8% of normal state population)
  • Black 9% (vs 2.1% of normal state population)
  • 62.7% are over the age of 30
  • Male 81%
  • Female 19%

Top 5 incarceration rates by county: Source

  1. Sherman County, 2.83 / 1000
  2. Marion County, 2.14 / 1000
  3. Jefferson County, 1.96 / 1000
  4. Linn County, 1.89 / 1000
  5. Clatsop County, 1.86 / 1000

And what did they do to get there? Source

  1. Drugs 20%
  2. Assault 13.4%
  3. Other 12.6%
  4. Theft 9.3%
  5. Burglary 8.9%

This also made me curious relative to education in Oregon. Here are a few statistics I found:

  • Average Teacher Salary: $59,204 Source
  • Total Number of Teachers: 22,357 Source
  • Median Class Size: 25 Source

So here are some of my random thoughts after doing this research:

  • It’s fascinating that every incarcerated inmate almost exactly equals a full time teacher. (These costs don’t even include local and municipal jail costs.)
  • African American Oregonians are clearly incarcerated at a much higher rate than either caucasian or hispanic Oregonians.
  • It’s ALARMING that 12.6% of the prison population is incarcerated for offenses labeled as “other”. Take a look at the source for the incarceration cause breakdown and you can see how granular it gets which makes the “other” classification that much more troublesome. (Forgery weighs in at a whopping 0.4%)
  • The likelihood of incarceration has nothing to do with the population density that you live in. Only one of the top 5 counties in the per capita list would be considered urban.

Does any of this mean anything? I’m not sure. I’ll definitely be thinking about it for a while. In the alarmist era we currently find ourselves in, I find it helpful to have some actual data behind to fall back on. As such, I’ve included links to all of my sources in case anyone else feels so inspired to do some data spelunking. If you do, please share what you find!

 

 

A Coffee Retrospective

2016-04-01 10.51.28Back in March, I was inspired by Manton’s Austin coffee quest and decided to try my own “30 Days of Coffee.” Starting on April 1 and continuing for 30 straight days, I would experience a new cafe or coffee shop that I had never visited before. The rules I set for myself were fairly simple:

  1. I must visit a coffee shop each day for 30 days.
  2. I can not have had coffee there previously.
  3. The coffee shop must reside within the boundaries of N/NE Portland. (I did this so that I could somewhat mimic Manton’s challenge given the plethora of coffee shops in Portland vs. Austin.)
  4. In order to count as a “coffee shop” it must serve lattes (my drink of choice)

I have now completed my 30 days, and I have to tell you that despite sounding easy, it’s actually quite difficult to do ANYTHING for 30 days straight. There were more than a few times that Tiffany had to remind me that I hadn’t made my daily trip (especially difficult since most non-chain coffee shops close at 4 p.m.)

Along the way, I documented each visit on my microblog which you can find here. I also snapped a picture of each location that hopefully gave a small glimpse into the ambiance of each.

This entire endeavor was an experiment in a number of areas, and I can honestly say that I learned a little bit about myself along the way. As I mentioned, I was microblogging the adventure and somehow ended up having a number of people who followed along. A few have even asked for a summary/recommendation list, to which I am more than happy to oblige.

The following is my list of coffee shops that I would gladly return to again. The rankings are relative to me, which means that it’s based on a blend of coffee quality, ambiance, location and overall comfort (including my ability to work remotely). This is simply a ranked list out of the 30 that I visited, not my overall list for Portland. In fact, I think only one of these would crack my Portland top 10 list.

So, with all of these caveats out of the way, here is my ordered list of places that I would gladly revisit:

  1. Blend Coffee – I cannot say enough good things about this place. From the cleanliness to the thoughtfulness of the seating to the ridiculous number of power outlets, everything about this place is well thought out. I only wish it was closer to my home. That being said, this is the only coffee shop on my list that I would go out of my way to visit. If you haven’t been here, I absolutely recommend a visit.
  2. Bassotto – This place was an absolute gem of a find because it’s actually a fantastic coffee shop disguised as a gelateria. It also doesn’t hurt that it’s located next door to the amazing Tamale Boy, but I think I’d come back even if it was located elsewhere. Finding Bassotto was one of the reasons that I did this challenge. It allowed me to find an awesome place that I normally wouldn’t have tried on my own.
  3. Prince Coffee – This place is SMALL. It’s also very new and in the beautiful Kenton neighborhood. I will revisit this place if for nothing else than their homemade stroopwafels.
  4. Miss Zumstein – This location is very comfortable, but their pastries are wonderful. The staff is friendly, and they have great coffee, but man are those pastries good.
  5. Locale Coffee – This is another of the new-wave Portland coffee shops located in the Mississippi neighborhood. I would definitely go back to it again, but there’s nothing particularly special about it. If you’re looking for a decent place to meet someone for coffee and you need to be near Mississippi, you can’t really go wrong here.
  6. Saint Simon – This is yet another stereotypical new-wave Portland coffee shop. It has good coffee, is nice inside, has a good location, but is a little too “trendy” for my taste. Think I’m exaggerating? Everything from the moose head on the wall to the forced industrial look to the “wood block” seating just screams Portlandia. I’d definitely take coffee from here again, but I probably wouldn’t stay for long.
  7. Seven Virtues – I didn’t want to like Seven Virtues because I find the entire Zipper building to be somewhat pretentious and annoying, but I have to admit that it was pretty nice inside and they had good coffee to boot. I’ve heard from at least one person that they went here right after it opened and were very unhappy with their experience. Perhaps they had some initial issues getting going?
  8. Posie’s Cafe – I’m not sure if I liked Posie’s based on its own merits or because it’s located in the Kenton neighborhood. Regardless, I found it to be very charming and a nice place to pop into if you’re looking for a quick caffeine pick me up. They had a lot of seating and had some pretty good looking pastries as well.

And that is it. Out of 30, I would revisit 8. Of the 8, only one of them would crack my top Portland Coffee Shops list. (I’ll try to put that together soon as well.)

As I mentioned, the challenge was more difficult than I expected given how often I go to coffee. In fact, I’ve started thinking differently about 30 day challenges in general and I have a few more I might try in the next few months. If you’d like to see the entire list of 30, I’ve included it below. You can also find my posts on Twitter or Facebook with the hashtag #pdxcoffeehunt.

  1. Cathedral Coffee – 7530 N Willamette Blvd
  2. TwentySix Cafe – NE 7th Ave
  3. Miss Zumstein – 5027 NE 42nd Ave.
  4. Saint Simon Coffee – 2005 NE Broadway
  5. Tiny’s Coffee2031 NE Martin Luther King Jr Blvd
  6. Fillmore Coffee – 7201 NE Glisan St
  7. Prince Coffee – 2030 N Willis Blvd
  8. Bison Coffee House – 3941 NE Cully Blvd
  9. Seven Virtues – 2705 NE Sandy Blvd
  10. Batter – 4425 NE Fremont St
  11. Kopi – 2327 E Burnside St
  12. Locale – 4330 N Mississippi Ave
  13. The Fresh Pot – 4001 N Mississippi Ave
  14. Goldrush – 2601 NE Martin Luther King Jr. Blvd
  15. Bassotto – 1760 NE Dekum
  16. Spielman Bagels – 2200 NE Broadway St
  17. Wholesome Blend – 4615 NE Sandy Blvd
  18. Cup & Bar – 118 NE Martin Luther King Jr. Blvd
  19. Heart Coffee Roasters – 2211 E Burnside St
  20. Blend Coffee Lounge – 2710 N Killingsworth St
  21. Cafe Eleven – 435 NE Rosa Parks Way
  22. The Arbor Lodge – 1507 N Rosa Parks Way
  23. Whole Foods Coffee Shop
  24. Extracto – 1465 NE Prescott St
  25. Coffee People – Portland Airport
  26. J Cafe – 533 NE Holladay St
  27. Coffee House Five – 740 N Killingsworth St
  28. Case Study – 1422 NE Alberta
  29. Posie’s Cafe – 8208 N Denver Ave
  30. Elevated Coffee – 5261 NE Martin Luther King Jr Blvd