Small Businesses and the Health Insurance Market

As a small business owner, I take very seriously the benefits we offer to our team. Every year, we work with our insurance broker to find the best health insurance plans we can offer at a price point that is workable. Like most companies, we offer full medical, dental and vision, and as a rule, we always pay the premiums for the employee and their family because we just feel that's the right thing to do.

Across all of the insurance networks, the plans more or less look the same - as you progress through the tiers from Bronze to Silver to Gold, the deductibles improve and the maximum out of pocket shrinks. We have been doing this for just shy of 15 years now, and every year, the plans get worse and more expensive. Every. Single. Year. And yet, as we analyze the various plans, we continually find that even our upgraded "Gold" plan is barely more than a safety net for catastrophic or major health events.

We have changed insurance networks 3 or 4 times through the years, trying to offer the best options to the team, but honestly they all seem nearly identical. The thing that's disturbing is that because we are less than 50 employees, we don't have options to simply "buy a better plan" as some might argue. Across all of the markets, we have only one more tier (Platinum) that we could upgrade our plan to, and even that isn't what I would consider a good plan; it's simply more expensive.

These ever rising prices are increasingly becoming a huge driver of base costs for us and other small businesses. Silverpine's employees have a fairly low median age, and nothing about our working environment is even remotely dangerous – we should be an ideal candidate for an insurance company. And yet nobody is competing for our business. Nobody is innovating to provide better products and coverage for us. This is a completely broken industry. Everything about it flies in the face of healthy free market dynamics.

I don't have a definitive, corrective solution to offer – I'm not an economist. But it does seem to me that health insurance is closer to a utility (like electricity or sewer service) and as I mentioned, the tenets of capitalism don't seem to apply to it. Outside of a stock price, it barely functions as an industry and quite frankly, the entire situation is bad for American businesses, especially small businesses, like ours. Something needs to change because health insurance is not only broken for the insured, it's also broken for most of the companies that cover the lion's share of the cost of that insurance. It's time for a change.

ACS Europe 2024

This summer, I had the pleasure of being invited to speak on a panel at ACS Europe, which is a global conference for the access control industry. The conference was held at Google's facility in Zürich, Switzerland. The entire event was invigorating and exciting as competitors and partners alike all met and discussed the industry and where we see it headed. I was thrilled to represent Silverpine and bring my own sliver of perspective to the conversation. Below is a short clip of part of my time on the panel.

Tools for a Distributed Software Agency - 2023

It's time for my annual reflection on the tools that we use to power Silverpine. I've been posting these for the past few years and it's always an interesting activity to see how things change over time. You can see old posts here:

Overall, we have continued to consolidate our tools, which is good from a financial perspective. One notable tool that fell from the list this year is Squarespace which we previously used to host the Silverpine website; we rewrote our website as a native front end hosted on AWS which allowed us to drop it from our monthly costs.

While tool consolidation feels good, I do worry that it can potentially lead to a lack of innovation. For the past few years, a lot of change has occurred in the design space (Sketch, Figma, InDesign, etc) and it felt like there were a lot of improvements being made amidst healthy competition. But this past year, that has slowed way down. Even amongst teleconferencing software it feels like things have mostly collapsed to a primary three options. I sincerely hope that 2024 will bring with it some overdue disruption into the tools ecosystem. I am definitely ready to try something new!

Communication

Slack - If we could pick only one tool from this list, it would be Slack. For any distributed team, Slack is absolutely essential. One thing that I did notice that changed this year is that our team took advantage of Slack's "Huddle" feature far more than in years past. We pay for Zoom as well, but it's much easier for team members to quickly jump into an adhoc call and do some screen sharing if they don't have to leave Slack. Definitely a much used feature.

Zoom - There are three main options for teleconferencing (Google Meets, Zoom and Microsoft Teams) and while none of them are perfect, Zoom still seems to work better across various hardware configurations than the others.

Google Workspace - Google Workspace replaced Dropbox for our team last year and for the most part it hasn't impacted us much. It's a fairly economical option for email and shared storage and while the shared storage functions aren't as robust as Dropbox, it works well enough.

Development and Design

Figma - Last year we moved fully to Figma (as did most of the industry) and it's been great. I'm feeling relieved that the Adobe acquisition/merger fell through because Figma is one of those pieces of software that almost feels magical, and I was worried that Adobe would kill that feeling. Everyone from designers to developers to clients (viewing prototypes) extolls how simple and accessible it is. It's just a fantastic tool.

Github - There's not a whole lot to say about Github. After Microsoft purchase it, they've mostly left them alone to flourish. We use it for the obvious code repository features, but we also use Github Actions for our CI (continuous integration) tooling.

