cog/OutputCoreAudio.m

159 lines
4.5 KiB
Objective-C

//
// OutputCoreAudio.m
// Cog
//
// Created by Zaphod Beeblebrox on 8/2/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "OutputCoreAudio.h"
@implementation OutputCoreAudio
static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
Sound *sound = (Sound *)inRefCon;
OSStatus err = noErr;
int amountAvailable;
int amountToRead;
void *readPointer;
[sound->readLock lock];
amountAvailable = [sound->readRingBuffer lengthAvailableToReadReturningPointer:&readPointer];
if (sound->playbackStatus == kCogStatusEndOfFile && amountAvailable == 0)
{
DBLog(@"FILE CHANGED!!!!!");
[sound sendPortMessage:kCogFileChangedMessage];
sound->readRingBuffer = [sound oppositeBuffer:sound->readRingBuffer];
[sound setPlaybackStatus:kCogStatusPlaying];
sound->currentPosition = 0;
double time = [sound calculateTime:sound->totalLength];
int bitrate = [sound->soundFile bitRate];
[sound sendPortMessage:kCogLengthUpdateMessage withData:&time ofSize:(sizeof(double))];
[sound sendPortMessage:kCogBitrateUpdateMessage withData:&bitrate ofSize:(sizeof(int))];
}
if (sound->playbackStatus == kCogStatusEndOfPlaylist && amountAvailable == 0)
{
//Stop playback
[sound setPlaybackStatus:kCogStatusStopped];
// return err;
}
if (amountAvailable < ([sound->readRingBuffer bufferLength] - BUFFER_WRITE_CHUNK))
{
// DBLog(@"AVAILABLE: %i", amountAvailable);
[sound fireFillTimer];
}
if (amountAvailable > inNumberFrames*sound->deviceFormat.mBytesPerPacket)
amountToRead = inNumberFrames*sound->deviceFormat.mBytesPerPacket;
else
amountToRead = amountAvailable;
memcpy(ioData->mBuffers[0].mData, readPointer, amountToRead);
ioData->mBuffers[0].mDataByteSize = amountToRead;
[sound->readRingBuffer didReadLength:amountToRead];
sound->currentPosition += amountToRead;
[sound->readLock unlock];
return err;
}
- (void)setup
{
ComponentDescription desc;
OSStatus err;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
Component comp = FindNextComponent(NULL, &desc); //Finds an component that meets the desc spec's
if (comp == NULL)
return NO;
err = OpenAComponent(comp, &outputUnit); //gains access to the services provided by the component
if (err)
return NO;
// Initialize AudioUnit
err = AudioUnitInitialize(outputUnit);
if (err != noErr)
return NO;
UInt32 size = sizeof (AudioStreamBasicDescription);
Boolean outWritable;
//Gets the size of the Stream Format Property and if it is writable
AudioUnitGetPropertyInfo(outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&size,
&outWritable);
//Get the current stream format of the output
err = AudioUnitGetProperty (outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&deviceFormat,
&size);
if (err != noErr)
return NO;
// change output format...
///Seems some 3rd party devices return incorrect stuff...or I just don't like noninterleaved data.
deviceFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
deviceFormat.mBytesPerFrame = deviceFormat.mChannelsPerFrame*(deviceFormat.mBitsPerChannel/8);
deviceFormat.mBytesPerPacket = deviceFormat.mBytesPerFrame * deviceFormat.mFramesPerPacket;
// DBLog(@"stuff: %i %i %i %i", deviceFormat.mBitsPerChannel, deviceFormat.mBytesPerFrame, deviceFormat.mBytesPerPacket, deviceFormat.mFramesPerPacket);
err = AudioUnitSetProperty (outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&deviceFormat,
size);
//Set the stream format of the output to match the input
err = AudioUnitSetProperty (outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&deviceFormat,
size);
//setup render callbacks
renderCallback.inputProc = Sound_Renderer;
renderCallback.inputProcRefCon = self;
AudioUnitSetProperty(outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(AURenderCallbackStruct));
// DBLog(@"Audio output successfully initialized");
return (err == noErr);
}
- (void)start
{
AudioOutputUnitStart(outputUnit);
}
- (void)stop
{
if (outputUnit)
AudioOutputUnitStop(outputUnit);
}
@end