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.

by Peter on 06/18/2008, in Software, 0 comments

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.

by Peter on 06/07/2008, in Software, 0 comments

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.

by Peter on 06/06/2008, in Random, 0 comments

The other day I decided to class-dump QTKit in hopes that QTMovie would have a real update-in-place method [1]. Well first of all, class-dump doesn’t work with 64 and 32 bit universal binaries so I didn’t actually use class-dump but rather o-tool [2].
Unfortunately QTKit doesn’t have some hidden update-in-place method. But! that isn’t to say it doesn’t have a bunch of really useful stuff that should be public for the sake of mine, and as a result everyone else’s sanity.
The following are just a few of the hidden methods, they’re simply the ones I found the most immediately useful.

@property float treble;
@property float bass;

The treble and bass properties do exactly what you think they do, they change the bass and treble levels on the receiving movie. The value scale is (oddly) between -8.0 and 8.0, 0.0 being default.

@property float balance;

Balance, just like the treble and bass properties, does exactly what you think it does. The value scale is between -1.0 and 1.0, 0.0 being default.

@property float gain;

I can’t say I am entirely sure about this property, it appears to be an alias for volume. The value scale is 0.0 to 1.0, 1.0 being default.

- (BOOL)isDRMAuthorized;
- (BOOL)isDRMProtected;

By far the most amusing of the hidden methods. I could see this being fairly useful.

@property BOOL saveable;

As far as I can tell this is just a convenience method that doesn’t really seem to do anything all that useful.

@property (copy) NSDictionary *annotations;

This has to be the most useful of the properties I found. ‘annotations’ is dictionary which contains all of the metadata associated with the file you’ve loaded into a QTMovie.

@property (copy) NSString *copyright;

This is likely another convenience method, the same information is available thru -attributeForKey: if you don’t feel like using private methods.

There we plenty of other private methods, many of which were convenience methods. The list above is simply of the methods I found immediately useful, there are dozens more.
If you wish to view the dump in its entirety the dump file is available here [3].


[1]: Yes, I am aware of -updateMovieFile, it doesn’t work for anything other then MOV files.
[2]: Okay, if you hadn’t guessed by now I dumped QTKit on Leopard.
[3]: Code dump generated with “otool -o -V /System/Library/Frameworks/QTKit.framework/Versions/A/QTKit > QTKit.dump”.

by Peter on 05/08/2008, in Cocoa, Software, 0 comments

In a move of what could only be described as absolute laziness, I am releasing the Transbar source into the general public.

It is under the MIT license:

/*
 * Copyright (c) 2008 Peter MacWhinnie
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

Transbar Page

by Peter on 04/29/2008, in Software, 0 comments
Copyright © 2007-2008, Peter MacWhinnie. All Fancy Rights Reserved. Boring ones too.