Tower - We moved to a team subscription for Tower a few years ago and haven't looked back. Their level of support and continued feature improvement have made it the de facto GUI for source control. Our team works on a myriad of platforms and Tower is well supported on all of them.

Jira - If you use Jira, you know why you use it, and you'll probably agree that it's "fine". That's really all I want to say about it. Nobody loves Jira, myself included.

Operations

Quickbooks - Is there even another option for keeping your books that isn't some sort of Oracle installation? Quickbooks works well for what we need. The CPA and bookkeeper are glad we use it. I guess that means something.

Harvest - We use Harvest for invoicing clients and a small amount of time-tracking. We've been pretty happy with it. The reporting tools make it easy to understand cashflow and to understand when invoices are late.

Gusto - If you need to pay employees or contractors, Gusto is fantastic. One small change from last year is that we upgraded to Gusto Plus and it has made a huge improvement in their customer support. We have employees in multiple states that all have unique tax and reporting requirements so moving to Plus has been invaluable. It's more expensive than the base plan but definitely worth it in terms of reducing headaches.

Adobe Acrobat Pro - We actually have been using Acrobat Pro for years; I simply missed including it in past years. As an agency with numerous clients and prospects, we are frequently dealing with NDAs, MSAs, SOWs and various other legal documents. Acrobat lets us easily deal with whatever form we receive and sign/counter-sign them. This is one of those tools that "just works."

The End

Today, my youngest will attend school for the final day. For fifteen years, we have woken them, dressed them, fed them, and helped them get out the door. After today, we are done.

The bittersweet is overwhelming me.

For Sarah, Rachel and April

Twenty five years ago, I was celebrating my 21st birthday with my college roommates. However, I received a call from my dad that abruptly ended the celebration. My cousin and her three young daughters had been murdered by their father; one of them in the arms of her grandmother who was trying to protect her. He had purchased a gun in Seattle and then drove down to Oregon and shot them in their front yard. The oldest had just come home from her first day of kindergarten.

My dad picked me up from my apartment, and we drove down in silence to my great-grandmother's house. The extended family was gathering to be together and to try and understand what had happened. Twenty five years later and I'm still not sure any of us understand. Can you ever really understand something like that?

Today, an eighteen year old kid murdered twenty-one people in an elementary school, and those memories have flooded back to me. I can remember that night like it was yesterday. As I try to process this horribly tragic event, I am sickened by my emotions. They aren't the emotions that I should be feeling. I'm not feeling anger; I'm feeling resignation. I'm feeling a disappointment in myself because I cannot muster the outrage that the murder of children should evoke.

I don't want this to be normal in the world I live in. But I find that anymore I simply feel helpless. I know that deep within I still have intense feelings of anger, but over and over and over and over again that rage runs headlong into the hopelessness. It just hurts too much.

We should be better. But we aren't.

I'm sorry Sarah, Rachel and April. We keep letting you down.

AirAware!

Ever since the incredibly awful fires/smoke that we encountered in Portland in September of 2020, I've been thinking a lot about the environment on a very local level. I fully admit that I was caught off-guard by my first climate related emergency, and I learned quite a bit about air quality terms like AQI. I also discovered some of the various monitoring networks with publicly available data, and sort of developed an itch to participate in some of them. One aspect in particular that surprised me was how awful the air quality inside my home became. I also realized that while the air quality networks could tell me what the outside AQI was, nobody could tell me what it was inside. So, I eventually purchased an Awair Element to put in my office to help me keep an eye on it. Of course, being a tinkerer, I couldn't help notice that Awair provides an open API for connecting to and monitoring their devices. And so, what would eventually become AirAware was born!

So, after a couple weeks, I had what I felt was a pretty decent reusable framework for iOS to connect to and fetch data from Awair devices. But by then I had an itch. I felt like I had just scratched the surface of what could be done. So after a bit more Googling, I found a number of other devices and networks that allow connecting to their APIs, and I decided to pull them into AirAware as well. In the end, I decided to add support for an initial 5 devices/networks:

There are potentially other devices and networks that I would love to add support for in the future, but for an initial release, I felt like this was sufficient.

Now, I will stop here and say that I love making frameworks and libraries for other developers. I feel good when I make something and can put it out into the universe to potentially help other people. That being said, will anyone find this useful? Will it help anyone? I have no idea, but that's ok! The point is that AirAware now exists and is available if anyone ever wants to add air monitoring functionality to their app, or if they just want to see a reference implementation for any of the supported devices/networks.

Technical Notes:

Tools for a Distributed Software Agency - 2021

Last year, I posted an overview of the tools that Silverpine uses, and I thought it might be interesting to revisit our toolset and compare with where we are versus where we were. Many of the tools continue to be the same, but there are a few changes. Read on for the details!

