159 lines
4.5 KiB
Matlab
159 lines
4.5 KiB
Matlab
|
//
|
||
|
// 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
|