For a little while now I’ve wondered why the label “Open Source” seems to mean that whatever the label has been applied to is fantastic, beautiful, and somehow better than everything else in the world evar. To me this seems a little backward, at best. So far I’ve found very few great open source desktop applications. Its not like there aren’t thousands, it just seems like most of them just aren’t up to par with their commercial counterparts, and, even if they are their interface is so damn awful that no one knows how to use it!
Interface
It seems one of the biggest problems open source software has is lack of good taste when it comes to interface. Often times open source applications are either unbelievably ugly and alien or so complicated that you need a manual to find out how to select something.
Its not that the open applications are inherently low quality or unstable, its just painfully obvious that the people who wrote the code, also designed the interface. Not all people who know how to write code are terrible at interface design, it just seems to be a fairly common trend. A somewhat notable exception to the rule seems to be growl, which is in fact open source and really even the ugliest themes for it are tolerable. ‘Though it does help they’ve had [semi-]professional designers, design things for them.
The backside
Although most open source software (OSS) seems to have absolutely no desire to actually have a nice polished interface, OSS does have a saving grace: Libraries. If there’s one thing the open source community is good at, its producing fairly quality libraries. The reason is fairly simple: The people who are actively involved in the OSS community are ultimately massive geeks and programmers. They know how to write good code and assuming the open project in question doesn’t have an interface, you can bet its going to be of some level of quality.
Another thing thats good about OSS is that it produces open, common standards that tend to carry across platforms, and projects. A good example of this is ogg vorbis which is a lovely open lossy audio format. Ogg Vorbis is now fairly widely supported and is really a rather nice format.
End.
I suppose the point I was trying to get across with this fairly crappy blog post was that open source desktop applications are usually of low quality in comparison to applications that have been at least partially commercially backed. Of course this is not always true, and its always nice to see an exception. In addition to that the OSS community seems to be quite prolific at producing both libraries and standards en-masse. Certainly not all libraries and standards are solid gold, but a notable amount are.
Until my next attempt to write a blog post, byebye.
For anyone who’s a) talked to me; b) followed by Twitter feed; you’ll know that OS X Leopards new AudioQueue Services is a brilliantly simple API, its very easy to use and understand. But, its also incredibly buggy and completely stops working with certain stream types. As a result of this I had to (somewhat angrily) write a wrapper for the output AudioUnit so I could continue to enjoy how simple things really were in implementation. So without further ado, here’s the header and implementation file. There is some documentation in the header section, however I make no guarantees about easy it will be for you to hook up, I wrote it specifically for my needs.
Header:
/*
* RAAudioQueue.h
* PlayerKit
*
* Created by Peter MacWhinnie on 5/31/08.
* Copyright 2008 Roundabout Software. All rights reserved.
*
*/
#include <AudioUnit/AudioUnit.h>
#include <CoreAudio/CoreAudio.h>
#ifndef RAAudioQueue_h
#define RAAudioQueue_h 1
/*!
@header RAAudioQueue
@abstract RAAudioQueue is a thin wrapper around AudioUnit_Output
@discussion RAAudioQueue is a paper thin wrapper around the output functionalities
of the AudioUnit framework. Its interface is designed to mimic leopards AudioQueue.
*/
/*!
@enum
@abstract RAAudioQueue errors
@discussion These are just errors that are specific to RAAudioQueue,
90% of the time the errors passed by the RAAudioQueue functions
are defined in a system framework, and not here.
@constant RAAudioQueueErrorCannotFindComponent This is returned if an output AudioUnit can’t be created
@constant RAAudioQueueErrorCannotCreateAudioQueue This is only returned if the memory cannot be allocated for an RAAudioQueueRef.
@constant RAAudioQueueErrorMissingParameters This is returned if you don’t pass in all the required parameters to a function.
*/
enum RAAudioQueueErrors
{
RAAudioQueueErrorCannotFindComponent = ‘Cmp?’,
RAAudioQueueErrorCannotCreateAudioQueue = ‘Mem?’,
RAAudioQueueErrorMissingParameters = ‘Pra?’,
};
/*!
@typedef RAAudioQueueRef
@abstract An Opaque RAAudioQueue struct
*/
typedef struct __RAAudioQueue * RAAudioQueueRef;
/*!
@typedef RAAudioQueueOutputProc
@abstract RAAudioQueueOutputProc is the template
@field audioQueue the sender
@field bufferCapacity the capacity of the AudioBuffer
@field buffer the AudioBuffer
@field userData data you passed to RAAudioQueue for this occassion.
*/
typedef void (*RAAudioQueueOutputProc)(RAAudioQueueRef audioQueue, UInt32 bufferCapacity, AudioBuffer *buffer, void *userData);
/*!
@function
@abstract Create a new RAAudioQueue
@discussion This method hides all the drudgery of opening the output audio unit.
@param inFormat the ASBD the RAAudioQueue is to use
@param inCallbackProc the callback to use when data is needed
@param inUserData userData that will be passed to you in the callback
@param outAudioQueue the resulting RAAudioQueue or 0 if there was an error.
*/
extern OSStatus RAAudioQueueNewOutput(AudioStreamBasicDescription *inFormat, RAAudioQueueOutputProc inCallbackProc, void *inUserData, RAAudioQueueRef *outAudioQueue);
/*!
@function
@abstract Dispose of an RAAudioQueue
@param audioQueue the RAAudioQueue to destroy
*/
extern OSStatus RAAudioQueueDispose(RAAudioQueueRef audioQueue);
/*!
@function
@abstract Start the specified audio queue
*/
extern OSStatus RAAudioQueueStart(RAAudioQueueRef audioQueue);
/*!
@function
@abstract Stop the specified audio queue
*/
extern OSStatus RAAudioQueueStop(RAAudioQueueRef audioQueue);
/*!
@function
@abstract RAAudioQueueSetProperty is a proxy for AudioUnitSetProperty
*/
extern OSStatus RAAudioQueueSetProperty(RAAudioQueueRef audioQueue, AudioUnitPropertyID inID, AudioUnitScope inScope, const void *inData, UInt32 inDataSize);
/*!
@function
@abstract RAAudioQueueGetProperty is a proxy for AudioUnitGetProperty
*/
extern OSStatus RAAudioQueueGetProperty(RAAudioQueueRef audioQueue, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void *outData, UInt32 *ioDataSize);
/*!
@function
@abstract RAAudioQueueSetParameter is a proxy for AudioUnitSetParameter
*/
extern OSStatus RAAudioQueueSetParameter(RAAudioQueueRef audioQueue, AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames);
/*!
@function
@abstract RAAudioQueueGetParameter is a proxy for AudioUnitGetParameter
*/
extern OSStatus RAAudioQueueGetParameter(RAAudioQueueRef audioQueue, AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue *outValue);
/*!
@function
@abstract Set the volume level for the specified audio queue
*/
extern OSStatus RAAudioQueueSetVolume(RAAudioQueueRef audioQueue, float volume);
/*!
@function
@abstract Get the volume level for the specified audio queue
*/
extern OSStatus RAAudioQueueGetVolume(RAAudioQueueRef, float *volume);
#endif /* RAAudioQueue_h */
Implementation:
/*
* RAAudioQueue.c
* PlayerKit
*
* Created by Peter MacWhinnie on 5/31/08.
* Copyright 2008 Roundabout Software. All rights reserved.
*
*/
#include “RAAudioQueue.h”
#pragma mark Implementation
struct __RAAudioQueue
{
AudioUnit outputAudioUnit;
void *userData;
AudioStreamBasicDescription format;
RAAudioQueueOutputProc outputProc;
};
OSStatus __RAAudioQueueRenderCallback(void *userdata, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
RAAudioQueueRef self = (RAAudioQueueRef)userdata;
int i = 0;
while (i < ioData->mNumberBuffers)
{
AudioBuffer buffer = ioData->mBuffers[i];
self->outputProc(self, (inNumberFrames * self->format.mBytesPerPacket), &buffer, self->userData);
if(buffer.mDataByteSize == 0)
{
AudioOutputUnitStop(self->outputAudioUnit);
break;
}
i++;
}
return noErr;
}
#pragma mark -
#pragma mark Tools
const ComponentDescription __RAAudioQueue_componentDescription = {
/* componentType */ kAudioUnitType_Output,
/* componentSubType */ kAudioUnitSubType_HALOutput,
/* componentManufacturer */ kAudioUnitManufacturer_Apple,
/* componentFlags */ 0,
/* componentFlagsMask */ 0
};
OSStatus __RAAudioQueueGetOutputAudioUnit(AudioUnit *outputUnit)
{
Component component = FindNextComponent(NULL, (ComponentDescription *)&__RAAudioQueue_componentDescription);
if(!component) { return RAAudioQueueErrorCannotFindComponent; }
return (OSStatus)OpenAComponent(component, outputUnit);
}
#pragma mark -
#pragma mark Creation
OSStatus RAAudioQueueNewOutput(AudioStreamBasicDescription *inFormat, RAAudioQueueOutputProc inCallbackProc, void *inUserData, RAAudioQueueRef *outAudioQueue)
{
if(!inFormat || !inCallbackProc || !outAudioQueue) { return RAAudioQueueErrorMissingParameters; }
//Get output unit
AudioUnit outputUnit;
OSStatus error = __RAAudioQueueGetOutputAudioUnit(&outputUnit);
if(error != noErr) { return error; }
//Create an RAAudioQueueRef
RAAudioQueueRef audioQueue = malloc(sizeof(struct __RAAudioQueue));
if(!audioQueue) { return RAAudioQueueErrorCannotCreateAudioQueue; }
audioQueue->outputAudioUnit = outputUnit;
audioQueue->userData = inUserData;
audioQueue->outputProc = inCallbackProc;
audioQueue->format = *inFormat;
//Enable IO
UInt32 enableIO = 1;
error = AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
if(error != noErr) { return error; }
//Set the input format
error = AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, inFormat, sizeof(*inFormat));
if(error != noErr) { return error; }
//Set the input callback
AURenderCallbackStruct inputCallbackStruct;
inputCallbackStruct.inputProc = __RAAudioQueueRenderCallback;
inputCallbackStruct.inputProcRefCon = audioQueue;
error = AudioUnitSetProperty(outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputCallbackStruct, sizeof(inputCallbackStruct));
if(error != noErr) { return error; }
//Initialize the AudioUnit
error = AudioUnitInitialize(outputUnit);
if(error != noErr) { return error; }
*outAudioQueue = audioQueue;
return noErr;
}
OSStatus RAAudioQueueDispose(RAAudioQueueRef audioQueue)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
audioQueue->userData = NULL;
audioQueue->outputProc = NULL;
AudioUnitUninitialize(audioQueue->outputAudioUnit);
OSStatus error = CloseComponent(audioQueue->outputAudioUnit);
free(audioQueue);
return error;
}
#pragma mark -
OSStatus RAAudioQueueStart(RAAudioQueueRef audioQueue)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return (OSStatus)AudioOutputUnitStart(audioQueue->outputAudioUnit);
}
OSStatus RAAudioQueueStop(RAAudioQueueRef audioQueue)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return (OSStatus)AudioOutputUnitStop(audioQueue->outputAudioUnit);
}
#pragma mark -
#pragma mark Parameters
OSStatus RAAudioQueueSetProperty(RAAudioQueueRef audioQueue, AudioUnitPropertyID inID, AudioUnitScope inScope, const void *inData, UInt32 inDataSize)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return AudioUnitSetProperty(audioQueue->outputAudioUnit, inID, inScope, 0, inData, inDataSize);
}
OSStatus RAAudioQueueGetProperty(RAAudioQueueRef audioQueue, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void *outData, UInt32 *ioDataSize)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return AudioUnitGetProperty(audioQueue->outputAudioUnit, inID, inScope, inElement, outData, ioDataSize);
}
#pragma mark -
OSStatus RAAudioQueueSetParameter(RAAudioQueueRef audioQueue, AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return AudioUnitSetParameter(audioQueue->outputAudioUnit, inID, inScope, inElement, inValue, inBufferOffsetInFrames);
}
OSStatus RAAudioQueueGetParameter(RAAudioQueueRef audioQueue, AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue *outValue)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return AudioUnitGetParameter(audioQueue->outputAudioUnit, inID, inScope, inElement, outValue);
}
#pragma mark -
OSStatus RAAudioQueueSetVolume(RAAudioQueueRef audioQueue, float volume)
{
return RAAudioQueueSetParameter(audioQueue, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, volume, 0);
}
OSStatus RAAudioQueueGetVolume(RAAudioQueueRef audioQueue, float *volume)
{
return RAAudioQueueGetParameter(audioQueue, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, (AudioUnitParameterValue *)&volume);
}
The above code may be used, modified, and redistributed in binary and source form, if all of the following conditions are met:
- Neither I, nor Roundabout Software is responsible for any damage this code might cause.
- Neither the name ‘Roundabout Software’ nor the name ‘Peter MacWhinnie’ may be used to endorse or promote products derived from the above source code without specific prior written permission.
That aside I would appreciate any improvements to this code be sent to me, this, however, is not a requirement.
As you probably noticed, I haven’t posted a whole lot of anything lately. Sure my Twitter feed slowly trickles into my WP blog, but those aren’t really posts, just short snippets of pure insanity. For whatever reason I feel obliged to write something, no matter how shitty the end result of this ‘writing’ is. Its possible that it has something to do with the fact I am waiting for my PowerBook to back up, or the fact I am waiting for a clients new MacBook Pro to finish running updates which is always annoying. Well sense I don’t really have that much to right about, here goes.
I’ve updated Tikiug to a fairly unimpressive version 2.0, I’ve told no one and posted nowhere about it, mainly because it actually drops functionality that I don’t believe anyone ever used. It no longer allows export to DLTA theme files, like anyone really uses them. It does, however, add a nice neat per-variation file list that allows you to drag ‘n drop files from the GUIKit to anywhere in your file system, no need to do a complete export anymore. This is actually the main reason I am calling it 2.0. In addition to this fairly minor feature, its a complete rewrite. Thats right, I dropped the original source completely and started over with a Leopard-only design in mind. Sure, it could still run on Tiger but I don’t care about Tiger.
I’ve also placed a small application on my site called Reflectomatic, again I’ve told no one and posted nowhere about it, but it is there. Reflectomatic is a one-trick application: it adds reflections to any type of image you want and then exports that image+reflection to a PNG file (there is no support for other formats, so suck it up and use PNG). It gives you some-what fine-grained control over the reflection, letting you control opacity, offset, and the amount of the image actually in the reflection. I wrote Reflectomatic because I didn’t want to spend thirty dollars for Pictureque to make easy reflections. Fucking Delicious Generation developers.
In addition to that as you may, or may not have noticed I’ve redesigned my site again. I moved away from the soft brown green and blue colors of the last design and moved into a stark black and white design that presents all of the content of petermacwhinnie.com with absolutely no fuss. Truly, it is boring. But! That doesn’t mean its not a nice design, even though I am of course already completely fucking bored with it. I really do hate web design, its so difficult to be satisfied with it for more then 15 short unpleasant minutes. And no, I don’t think a professional designer could do away with this, they may be better at it then me, but they aren’t that much better.
Besides all that lovely shit the school year is coming to a fast-approaching end, as in the last quarter is over on Friday the 13th, of all days. Quite frankly, I am really, really, REALLY fucking glad the school year is over. Its been long and some-what productive, but that doesn’t mean I won’t be glad to be ‘officially’ over for the summer. I’ve been working (and procrastinating) on a history paper, it covers 50 years of the 20th century. Its a time period I quite enjoy reading and learning about, but I swear its going to kill me to finish this paper. I am hoping that after the official school year is over I can take a breath and enjoy writing the rest of the paper, because its likely going to take until the next school year starts to get it up to my own standards, as well as everyone else’s.
Well, that just about covers everything I wanted to briefly write about. Amusingly enough, time machine still isn’t done backing up my PowerBook, and the MacBook Pro is still running updates. Damn I hate rainy weather. I do hope you’ve enjoyed this short time we’ve had together, until next time.
Bye.
Quick Edit: the MacBook Pro is now installing, its done downloading; Also, its really creepy how Wordpress hides the post you’ve been working on when you press Save, it seems like its just eating it and not saving it. I hate web applications.