Communication Tools

Without a doubt, Slack is the single most valuable tool for us as a distributed company. One thing that has changed over the past year, however, is that almost all of our clients have now adopted Slack as well. This has been a great improvement as we can now utilize shared channels between workspaces which greatly reduces clutter and overhead. This has allowed us to stay mostly within the Silverpine Slack workspace which makes everything simpler and cleaner in terms of organization and tracking conversations both internal, and external. If you're new to Slack, or have yet to try it out, I have a few posts here and here to help you get going.

Looking back to where we were last year, it's clear that teleconferencing technology and tools have rapidly improved. This is one of the few silver linings of Covid. I've never held video conferencing software in very high regard, but I can definitely say that the tools have gone from incredibly awful to "ok". And to that end, Zoom has definitely moved the quality/easy of use bar further and faster than the competitors. We will see if Zoom can keep the crown in the coming year as Teams and other platforms try to leapfrog it. For now, however, Zoom is the best, easiest to use tool for video conferencing for our business.

Last year, we standardized on Dropbox Pro for file sharing and quite honestly, it's been so seamless to our workflows that I sometimes forget about it. I'm still surprised that Apple or Google haven't acquired Dropbox yet, but I have no complaints. It just works.

We use Gmail for our email, and thereby get all of Google Workspace (formerly known as G Suite). I said before that I'm not even sure where I would look to replace the email side of the tools, and that still applies. This past year, however, we have also started using more of the other Workspace tools (Sheets, Docs, etc) but we definitely haven't standardized. Most of the tools are convenient simply because they are ever-present, but if you need to create a document with a significant amount of formatting, you'll want to look elsewhere.

Development Tools

We have looked at a few alternatives to Github (like BitBucket) but at this point, GitHub is pretty much the de facto standard for source control systems. I will admit that I was a little nervous when Microsoft acquired them, but quite frankly, their service has improved to the point that we now use them for both source control and for our automated build system through GitHub Actions. Their cost model is also very helpful to us now that it's seat based rather than project based.

The choice of a git tool can be fairly controversial, and we will never mandate a particular app/tool, but we have found that, overall, Tower is a very good, stable, and constantly improving product. They have versions for both Mac and Windows, and their licensing model makes it easy for us to provide seats to any of our engineers that want to use it. I wouldn't say that it's a perfect piece of software, but it's definitely better than using the command line. We're not barbarians!

Of all the tools on this list, MantisHub is the one that I'm the least confident that we will still be using a year from now. MantisHub is a fairly inexpensive, hosted, bug tracking system that's based on the open source Mantis platform. It has replaced Lighthouse which we previously used. We made the decision to replace Lighthouse because it has fallen into abandonware status, and lately hasn't been able to provide the workflow that we need. I wouldn't say that I love MantisHub, but it's not as heavy as Jira and definitely less expensive. We are continually looking to improve our bug tracking system so if you have one that you like, I'd love to hear about it.

Design Tools

Sketch has owned the "non-Photoshop" tool space for years, and it remains the default/standard for our designers. Sketch is a familiar, comfortable, design tool that does what it needs to do. The licensing and cost model work amazingly well for an agency and while I wish that their administrative tools were easier to use when managing seats, it's not something we have to do often. While we were still a Sketch shop this past year, it's possible that the winds of change are blowing. We've been having internal conversations recently about switching to Figma which has quickly emerged as the cool, new kid on the block. I've used Figma a bit and its collaboration tools blow Sketch out of the water (at least at the time of this writing.)

I believe I overlooked our use of Zeplin in my previous tool list. That is a fairly significant oversight on my part! We've been using Zeplin for years now and I don't see that changing any time soon. Zeplin is a very robust tool that engineers and designers can use to easily export image assets out of Sketch (or Figma) and import directly into project workspaces. Zeplin provides that "last mile" in the asset pipeline and supports exporting for web, iOS or Android, each of which have differing format requirements. We love Zeplin!

A critical part of our development process with clients is our prototyping phase. We work extensively with them to help them understand how the UI that we are designing will translate into UX. Particularly when working on mobile platforms, the interface really needs to "feel" right, and InVision is the prototyping tool that we utilize.

Operational/Finance Tools

Like nearly every other small business in the U.S. we use Quickbooks to manage our finances. I don't have a lot of good things to say other than the fact that our CPA prefers us to use Quickbooks.

Silverpine's web content is fairly static, and in general our web traffic is very low. I think it's mostly visited by people who are curious about who we are in the hopes of potentially partnering with us on a project. To that end, we don't really need much in terms of web hosting, so we use Squarespace. One of the best things I can say about Squarespace is that it is easy to get a decent looking web page up, quickly. They have a plethora of templates and basic customization is very easy. If you start wanting to get fancy, though, it gets incredibly clunky, quickly. It's also slightly expensive in comparison to some of its competitors. That being said, we've been using it for years now without too many complaints.

