|
|
|
@@ -52,13 +52,51 @@
|
|
|
|
|
#define STK_LOWPASSFILTERTIMESLICE (0.0005)
|
|
|
|
|
|
|
|
|
|
#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (10)
|
|
|
|
|
#define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0.1)
|
|
|
|
|
#define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (1)
|
|
|
|
|
#define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING_AFTER_BUFFER_UNDERRUN (7.5)
|
|
|
|
|
#define STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION (2048)
|
|
|
|
|
#define STK_DEFAULT_READ_BUFFER_SIZE (64 * 1024)
|
|
|
|
|
#define STK_DEFAULT_PACKET_BUFFER_SIZE (2048)
|
|
|
|
|
|
|
|
|
|
#define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]];
|
|
|
|
|
|
|
|
|
|
static void PopulateOptionsWithDefault(STKAudioPlayerOptions* options)
|
|
|
|
|
{
|
|
|
|
|
if (options->bufferSizeInSeconds == 0)
|
|
|
|
|
{
|
|
|
|
|
options->bufferSizeInSeconds = STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->readBufferSize == 0)
|
|
|
|
|
{
|
|
|
|
|
options->readBufferSize = STK_DEFAULT_READ_BUFFER_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->secondsRequiredToStartPlaying == 0)
|
|
|
|
|
{
|
|
|
|
|
options->secondsRequiredToStartPlaying = MIN(STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING, options->bufferSizeInSeconds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->secondsRequiredToStartPlayingAfterBufferUnderun == 0)
|
|
|
|
|
{
|
|
|
|
|
options->secondsRequiredToStartPlayingAfterBufferUnderun = MIN(STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING_AFTER_BUFFER_UNDERRUN, options->bufferSizeInSeconds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define CHECK_STATUS_AND_RETURN(call) \
|
|
|
|
|
if ((status = (call))) \
|
|
|
|
|
{ \
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError]; \
|
|
|
|
|
return;\
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define CHECK_STATUS_AND_RETURN_VALUE(call, value) \
|
|
|
|
|
if ((status = (call))) \
|
|
|
|
|
{ \
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError]; \
|
|
|
|
|
return value;\
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
STKAudioPlayerInternalStateInitialised = 0,
|
|
|
|
@@ -112,6 +150,12 @@ STKAudioPlayerInternalState;
|
|
|
|
|
|
|
|
|
|
#pragma mark STKAudioPlayer
|
|
|
|
|
|
|
|
|
|
static AudioComponentDescription mixerDescription;
|
|
|
|
|
static AudioComponentDescription nbandUnitDescription;
|
|
|
|
|
static AudioComponentDescription outputUnitDescription;
|
|
|
|
|
static AudioComponentDescription convertUnitDescription;
|
|
|
|
|
static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
|
|
|
|
|
|
|
|
|
|
@interface STKAudioPlayer()
|
|
|
|
|
{
|
|
|
|
|
BOOL muted;
|
|
|
|
@@ -120,12 +164,28 @@ STKAudioPlayerInternalState;
|
|
|
|
|
int readBufferSize;
|
|
|
|
|
STKAudioPlayerInternalState internalState;
|
|
|
|
|
|
|
|
|
|
Float32 volume;
|
|
|
|
|
Float32 peakPowerDb[2];
|
|
|
|
|
Float32 averagePowerDb[2];
|
|
|
|
|
|
|
|
|
|
BOOL meteringEnabled;
|
|
|
|
|
STKAudioPlayerOptions options;
|
|
|
|
|
AudioComponentInstance audioUnit;
|
|
|
|
|
|
|
|
|
|
AUGraph audioGraph;
|
|
|
|
|
AUNode eqNode;
|
|
|
|
|
AUNode mixerNode;
|
|
|
|
|
AUNode outputNode;
|
|
|
|
|
|
|
|
|
|
AUNode eqInputNode;
|
|
|
|
|
AUNode eqOutputNode;
|
|
|
|
|
AUNode mixerInputNode;
|
|
|
|
|
AUNode mixerOutputNode;
|
|
|
|
|
|
|
|
|
|
AudioComponentInstance eqUnit;
|
|
|
|
|
AudioComponentInstance mixerUnit;
|
|
|
|
|
AudioComponentInstance outputUnit;
|
|
|
|
|
|
|
|
|
|
UInt32 eqBandCount;
|
|
|
|
|
|
|
|
|
|
UInt32 framesRequiredToStartPlaying;
|
|
|
|
|
UInt32 framesRequiredToPlayAfterRebuffering;
|
|
|
|
@@ -137,6 +197,7 @@ STKAudioPlayerInternalState;
|
|
|
|
|
NSMutableArray* bufferingQueue;
|
|
|
|
|
|
|
|
|
|
OSSpinLock pcmBufferSpinLock;
|
|
|
|
|
OSSpinLock internalStateLock;
|
|
|
|
|
volatile UInt32 pcmBufferTotalFrameCount;
|
|
|
|
|
volatile UInt32 pcmBufferFrameStartIndex;
|
|
|
|
|
volatile UInt32 pcmBufferUsedFrameCount;
|
|
|
|
@@ -146,7 +207,6 @@ STKAudioPlayerInternalState;
|
|
|
|
|
AudioBufferList pcmAudioBufferList;
|
|
|
|
|
AudioConverterRef audioConverterRef;
|
|
|
|
|
|
|
|
|
|
AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
|
|
|
|
|
AudioStreamBasicDescription audioConverterAudioStreamBasicDescription;
|
|
|
|
|
|
|
|
|
|
BOOL discontinuous;
|
|
|
|
@@ -200,6 +260,60 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
|
|
|
|
|
@implementation STKAudioPlayer
|
|
|
|
|
|
|
|
|
|
+(void) initialize
|
|
|
|
|
{
|
|
|
|
|
convertUnitDescription = (AudioComponentDescription)
|
|
|
|
|
{
|
|
|
|
|
.componentManufacturer = kAudioUnitManufacturer_Apple,
|
|
|
|
|
.componentType = kAudioUnitType_FormatConverter,
|
|
|
|
|
.componentSubType = kAudioUnitSubType_AUConverter,
|
|
|
|
|
.componentFlags = 0,
|
|
|
|
|
.componentFlagsMask = 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const int bytesPerSample = sizeof(AudioSampleType);
|
|
|
|
|
|
|
|
|
|
canonicalAudioStreamBasicDescription = (AudioStreamBasicDescription)
|
|
|
|
|
{
|
|
|
|
|
.mSampleRate = 44100.00,
|
|
|
|
|
.mFormatID = kAudioFormatLinearPCM,
|
|
|
|
|
.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked,
|
|
|
|
|
.mFramesPerPacket = 1,
|
|
|
|
|
.mChannelsPerFrame = 2,
|
|
|
|
|
.mBytesPerFrame = bytesPerSample * 2 /*channelsPerFrame*/,
|
|
|
|
|
.mBitsPerChannel = 8 * bytesPerSample,
|
|
|
|
|
.mBytesPerPacket = (bytesPerSample * 2)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
outputUnitDescription = (AudioComponentDescription)
|
|
|
|
|
{
|
|
|
|
|
.componentType = kAudioUnitType_Output,
|
|
|
|
|
#if TARGET_OS_IPHONE
|
|
|
|
|
.componentSubType = kAudioUnitSubType_RemoteIO,
|
|
|
|
|
#else
|
|
|
|
|
.componentSubType = kAudioUnitSubType_DefaultOutput,
|
|
|
|
|
#endif
|
|
|
|
|
.componentFlags = 0,
|
|
|
|
|
.componentFlagsMask = 0,
|
|
|
|
|
.componentManufacturer = kAudioUnitManufacturer_Apple
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mixerDescription = (AudioComponentDescription)
|
|
|
|
|
{
|
|
|
|
|
.componentType = kAudioUnitType_Mixer,
|
|
|
|
|
.componentSubType = kAudioUnitSubType_MultiChannelMixer,
|
|
|
|
|
.componentFlags = 0,
|
|
|
|
|
.componentFlagsMask = 0,
|
|
|
|
|
.componentManufacturer = kAudioUnitManufacturer_Apple
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
nbandUnitDescription = (AudioComponentDescription)
|
|
|
|
|
{
|
|
|
|
|
.componentType = kAudioUnitType_Effect,
|
|
|
|
|
.componentSubType = kAudioUnitSubType_NBandEQ,
|
|
|
|
|
.componentManufacturer=kAudioUnitManufacturer_Apple
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
-(STKAudioPlayerOptions) options
|
|
|
|
|
{
|
|
|
|
|
return options;
|
|
|
|
@@ -212,16 +326,14 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
|
|
|
|
|
-(void) setInternalState:(STKAudioPlayerInternalState)value
|
|
|
|
|
{
|
|
|
|
|
if (value == internalState)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internalState = value;
|
|
|
|
|
|
|
|
|
|
[self setInternalState:value ifInState:NULL];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void) setInternalState:(STKAudioPlayerInternalState)value ifInState:(BOOL(^)(STKAudioPlayerInternalState))ifInState
|
|
|
|
|
{
|
|
|
|
|
STKAudioPlayerState newState;
|
|
|
|
|
|
|
|
|
|
switch (internalState)
|
|
|
|
|
switch (value)
|
|
|
|
|
{
|
|
|
|
|
case STKAudioPlayerInternalStateInitialised:
|
|
|
|
|
newState = STKAudioPlayerStateReady;
|
|
|
|
@@ -257,17 +369,44 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OSSpinLockLock(&internalStateLock);
|
|
|
|
|
|
|
|
|
|
if (value == internalState)
|
|
|
|
|
{
|
|
|
|
|
OSSpinLockUnlock(&internalStateLock);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ifInState != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (!ifInState(self->internalState))
|
|
|
|
|
{
|
|
|
|
|
OSSpinLockUnlock(&internalStateLock);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internalState = value;
|
|
|
|
|
|
|
|
|
|
STKAudioPlayerState previousState = self.state;
|
|
|
|
|
|
|
|
|
|
if (newState != previousState)
|
|
|
|
|
{
|
|
|
|
|
self.state = newState;
|
|
|
|
|
|
|
|
|
|
OSSpinLockUnlock(&internalStateLock);
|
|
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
|
|
|
{
|
|
|
|
|
[self.delegate audioPlayer:self stateChanged:self.state previousState:previousState];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
OSSpinLockUnlock(&internalStateLock);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(STKAudioPlayerStopReason) stopReason
|
|
|
|
@@ -295,45 +434,33 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
|
|
|
|
|
-(id) init
|
|
|
|
|
{
|
|
|
|
|
return [self initWithReadBufferSize:STK_DEFAULT_READ_BUFFER_SIZE andOptions:STKAudioPlayerOptionNone];
|
|
|
|
|
return [self initWithOptions:(STKAudioPlayerOptions){}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(id) initWithOptions:(STKAudioPlayerOptions)optionsIn
|
|
|
|
|
{
|
|
|
|
|
return [self initWithReadBufferSize:STK_DEFAULT_READ_BUFFER_SIZE andOptions:optionsIn];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(id) initWithReadBufferSize:(int)readBufferSizeIn andOptions:(STKAudioPlayerOptions)optionsIn
|
|
|
|
|
{
|
|
|
|
|
if (self = [super init])
|
|
|
|
|
{
|
|
|
|
|
options = optionsIn;
|
|
|
|
|
|
|
|
|
|
const int bytesPerSample = sizeof(AudioSampleType);
|
|
|
|
|
|
|
|
|
|
canonicalAudioStreamBasicDescription.mSampleRate = 44100.00;
|
|
|
|
|
canonicalAudioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
|
|
|
|
|
canonicalAudioStreamBasicDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
|
|
|
|
canonicalAudioStreamBasicDescription.mFramesPerPacket = 1;
|
|
|
|
|
canonicalAudioStreamBasicDescription.mChannelsPerFrame = 2;
|
|
|
|
|
canonicalAudioStreamBasicDescription.mBytesPerFrame = bytesPerSample * canonicalAudioStreamBasicDescription.mChannelsPerFrame;
|
|
|
|
|
canonicalAudioStreamBasicDescription.mBitsPerChannel = 8 * bytesPerSample;
|
|
|
|
|
canonicalAudioStreamBasicDescription.mBytesPerPacket = canonicalAudioStreamBasicDescription.mBytesPerFrame * canonicalAudioStreamBasicDescription.mFramesPerPacket;
|
|
|
|
|
self->volume = 1.0;
|
|
|
|
|
|
|
|
|
|
PopulateOptionsWithDefault(&options);
|
|
|
|
|
|
|
|
|
|
framesRequiredToStartPlaying = canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING;
|
|
|
|
|
framesRequiredToPlayAfterRebuffering = canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS;
|
|
|
|
|
framesRequiredToStartPlaying = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlaying;
|
|
|
|
|
framesRequiredToPlayAfterRebuffering = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlayingAfterBufferUnderun;
|
|
|
|
|
|
|
|
|
|
pcmAudioBuffer = &pcmAudioBufferList.mBuffers[0];
|
|
|
|
|
|
|
|
|
|
pcmAudioBufferList.mNumberBuffers = 1;
|
|
|
|
|
pcmAudioBufferList.mBuffers[0].mDataByteSize = (canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS) * canonicalAudioStreamBasicDescription.mBytesPerFrame;
|
|
|
|
|
pcmAudioBufferList.mBuffers[0].mDataByteSize = (canonicalAudioStreamBasicDescription.mSampleRate * options.bufferSizeInSeconds) * canonicalAudioStreamBasicDescription.mBytesPerFrame;
|
|
|
|
|
pcmAudioBufferList.mBuffers[0].mData = (void*)calloc(pcmAudioBuffer->mDataByteSize, 1);
|
|
|
|
|
pcmAudioBufferList.mBuffers[0].mNumberChannels = 2;
|
|
|
|
|
|
|
|
|
|
pcmBufferFrameSizeInBytes = canonicalAudioStreamBasicDescription.mBytesPerFrame;
|
|
|
|
|
pcmBufferTotalFrameCount = pcmAudioBuffer->mDataByteSize / pcmBufferFrameSizeInBytes;
|
|
|
|
|
|
|
|
|
|
readBufferSize = readBufferSizeIn;
|
|
|
|
|
readBufferSize = options.readBufferSize;
|
|
|
|
|
readBuffer = calloc(sizeof(UInt8), readBufferSize);
|
|
|
|
|
|
|
|
|
|
pthread_mutexattr_t attr;
|
|
|
|
@@ -355,7 +482,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
bufferingQueue = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
|
|
[self resetPcmBuffers];
|
|
|
|
|
[self createAudioUnit];
|
|
|
|
|
[self createAudioGraph];
|
|
|
|
|
[self createPlaybackThread];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -388,11 +515,13 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
AudioConverterDispose(audioConverterRef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (audioUnit)
|
|
|
|
|
if (outputUnit)
|
|
|
|
|
{
|
|
|
|
|
AudioComponentInstanceDispose(audioUnit);
|
|
|
|
|
AudioComponentInstanceDispose(outputUnit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AUGraphClose(audioGraph);
|
|
|
|
|
|
|
|
|
|
pthread_mutex_destroy(&playerMutex);
|
|
|
|
|
pthread_mutex_destroy(&mainThreadSyncCallMutex);
|
|
|
|
|
pthread_cond_destroy(&playerThreadReadyCondition);
|
|
|
|
@@ -732,7 +861,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
|
|
|
|
|
-(Float64) currentTimeInFrames
|
|
|
|
|
{
|
|
|
|
|
if (audioUnit == nil)
|
|
|
|
|
if (audioGraph == nil)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@@ -932,7 +1061,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:entry];
|
|
|
|
|
[self startAudioUnit];
|
|
|
|
|
|
|
|
|
|
[self startAudioGraph];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@@ -1111,7 +1241,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
[currentlyReadingEntry.dataSource unregisterForEvents];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self->options & STKAudioPlayerOptionFlushQueueOnSeek)
|
|
|
|
|
if (self->options.flushQueueOnSeek)
|
|
|
|
|
{
|
|
|
|
|
self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek;
|
|
|
|
|
[self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES clearQueue:YES];
|
|
|
|
@@ -1286,7 +1416,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
|
|
|
|
|
self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek;
|
|
|
|
|
|
|
|
|
|
if (audioUnit)
|
|
|
|
|
if (audioGraph)
|
|
|
|
|
{
|
|
|
|
|
[self resetPcmBuffers];
|
|
|
|
|
}
|
|
|
|
@@ -1430,9 +1560,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
self.stateBeforePaused = self.internalState;
|
|
|
|
|
self.internalState = STKAudioPlayerInternalStatePaused;
|
|
|
|
|
|
|
|
|
|
if (audioUnit)
|
|
|
|
|
if (audioGraph)
|
|
|
|
|
{
|
|
|
|
|
error = AudioOutputUnitStop(audioUnit);
|
|
|
|
|
error = AUGraphStop(audioGraph);
|
|
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
|
{
|
|
|
|
@@ -1465,9 +1595,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
|
|
|
|
[self resetPcmBuffers];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (audioUnit != nil)
|
|
|
|
|
if (audioGraph != nil)
|
|
|
|
|
{
|
|
|
|
|
error = AudioOutputUnitStart(audioUnit);
|
|
|
|
|
error = AUGraphStart(audioGraph);
|
|
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
|
{
|
|
|
|
@@ -1722,88 +1852,213 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void) createAudioUnit
|
|
|
|
|
-(void) createOutputUnit
|
|
|
|
|
{
|
|
|
|
|
pthread_mutex_lock(&playerMutex);
|
|
|
|
|
|
|
|
|
|
OSStatus status;
|
|
|
|
|
AudioComponentDescription desc;
|
|
|
|
|
|
|
|
|
|
desc.componentType = kAudioUnitType_Output;
|
|
|
|
|
#if TARGET_OS_IPHONE
|
|
|
|
|
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
|
|
|
|
#else
|
|
|
|
|
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
|
|
|
|
#endif
|
|
|
|
|
desc.componentFlags = 0;
|
|
|
|
|
desc.componentFlagsMask = 0;
|
|
|
|
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
|
|
|
|
|
|
|
|
AudioComponent component = AudioComponentFindNext(NULL, &desc);
|
|
|
|
|
|
|
|
|
|
status = AudioComponentInstanceNew(component, &audioUnit);
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError];
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OSStatus status;
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &outputUnitDescription, &outputNode));
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, outputNode, &outputUnitDescription, &outputUnit));
|
|
|
|
|
|
|
|
|
|
#if TARGET_OS_IPHONE
|
|
|
|
|
UInt32 flag = 1;
|
|
|
|
|
status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError];
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription));
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError];
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AURenderCallbackStruct callbackStruct;
|
|
|
|
|
|
|
|
|
|
callbackStruct.inputProc = OutputRenderCallback;
|
|
|
|
|
callbackStruct.inputProcRefCon = (__bridge void*)self;
|
|
|
|
|
|
|
|
|
|
status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callbackStruct, sizeof(callbackStruct));
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError];
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = AudioUnitInitialize(audioUnit);
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError];
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&playerMutex);
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(BOOL) startAudioUnit
|
|
|
|
|
-(void) createMixerUnit
|
|
|
|
|
{
|
|
|
|
|
OSStatus status;
|
|
|
|
|
|
|
|
|
|
if (!self.options.enableVolumeMixer)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &mixerDescription, &mixerNode));
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, mixerNode, &mixerDescription, &mixerUnit));
|
|
|
|
|
|
|
|
|
|
UInt32 busCount = 1;
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(busCount)));
|
|
|
|
|
|
|
|
|
|
Float64 graphSampleRate = 44100.0;
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &graphSampleRate, sizeof(graphSampleRate)));
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, 0, 1.0, 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void) createEqUnit
|
|
|
|
|
{
|
|
|
|
|
#if TARGET_OS_MAC && MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
|
|
|
|
|
return;
|
|
|
|
|
#else
|
|
|
|
|
OSStatus status;
|
|
|
|
|
|
|
|
|
|
if (self->options.equalizerBandFrequencies[0] == 0)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &nbandUnitDescription, &eqNode));
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, eqNode, NULL, &eqUnit));
|
|
|
|
|
|
|
|
|
|
while (self->options.equalizerBandFrequencies[eqBandCount] != 0)
|
|
|
|
|
{
|
|
|
|
|
eqBandCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(eqUnit, kAUNBandEQProperty_NumberOfBands, kAudioUnitScope_Global, 0, &eqBandCount, sizeof(eqBandCount)));
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < eqBandCount; i++)
|
|
|
|
|
{
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_Frequency + i, kAudioUnitScope_Global, 0, (AudioUnitParameterValue)self->options.equalizerBandFrequencies[i], 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < eqBandCount; i++)
|
|
|
|
|
{
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_BypassBand + i, kAudioUnitScope_Global, 0, (AudioUnitParameterValue)0, 0));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void) setGain:(float)gain forEqualizerBand:(int)bandIndex
|
|
|
|
|
{
|
|
|
|
|
if (!eqUnit)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OSStatus status;
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_Gain + bandIndex, kAudioUnitScope_Global, 0, gain, 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(AUNode) createConverterNode:(AudioStreamBasicDescription)srcFormat desFormat:(AudioStreamBasicDescription)desFormat
|
|
|
|
|
{
|
|
|
|
|
OSStatus status;
|
|
|
|
|
AUNode convertNode;
|
|
|
|
|
AudioComponentInstance convertUnit;
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN_VALUE(AUGraphAddNode(audioGraph, &convertUnitDescription, &convertNode), 0);
|
|
|
|
|
CHECK_STATUS_AND_RETURN_VALUE(status = AUGraphNodeInfo(audioGraph, convertNode, &mixerDescription, &convertUnit), 0);
|
|
|
|
|
CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &srcFormat, sizeof(srcFormat)), 0);
|
|
|
|
|
CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &desFormat, sizeof(desFormat)), 0);
|
|
|
|
|
|
|
|
|
|
return convertNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void) connectNodes:(AUNode)srcNode desNode:(AUNode)desNode srcUnit:(AudioComponentInstance)srcUnit desUnit:(AudioComponentInstance)desUnit
|
|
|
|
|
{
|
|
|
|
|
OSStatus status;
|
|
|
|
|
AudioStreamBasicDescription srcFormat, desFormat;
|
|
|
|
|
UInt32 size = sizeof(AudioStreamBasicDescription);
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitGetProperty(srcUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &srcFormat, &size));
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitGetProperty(desUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &desFormat, &size));
|
|
|
|
|
|
|
|
|
|
status = AudioUnitSetProperty(desUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &srcFormat, sizeof(srcFormat));
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
AUNode convertNode = [self createConverterNode:srcFormat desFormat:desFormat];
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, convertNode, 0, mixerNode, 0));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, srcNode, 0, desNode, 0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void) setOutputCallbackForFirstNode:(AUNode)firstNode firstUnit:(AudioComponentInstance)firstUnit
|
|
|
|
|
{
|
|
|
|
|
OSStatus status;
|
|
|
|
|
AURenderCallbackStruct callbackStruct;
|
|
|
|
|
|
|
|
|
|
callbackStruct.inputProc = OutputRenderCallback;
|
|
|
|
|
callbackStruct.inputProcRefCon = (__bridge void*)self;
|
|
|
|
|
|
|
|
|
|
status = AudioUnitSetProperty(firstUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription));
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
AudioStreamBasicDescription format;
|
|
|
|
|
UInt32 size = sizeof(format);
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AudioUnitGetProperty(firstUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, &size));
|
|
|
|
|
|
|
|
|
|
AUNode converterNode = [self createConverterNode:canonicalAudioStreamBasicDescription desFormat:format];
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, converterNode, 0, &callbackStruct));
|
|
|
|
|
|
|
|
|
|
status = AUGraphConnectNodeInput(audioGraph, converterNode, 0, firstNode, 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, firstNode, 0, &callbackStruct));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void) createAudioGraph
|
|
|
|
|
{
|
|
|
|
|
OSStatus status;
|
|
|
|
|
NSMutableArray* nodes = [[NSMutableArray alloc] init];
|
|
|
|
|
NSMutableArray* units = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(NewAUGraph(&audioGraph));
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphOpen(audioGraph));
|
|
|
|
|
|
|
|
|
|
[self createEqUnit];
|
|
|
|
|
[self createMixerUnit];
|
|
|
|
|
[self createOutputUnit];
|
|
|
|
|
|
|
|
|
|
if (eqNode)
|
|
|
|
|
{
|
|
|
|
|
[nodes addObject:@(eqNode)];
|
|
|
|
|
[units addObject:[NSValue valueWithPointer:eqUnit]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mixerNode)
|
|
|
|
|
{
|
|
|
|
|
[nodes addObject:@(mixerNode)];
|
|
|
|
|
[units addObject:[NSValue valueWithPointer:mixerUnit]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (outputNode)
|
|
|
|
|
{
|
|
|
|
|
[nodes addObject:@(outputNode)];
|
|
|
|
|
[units addObject:[NSValue valueWithPointer:outputUnit]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self setOutputCallbackForFirstNode:(AUNode)[[nodes objectAtIndex:0] intValue] firstUnit:(AudioComponentInstance)[[units objectAtIndex:0] pointerValue]];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < nodes.count - 1; i++)
|
|
|
|
|
{
|
|
|
|
|
AUNode srcNode = [[nodes objectAtIndex:i] intValue];
|
|
|
|
|
AUNode desNode = [[nodes objectAtIndex:i + 1] intValue];
|
|
|
|
|
AudioComponentInstance srcUnit = (AudioComponentInstance)[[units objectAtIndex:i] pointerValue];
|
|
|
|
|
AudioComponentInstance desUnit = (AudioComponentInstance)[[units objectAtIndex:i + 1] pointerValue];
|
|
|
|
|
|
|
|
|
|
[self connectNodes:srcNode desNode:desNode srcUnit:srcUnit desUnit:desUnit];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_STATUS_AND_RETURN(AUGraphInitialize(audioGraph));
|
|
|
|
|
|
|
|
|
|
self.volume = self->volume;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(BOOL) startAudioGraph
|
|
|
|
|
{
|
|
|
|
|
OSStatus status;
|
|
|
|
|
|
|
|
|
|
[self resetPcmBuffers];
|
|
|
|
|
|
|
|
|
|
status = AudioOutputUnitStart(audioUnit);
|
|
|
|
|
|
|
|
|
|
Boolean isRunning;
|
|
|
|
|
|
|
|
|
|
status = AUGraphIsRunning(audioGraph, &isRunning);
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
@@ -1812,6 +2067,20 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isRunning)
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = AUGraphStart(audioGraph);
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError];
|
|
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1819,7 +2088,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
|
|
|
|
{
|
|
|
|
|
OSStatus status;
|
|
|
|
|
|
|
|
|
|
if (!audioUnit)
|
|
|
|
|
if (!audioGraph)
|
|
|
|
|
{
|
|
|
|
|
stopReason = stopReasonIn;
|
|
|
|
|
self.internalState = STKAudioPlayerInternalStateStopped;
|
|
|
|
@@ -1827,17 +2096,28 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = AudioOutputUnitStop(audioUnit);
|
|
|
|
|
|
|
|
|
|
[self resetPcmBuffers];
|
|
|
|
|
Boolean isRunning;
|
|
|
|
|
|
|
|
|
|
status = AUGraphIsRunning(audioGraph, &isRunning);
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else if (!isRunning)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = AUGraphStop(audioGraph);
|
|
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
|
{
|
|
|
|
|
[self unexpectedError:STKAudioPlayerErrorAudioSystemError];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self resetPcmBuffers];
|
|
|
|
|
|
|
|
|
|
stopReason = stopReasonIn;
|
|
|
|
|
self.internalState = STKAudioPlayerInternalStateStopped;
|
|
|
|
|
}
|
|
|
|
@@ -2126,19 +2406,11 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
|
|
|
|
UInt32 frameSizeInBytes = audioPlayer->pcmBufferFrameSizeInBytes;
|
|
|
|
|
UInt32 used = audioPlayer->pcmBufferUsedFrameCount;
|
|
|
|
|
UInt32 start = audioPlayer->pcmBufferFrameStartIndex;
|
|
|
|
|
STKAudioPlayerInternalState state = audioPlayer->internalState;
|
|
|
|
|
UInt32 end = (audioPlayer->pcmBufferFrameStartIndex + audioPlayer->pcmBufferUsedFrameCount) % audioPlayer->pcmBufferTotalFrameCount;
|
|
|
|
|
BOOL signal = audioPlayer->waiting && used < audioPlayer->pcmBufferTotalFrameCount / 2;
|
|
|
|
|
NSArray* frameFilters = audioPlayer->frameFilters;
|
|
|
|
|
|
|
|
|
|
STKAudioPlayerInternalState state = audioPlayer.internalState;
|
|
|
|
|
|
|
|
|
|
if (state == STKAudioPlayerInternalStatePendingNext)
|
|
|
|
|
{
|
|
|
|
|
OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (entry)
|
|
|
|
|
{
|
|
|
|
|
if (state == STKAudioPlayerInternalStateWaitingForData)
|
|
|
|
@@ -2158,9 +2430,23 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
|
|
|
|
}
|
|
|
|
|
else if (state == STKAudioPlayerInternalStateRebuffering)
|
|
|
|
|
{
|
|
|
|
|
int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying;
|
|
|
|
|
int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToPlayAfterRebuffering;
|
|
|
|
|
|
|
|
|
|
if (audioPlayer->currentlyPlayingEntry->lastFrameQueued >= 0)
|
|
|
|
|
if (entry->lastFrameQueued >= 0)
|
|
|
|
|
{
|
|
|
|
|
framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued - entry->framesQueued);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (used < framesRequiredToStartPlaying)
|
|
|
|
|
{
|
|
|
|
|
waitForBuffer = YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (state == STKAudioPlayerInternalStateWaitingForDataAfterSeek)
|
|
|
|
|
{
|
|
|
|
|
int64_t framesRequiredToStartPlaying = inNumberFrames;
|
|
|
|
|
|
|
|
|
|
if (entry->lastFrameQueued >= 0)
|
|
|
|
|
{
|
|
|
|
|
framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued - entry->framesQueued);
|
|
|
|
|
}
|
|
|
|
@@ -2176,7 +2462,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
|
|
|
|
|
|
|
|
|
UInt32 totalFramesCopied = 0;
|
|
|
|
|
|
|
|
|
|
if (used > 0 && !waitForBuffer && entry != nil)
|
|
|
|
|
if (used > 0 && !waitForBuffer && entry != nil && ((state & STKAudioPlayerInternalStateRunning) && state != STKAudioPlayerInternalStatePaused))
|
|
|
|
|
{
|
|
|
|
|
if (state == STKAudioPlayerInternalStateWaitingForData)
|
|
|
|
|
{
|
|
|
|
@@ -2254,7 +2540,10 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
|
|
|
|
OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
audioPlayer.internalState = STKAudioPlayerInternalStatePlaying;
|
|
|
|
|
[audioPlayer setInternalState:STKAudioPlayerInternalStatePlaying ifInState:^BOOL(STKAudioPlayerInternalState state)
|
|
|
|
|
{
|
|
|
|
|
return (state & STKAudioPlayerInternalStateRunning) && state != STKAudioPlayerInternalStatePaused;
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (totalFramesCopied < inNumberFrames)
|
|
|
|
@@ -2267,14 +2556,17 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
|
|
|
|
{
|
|
|
|
|
// Buffering
|
|
|
|
|
|
|
|
|
|
audioPlayer.internalState = STKAudioPlayerInternalStateRebuffering;
|
|
|
|
|
[audioPlayer setInternalState:STKAudioPlayerInternalStateRebuffering ifInState:^BOOL(STKAudioPlayerInternalState state)
|
|
|
|
|
{
|
|
|
|
|
return (state & STKAudioPlayerInternalStateRunning) && state != STKAudioPlayerInternalStatePaused;
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (frameFilters)
|
|
|
|
|
{
|
|
|
|
|
NSUInteger count = frameFilters.count;
|
|
|
|
|
AudioStreamBasicDescription asbd = audioPlayer->canonicalAudioStreamBasicDescription;
|
|
|
|
|
AudioStreamBasicDescription asbd = canonicalAudioStreamBasicDescription;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
@@ -2636,4 +2928,32 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
|
|
|
|
pthread_mutex_unlock(&self->playerMutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark Volume
|
|
|
|
|
|
|
|
|
|
-(void) setVolume:(Float32)value;
|
|
|
|
|
{
|
|
|
|
|
self->volume = value;
|
|
|
|
|
|
|
|
|
|
#if (TARGET_OS_IPHONE)
|
|
|
|
|
if (self->mixerNode)
|
|
|
|
|
{
|
|
|
|
|
AudioUnitSetParameter(self->mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, self->volume, 0);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
if (self->mixerNode)
|
|
|
|
|
{
|
|
|
|
|
AudioUnitSetParameter(self->mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, self->volume, 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
AudioUnitSetParameter(outputUnit, kHALOutputParam_Volume, kAudioUnitScope_Output, kOutputBus, self->volume, 0);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(Float32) volume
|
|
|
|
|
{
|
|
|
|
|
return self->volume;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|