cremes' CoreAudio and QuickTime Blog
Track the work I'm doing on a QuickTime Component to support the FLAC lossless compression codec via CoreAudio




Project Downloads:



My Other Projects:

External Links:














Click to see the XML version of this web page.

Click here to send an email to the editor of this weblog.
 

 

Tuesday, February 10, 2004
 

Final post for the night.

I finished slogging through the ACAppleIMA4Codec and ACAppleIMA4Decoder classes. It's neat how C++ classes build upon themselves and use inherited functionality, but tracing execution through the subclasses and parent classes can be difficult.

Enough grousing... I think I get it. I'm looking at the decoder first because an obvious Day One target is being able to decode the FLAC stream and output linear PCM.

Here's what I don't get. How does QuickTime know how to play an audio format it hasn't seen before? If I drop in a fully functional FLAC decoder into /Library/QuickTime, QT would have the ability to decode the stream data but would QT be aware of this? How?

From the old Component Manager I can see how an application can seek to match itself up with a codec (by passing in a componentType, componentSubType, etc., etc.) but what about the MoviePlayer application. How can it open and recognize enough information from the file to match up with the right codec?

I don't even know where to look to find these answers. I'll probably ask on quicktime-api, but I'd appreciate any pointers in the comments section or via email to me.
11:00:06 PM    comment []



Minor but important style note regarding the Apple example source.

Any method variable that is named "in" (such as "inPropertyID") indicates that this variable is being passed in to the method for some operation.

Any method variable named "out" (such as "outWritable") indicates that this variables value will be returned to the caller and used in some fashion.

[edit] Any method variable named "io" (such as "ioPropertyDataSize") indicates that the caller is passing in a value for the method to use and the method may pass back a changed value to the caller.

Makes the code easier to read and understand.
10:12:59 PM    comment []



Looking through the ACCodec, ACBaseCodec and ACSimpleCodec classes I noticed that several things looked eerily similar to the old Sound Manager stuff. When I got to the file ACCodecDispatch.h I had an "ah ha" moment.

The template class defined in that file now takes the place of all that screwy C preprocessor bullshit from the old Carbon Sound Manager.

In the old Sound Manager, there would be a file in your project that had contents like:

	ComponentSelectorOffset (6)
	
	ComponentRangeCount (2)
	ComponentRangeShift (7)
	ComponentRangeMask	(7F)
	
	ComponentRangeBegin (0)
		ComponentError (Target)
		ComponentError (Register)
		StdComponentCall (Version)
		StdComponentCall (CanDo)
		StdComponentCall (Close)
		StdComponentCall (Open)
	ComponentRangeEnd (0)

This tied in to some *really* ugly stuff in the background. To call your FooCodec's Register function it would have to concatenate your codec name ("FooCodec") to a function basename (like "Register") and include the parameters along with any commas to separate those parameters. The whole mess would look like:

#define CALLCOMPONENT_BASENAME() FooCodec
#ifdef CALLCOMPONENT_BASENAME
	#ifndef CALLCOMPONENT_GLOBALS
		#define CALLCOMPONENT_GLOBALS() 
		#define ADD_CALLCOMPONENT_COMMA 
	#else
		#define ADD_CALLCOMPONENT_COMMA ,
	#endif
	#define CALLCOMPONENT_GLUE(a,b) a##b
	#define CALLCOMPONENT_STRCAT(a,b) CALLCOMPONENT_GLUE(a,b)
	#define ADD_CALLCOMPONENT_BASENAME(name) CALLCOMPONENT_STRCAT(CALLCOMPONENT_BASENAME(),name)

EXTERN_API( ComponentResult ) ADD_CALLCOMPONENT_BASENAME(Open) (CALLCOMPONENT_GLOBALS() ADD_CALLCOMPONENT_COMMA ComponentInstance self);

#endif /* CALLCOMPONENT_BASENAME */

The system would use it to piece together your FooCodecOpen(FooCodecGlobalsPtr globals, ComponentInstance self) function name.

All of that nonsense is now replaced by (edited for length):

template <class CodecClass>
ComponentResult ACCodecDispatch(ComponentParameters* inParameters, CodecClass* inThis)
{
	ComponentResult	theError = kAudioCodecNoError;
	
	try
	{
		switch (inParameters->what)
		{
			//	these selectors don't use the object pointer
			
			case kComponentOpenSelect:
				{
					CodecClass*	theCodec = new CodecClass();
					SetComponentInstanceStorage(((AudioCodecOpenGluePB*)inParameters)->inCodec, (Handle)theCodec);
				}
				break;
		};
	}
	catch(ComponentResult inErrorCode)
	{
		theError = inErrorCode;
	}
	catch(...)
	{
		theError = kAudioCodecUnspecifiedError;
	}
	
	return theError;
}

Much cleaner in my view. It uses the power of templates to sub in the correct names and dispatch the component function call to the appropriate class method. Ultimately both things perform the same function, but one way requires you to chase through multiple files to figure out what is going on while the other way just prints the code in a nice, clean manner. I never thought I'd say this, but the C++ way is the "right" way to do this.

Nice job, CoreAudio guys!
9:40:55 PM    comment []




To all of you wondering when I'll start coding, the answer is simple: when I understand what the hell I'm doing.

I need to ask some questions, study some code, ask some more questions, study the code again, do some judicious copy & paste, test, crash my IDE, ask some more questions, etc. The code will come.
8:18:35 PM    comment []




Back on the 9th I asked a question about definitions on audio concepts like sample, frame, etc. One of my reasons for doing so is that depending on the codec I look at, each one appears to use different words to describe the same concept.

What I really needed was to figure out what Apple means by these words. That's the environment I'm in, so it's the one I need to grok. Googling around I found this PDF on the Apple developer site. It's a glossary of audio terms and what they mean in the Apple environment. Very useful!

I can use that glossary to translate between Apple and (in this case) FLAC. For example, in the Apple world the term for a unit of uncompressed audio data (one or more samples) is "frame." In FLAC the same concept is "block."

Similarly, Apple says calls one or more compressed samples a "packet." In FLAC they call this a "frame."

See how they reuse the terms? The format documentation page at the FLAC home page uses these alternate definitions.
8:10:42 PM    comment []




I've also started taking a look at the CoreAudio examples that shipped with Panther and XCode. The example at /Developer/Examples/CoreAudio/AudioCodecs shows how to create a codec (compressor/decompressor). Even better, it shows how to wrap it all up in an interface that the Carbon Sound Manager can use.

Apparently QuickTime hasn't been revved yet to directly interface to CoreAudio, so any codecs created in CA aren't visible to QT. To remedy this, the wizards at Apple generated SMAC (I think this stands for Sound Manager Audio Codec). SMAC makes the CA codec appear to be a Sound Manager codec so that QT can see it and use it. Nice.
12:01:25 AM    comment []





Click here to visit the Radio UserLand website. © Copyright 2004 Chuck Remes.
Last update: 10/2/04; 11:47:27 AM.
This theme is based on the SoundWaves (blue) Manila theme.
February 2004
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29            
Jan   Mar