One new addition/change to our set of tools is that this past year we dropped Blinksale and replaced it with Harvest for invoicing clients. A lot of people use Harvest for time tracking, but it also has a good set of invoicing tools and reports. Both Blinksale and Harvest are good tools and are fairly similar in terms of cost, but Blinksale seems to cater its tools more towards freelancers and less towards agencies. Harvest seems to do a better job of catering to both groups, and it's made our invoicing processes much easier this past year.

People like to get paid. That seems to be a nearly universal truth, and for us, Gusto is how that happens. We've been using Gusto for quite a while now and they have done a fantastic job at continuing to roll out new features and products that really complement their core offering, which is payroll. We use Gusto for both employees and contractors, and I haven't heard a single issue about it. Gusto does a really good job at tracking taxes and tax reporting for federal, state and most local requirements and many times they will pro-actively alert us about upcoming changes. On top of that, their customer service is very responsive so when we do have questions about things, they get back to us quickly with real answers. Gusto is fantastic.

Silverpine®

We received notification today that our trademark on “Silverpine” was registered with the USPTO. We first filed for application back in August, so it’s been a long time coming. While it’s certainly not critical to our business operations, it’s nice to have protection around our brand. I’ll have to get used to writing Silverpine® from now on though.

Screen Shot 2020-03-20 at 1.29.56 PM.png

 

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

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.

	<table width="600" cellspacing="15"><tr>
		<td width="100%">
			<br>
			<br>
			<font size="3" face="Arial, Helvetica, sans-serif"><b>Overview:</b></font>
			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.
			<br>
			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 
			<br><i>
			<br>          "...a form of logarithmic data compression
			<br>          for audio data.  Due to the fact that we hear logarithmically, 
			<br>          sound recorded at higher levels does not require the same 
			<br>          resolution as low-level sound.  This allows us to disregard
			<br>          the least significant bits in  high-level data.  This turns  
			<br>          out to resemble a logarithmic transformation.  The resulting
			<br>          compression forces a 16-bit number to be represented as an 8-bit 
			<br>          number." </i>
			<a href="https://web.archive.org/web/20040608152810/http://www-s.ti.com/sc/psheets/spra267/spra267.pdf">
			(www-s.ti.com/sc/psheets/spra267/spra267.pdf)
			</a>
			<br>
			<br>And from the comp.dsp newsgroup FAQ we also get this definition:
			<br><i>
			<br>          Mu-law (also "u-law") encoding is a form of logarithmic
			<br>          quantization or companding. It's based on the observation that
			<br>          many signals are statistically more likely to be near a low
			<br>          signal level than a high signal level. Therefore, it makes
			<br>          more sense to have more quantization points near a low level
			<br>          than a high level. In a typical mu-law system, linear samples
			<br>          of 14 to 16 bits are companded to 8 bits. Most telephone
			<br>          quality codecs (including the Sparcstation's audio codec) use
			<br>          mu-law encoded samples.
			<br></i>          
			<br>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.
			<br>
			<br><b><i>DISCLAIMER:</i></b>
			<br>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.
			<br>
			<br>
			<br><font size="3" face="Arial, Helvetica, sans-serif"><b>Mu-Law Compression:</b></font>
			<br>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".  
			<br>To generate a compressed Mu-Law sample from an uncompressed sample, the following
			algorithm is applied to the 16-bit source sample.  
			<br><i>(Please refer to the code listing for Mu-Law compression.)</i>
			<br>
			<br>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.)
			<br>
			<br>
			<br><b>MuLaw Compresion Code:</b>
			<br>
			<font size="1" face="Courier, Helvetica, sans-serif">
			<br>const int cBias = 0x84;
			<br>const int cClip = 32635;
			<br>
			<br>static char MuLawCompressTable[256] = 
			<br>{
			<br>     0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
			<br>     4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
			<br>     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
			<br>     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
			<br>     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
			<br>     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
			<br>     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
			<br>     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
			<br>     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
			<br>};
			<br>
			<br>unsigned char LinearToMuLawSample(short sample)
			<br>{
			<br>     int sign = (sample &gt;&gt; 8) &amp; 0x80;	
			<br>     if (sign)
			<br>          sample = (short)-sample;
			<br>     if (sample &gt; cClip)
			<br>          sample = cClip;
			<br>     sample = (short)(sample + cBias);
			<br>     int exponent = (int)MuLawCompressTable[(sample&gt;&gt;7) &amp; 0xFF];
			<br>     int mantissa = (sample &gt;&gt; (exponent+3)) &amp; 0x0F;
			<br>     int compressedByte = ~ (sign | (exponent &lt;&lt; 4) | mantissa);
			<br>	
			<br>     return (unsigned char)compressedByte;
			<br>}
			<br></font>
			<br>
			<br>
			<br>
			<br><font size="3" face="Arial, Helvetica, sans-serif"><b>A-Law Compression:</b></font>
			<br>
			<br>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.
			<br>
			<br>
			<br><b>A-Law Compression Code:</b>
			<br>
			<font size="1" face="Courier, Helvetica, sans-serif">
			<br>static char ALawCompressTable[128] = 
			<br>{
			<br>     1,1,2,2,3,3,3,3,
			<br>     4,4,4,4,4,4,4,4,
			<br>     5,5,5,5,5,5,5,5,
			<br>     5,5,5,5,5,5,5,5,
			<br>     6,6,6,6,6,6,6,6,
			<br>     6,6,6,6,6,6,6,6,
			<br>     6,6,6,6,6,6,6,6,
			<br>     6,6,6,6,6,6,6,6,
			<br>     7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7,
			<br>     7,7,7,7,7,7,7,7
			<br>};
			<br>
			<br>unsigned char LinearToALawSample(short sample)
			<br>{
			<br>     int sign;
			<br>     int exponent;
			<br>     int mantissa;
			<br>     unsigned char compressedByte;
			<br>
			<br>     sign = ((~sample) &gt;&gt; 8) &amp; 0x80;
			<br>     if (!sign) 
			<br>          sample = (short)-sample;
			<br>     if (sample &gt; cClip)
			<br>          sample = cClip;
			<br>     if (sample &gt;= 256)
			<br>     {
			<br>          exponent = (int)ALawCompressTable[(sample &gt;&gt; 8) &amp; 0x7F];
			<br>          mantissa = (sample &gt;&gt; (exponent + 3) ) &amp; 0x0F;
			<br>          compressedByte = ((exponent &lt;&lt; 4) | mantissa);
			<br>     }
			<br>     else 
			<br>     {
			<br>          compressedByte = (unsigned char)(sample &gt;&gt; 4);
			<br>     }
			<br>     compressedByte ^= (sign ^ 0x55);
			<br>     return compressedByte;
			<br>}
			<br></font>
			<br>
			<br>
			<br><font size="3" face="Arial, Helvetica, sans-serif"><b>Decompression:</b></font>
			<br>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!
			<br>
			<br>
			<br><b>Decompression Code:</b>
			<br><font size="1" face="Courier, Helvetica, sans-serif">
			<br>static short MuLawDecompressTable[256] = 
			<br>{        
			<br>     -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
			<br>     -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
			<br>     -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
			<br>     -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
			<br>      -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
			<br>      -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
			<br>      -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
			<br>      -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
			<br>      -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
			<br>      -1372, -1308, -1244, -1180, -1116, -1052,   -988,   -924,
			<br>       -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,
			<br>       -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,
			<br>       -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,
			<br>       -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,
			<br>       -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,
			<br>        -56,   -48,   -40,   -32,   -24,   -16,    -8,     0,
			<br>      32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
			<br>      23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
			<br>      15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
			<br>      11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,
			<br>       7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,
			<br>       5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,
			<br>       3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,
			<br>       2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,
			<br>       1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,
			<br>       1372,  1308,  1244,  1180,  1116,  1052,   988,   924,
			<br>        876,   844,   812,   780,   748,   716,   684,   652,
			<br>        620,   588,   556,   524,   492,   460,   428,   396,
			<br>        372,   356,   340,   324,   308,   292,   276,   260,
			<br>        244,   228,   212,   196,   180,   164,   148,   132,
			<br>        120,   112,   104,    96,    88,    80,    72,    64,
			<br>         56,    48,    40,    32,    24,    16,     8,     0
			<br>};
			<br>
			<br>
			</font>
			<br>
			<br><font size="1" face="Courier, Helvetica, sans-serif">
			<br>static short ALawDecompressTable[256] = 
			<br>{
			<br>     -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
			<br>     -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
			<br>     -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
			<br>     -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
			<br>     -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
			<br>     -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
			<br>     -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
			<br>     -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
			<br>     -344,   -328,   -376,   -360,   -280,   -264,   -312,   -296,
			<br>     -472,   -456,   -504,   -488,   -408,   -392,   -440,   -424,
			<br>     -88,     -72,    -120,   -104,    -24,      -8,      -56,     -40,
			<br>     -216,   -200,   -248,   -232,   -152,   -136,   -184,   -168,
			<br>     -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
			<br>     -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
			<br>     -688,   -656,   -752,   -720,   -560,   -528,   -624,   -592,
			<br>     -944,   -912,  -1008,  -976,   -816,   -784,   -880,   -848,
			<br>      5504,   5248,   6016,   5760,   4480,   4224,   4992,   4736,
			<br>      7552,   7296,   8064,   7808,   6528,   6272,   7040,   6784,
			<br>      2752,   2624,   3008,   2880,   2240,   2112,   2496,   2368,
			<br>      3776,   3648,   4032,   3904,   3264,   3136,   3520,   3392,
			<br>      22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
			<br>      30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
			<br>      11008, 10496, 12032, 11520,  8960,   8448,   9984,   9472,
			<br>      15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
			<br>      344,   328,   376,   360,   280,   264,   312,   296,
			<br>      472,   456,   504,   488,   408,   392,   440,   424,
			<br>      88,    72,   120,   104,    24,     8,    56,    40,
			<br>      216,   200,   248,   232,   152,   136,   184,   168,
			<br>      1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,
			<br>      1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,
			<br>      688,   656,   752,   720,   560,   528,   624,   592,
			<br>      944,   912,  1008,   976,   816,   784,   880,   848
			<br>};
			<br></font>
			<br>
			<br>
		</td>
		<td width="25">
		</td>
	</tr></table>

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 Zumstein5027 NE 42nd Ave.
  4. Saint Simon Coffee - 2005 NE Broadway
  5. Tiny’s Coffee - 2031 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
 

 

The Trouble With Cross Posting

Recently, I setup a microblog (http://jonhays.me) to supplement my interactions on Twitter, Facebook and Instagram. After several conversations with my friend Manton, I decided that I would setup a new WordPress site and use that as the content “repository” and use cross-posting tools to distribute out to Twitter, Facebook and Instagram.

There are several reasons for doing this, but my primary motivations are:

  1. I want to spend less time on the individual social media platforms.
  2. I want to own my content and have better control over it.
After a little bit of consternation over appropriate domain names and blog names, I set about assembling the site and my work flow.

One of my high level requirements is that I wanted to be able to post to my microblog from my phone. Another of my requirements is that I wanted to be able to post entries both with and without images. Of course the third requirement that I mentioned above is that it needs to cross-post to Twitter, Facebook and Instagram.

Naively, I just assumed I could use the WordPress app and setup a few IFTTT triggers and be done with it. As you can guess, it’s not quite as simple as that.

Let’s tackle cross-posting of images first: what should be posted to Instagram for a blog entry with no image? With multiple images?

Realizing that simply posting from WordPress wasn’t going to work, my first instinct was to modify my workflow so that if I wanted to post an image, I would post from Instagram and use an IFTTT action to then cross-post the image to my microblog which would then cross-post to Facebook and Twitter.

Unfortunately, using this method is like playing a game of telephone with social media and the end result looks like this on Facebook:

Screen Shot 2016-04-12 at 10.30.16 AM

Several of my friends replied after seeing these asking if my computer got “hacked.”

Images aren’t the only troublesome area either. Take for instance cross-posting of text posts larger than 140 characters to Twitter. It turns out that IFTTT is actually quite terrible about handling these as well:

Screen Shot 2016-04-12 at 10.47.32 AM

Simply truncating the text without a link back to the original post is the worst kind of tease and also not acceptable. What a good cross-post tool should do is truncate at word boundaries and provide a link back to the original post. IFTTT is simply not up to the task for this.

In fact, as it turns out, IFTTT is actually quite terrible for cross-posting to just about every platform. I am investigating other alternatives at the moment but as of right now, I am still stuck with those terrible Facebook cross posts, and I have no way to post directly on the microblog and have the ones with images get cross-posted to Instagram.

Fortunately, there are smart people working on these problems! I’m using a beta tool to do the cross-posting to Twitter. The beta tool actually works quite well and I’ve been trying to convince the author to expand to include Facebook as well, but he’s reluctant to add more features at the moment because he’s trying to launch.

With so much out of control negativity and lack of author control on Twitter and Facebook, it feels like there is an opening for something like micro-blogs to augment existing platforms in a positive way. And while I don’t think Twitter or Facebook are going anywhere, I believe micro-blogs can help fill the gap for content creators that are conscientious about their craft.

I’m still exploring other avenues for cross-posting and I’ll try to post updates as I find them either here or on the microblog, but it feels like this is a viable, mostly untapped market.

 

First Musings on WWDC 2014

I’m writing this sitting in the San Francisco airport waiting for my return flight after a great week at Apple’s annual developer conference. It’s been an amazing week and I’m still processing a lot of what I experienced. First off, let me say that I believe that WWDC 2014 will be considered a turning point when viewed in retrospect. As a developer, I am absolutely giddy with all of the possibilities that Apple has opened up with their announcements. There is a stark contrast between last year’s developer conference and this year’s conference. At the end of last year, I was thinking about all of the things that I HAD to do with iOS 7. With iOS 8, I am absolutely bubbling with ideas of things that I GET to do. iOS 8 is going to be a big deal. From HealthKit to HomeKit to Extensions to CloudKit, Apple has paved the way for some amazing things to be built.

And then there is Swift. Swift has taken my excitement about the new frameworks and cranked it to 11. Not only has Apple provided a sorely needed modern language to the platform, they have delivered it complete with a fantastic toolset. Clearly this has been in the works for a very long time. Swift development reminds me of the early days when I was first learning to program. Back in those ancient days, you simply turned on your computer and started typing. Swift very much has the same feel to it. In fact, I actually plan to have my 9 year old son sit down with me and learn it together.

This is big stuff. I am still processing a lot of what I have seen and learned and will post more as I unravel it. I think this is going to be a great year to be an iOS and Mac developer!

Free Code!

For Sunlit 1.1 we decided to expand the import capabilities to include both Flickr and Instagram photos. One of my challenges in developing the interfaces was to simplify down the various functions available from Flickr and Instagram’s APIs and to try and provide a somewhat consistent interface across the two services for the app despite their differences. Writing this code was no mean feat for me as I had to implement OAuth not once, but twice because Flickr and Instagram use slightly different variations so I couldn’t simply reuse the code across the two classes.

By the time I was done, I was relatively happy with the interface I had created, and in the spirit of openness and sharing with the community in which I develop, I have decided publish the source for querying and requesting data from both these services in my open source toolbox code. Affectionately named UUFlickr and UUInstagram, the classes are relatively simple to use, and you should be able to get you up and running quickly. The only moderately tricky part is updating your app to handle the URL callback mechanisms required by the OAuth implementations. I had fun developing this code, and hopefully someone will find this useful. If you’re interested in using it in your project but need a little help, feel free to ping me and I’ll see if we can’t figure it out.

Enjoy!

What the Flappy Bird Knows

The strange case of Flappy Bird seems to be all over the Internet right now. If you’re unfamiliar with the app, take a moment to educate yourself. Reading comments from its creator, it’s clear that the developer did nothing to promote his app, and obviously had no idea that the wave of success was coming his way. Anyone with a discerning eye that plays the game will also come away confused because quite bluntly, there’s nothing noteworthy here. Instead, what you see is a bizarre case of mob mentality charging through the App Store, and ultimately, I feel sorry for him. However, what concerns me most about this situation is that it highlights all the things that are wrong with the App Store and the most troubling aspects being that these two things have now become the accepted norm:

  1. The App Store is viewed as developer hostile
  2. Success on the App Store is more or less a lottery ticket.
The App Store is Apple's ace-in-the-hole advantage in the Smart Phone platform wars and it should do everything it can to protect that advantage. As a case in point, I recently tried carrying a WindowsPhone for a week (more about that in a different post) and I found the device quite pleasing overall, but ultimately I couldn't get past the lack of essential third party apps. This is Apple's huge advantage, and as a platform, it is in Apple's best interests to treat the App Store as a meritocracy where the best of the best rise to the top. Right now, that isn't happening. I really hope that someone at Apple is paying attention.

Apple's Next Love

Apple’s next product is going to be a smart band. Not a smart watch; a smart band. The difference is subtle, but significant: a smart watch implies that the device’s input is chiefly on its face and that its primary job is to display information to the wearer. As a long time smart band wearer, I can tell you that a smart band very rarely displays information and is much more important as an information collector, and this is where it gets interesting: Apple’s next product is going to convince you to put a device in direct contact with your skin. If they succeed in convincing you the things that can be done are limited only by the complexity of its sensors: heart rate sensors, respiration sensors, temperature sensors, vasculature visualization, non-invasive glucose monitoring, and more. This also falls in line with some of Apple’s recent hires: Roy J.E.M Raymann, Michael O’Reilly M.D., Nancy Dougherty, Ravi Narashimanetc. This has the potential to radically change healthcare in our world. Apple’s new device could revolutionize how we think about detection and prevention, and if you ask any medical expert they will tell you that prevention and detection is orders of magnitude more important than treatment.

Manton Reece thinks that Apple needs to fall in love with their next product category, and I think he’s right, but I actually think that Apple has fallen in love with its next product which is exactly why it hasn’t launched yet. One of the problems with being in love with something that you’re developing is knowing when to ship 1.0. It needs to be right. It needs to be perfect. I have a feeling, though, that they’re getting close, and when they do finally announce it, we’re going to be amazed.

The Schemes of Sunlit

One of the things that we established early on as a core principle for Sunlit was that we wanted to make sure we were focused on story telling and the sharing of stories and not on things on the periphery. Many times during development, we were tempted to create an über-camera or a whiz-bang photo editor, however the App Store is full of other apps that do these things and do them well. We do include a fairly simple camera in the app as well as some beautiful filters that can be applied, but what we really envisioned was people using either the built in camera app or a great third party app (such as Favd) to take their photos and then create a story with Sunlit using the pictures they had already taken.

To us, the true beauty of Sunlit is how it pulls together the value created from other platforms (App.net, Dropbox, Ohai, etc.) and creates something that combines all those things into something that is somehow more moving than any of them alone. To that end, it was important that we provide a way for Sunlit to fit into this evolving macro-system because we recognize that the best apps and the best user experiences come when things work cohesively. We are always re-evaluating how we can do this best, but I am very happy that Sunlit launched with 1.0 support for a number of URL schemes, including support for the x-callback specification. We also have a number of actions that can be invoked externally to allow other apps to extend support for Sunlit into the activities that they do well. The URL schemes are documented here and are updated as we add support for other actions. If you are interested in adding support for any of these and need assistance, feel free to shoot me a message on ADN (@cheesemaker) or post your question in the Sunlit Glassboard Forum (invite code SUNLIT) or just shoot an email to support@riverfold.com

Now go build something great!

Sharing About Overshare

As Manton explained on his blog, we have been working on Sunlit for a long time. During that time, I can tell you that Sunlit changed and evolved from what we originally had envisioned. However, if you listen to Core Intuition, you know that we’ve been beta’ing the app for quite a while. So, when Jared Sinclair and Justin Williams announced the Overshare Kit open source replacement for iOS UIActivityViewControllers, we had a hard decision to make. We had to decide if it was worth adding new functionality at such a late point in our development or if we could live without it.

Now, I don’t want to get into a UI/UX design war with anyone. I know that there are various, differing opinions on the look and feel of iOS 7 and I am but a humble developer. However, if you have ever implemented a custom UIActivity in iOS it is undeniable that the monochrome icons that you are forced to use look inexcusably ugly. In fact, they are so disappointingly ugly that Manton and I felt that we had no choice but to use Overshare Kit.

photo

I am proud of many things that we’ve built into Sunlit and our support for Overshare Kit is one of them. I love the iOS development community and Jared is a great guy so being able to support a project that he owns and cares about is a good feeling. We also had the opportunity to contribute directly to the project and a number of changes that we made for Sunlit made it back into the master branch. To me, this is the heart and soul of iOS development. It’s doing things with other people that share your value of quality.

If you are a developer, and you haven’t checked out Overshare Kit, you really should. It allows you to create beautiful sharing activity views. It comes with some of the most common ones out of the box (Facebook, Twitter, etc) and is fairly easy to extend. Jared is also very responsive with suggestions/fixes for things, and while it’s only been around for a few months, I can’t recommend it highly enough. Oh, and if you’re having trouble integrating it, feel free to hit me up. I’d love to see more apps using it.

Sunlit Is For Me

If you don’t like my new app Sunlit, I will still be happy. Don’t get me wrong. I hope you like Sunlit and it helps you do things with your photos and memories that you couldn’t do before, but if you don’t like it, I’ll still be happy. I’ll be happy because Sunlit is for me.

Sunlit is an app that I’ve wanted for years and I’m thrilled to finally have on my phone. One of my absolute favorite uses for Sunlit is for capturing memories from smaller events. These are events that while important, don’t tend to have the significance of a wedding or Disneyland trip so the photos often never see the light of day. With Sunlit, however, I can now do something meaningful with these photos.

Earlier this year while Sunlit was in beta, I went to a birthday party for my wife’s mother. There were about 15 people present and the celebration went on for around 2 hours. During that time, I snapped close to 60 photos of the party. Some of these photos were blurry, some of them were decent, but a few were quite good. On our drive home, I picked the best ones and created “Nana’s Birthday” story. I then used Sunlit to publish “Nana’s Birthday” and suddenly I had a story captured on a web page that I could share with my wife’s family quickly via an email or SMS message.

This is an important thing to me. Before Sunlit, these photos were just photos. The effort to take them was almost more work than it was worth because they would rarely be seen by me, let alone anyone else. With Sunlit, photos turn into memories. They turn into something beautiful that I can share and that can be appreciated and loved by the people I care about.

So, if you don’t like Sunlit, that’s ok. Sunlit is for me, but Sunlit is also for sharing so I really do hope you like it too.