I have developed an audio play app in iOS with Objective-c and C language, it can play audio in most situations. But when I switch the app into background and then switch it back,it will not play the audio. Another problem is when I open it with earphone, if I take off the earphone, it will also stop play the audio.
The init function is:
- (instancetype)init {
self = [super init];
if (self) {
sysnLock = [[NSLock alloc] init];
if (_audioDescription.mSampleRate <= 0) {
_audioDescription.mSampleRate = _sampleRates;
_audioDescription.mFormatID = kAudioFormatLinearPCM;
_audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
_audioDescription.mChannelsPerFrame = _channels;
_audioDescription.mFramesPerPacket = 1;
_audioDescription.mBitsPerChannel = 16;
_audioDescription.mBytesPerFrame = (_audioDescription.mBitsPerChannel / 8) * _audioDescription.mChannelsPerFrame;
_audioDescription.mBytesPerPacket = _audioDescription.mBytesPerFrame * _audioDescription.mFramesPerPacket;
}
AudioQueueNewOutput(&_audioDescription, AudioPlayerCallback, (__bridge void *_Nullable)(self), nil, 0, 0,
&audioQueue);
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 100.0);
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
audioQueueBufferUsed[i] = false;
osState = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);
}
osState = AudioQueueStart(audioQueue, NULL);
}
return self;
}
In which AudioPlayerCallback
is a callback function to reset the audioBufferUsed.
And the audio play function is:
- (void)startPlay:(AudioData *)audioData {
uint8_t *c_data = get_data(audioData);
size_t c_data_size = get_data_size(audioData);
[sysnLock lock];
int i = 0;
while (true) {
if (!audioQueueBufferUsed[i]) {
audioQueueBufferUsed[i] = true;
break;
} else {
i++;
if (i >= QUEUE_BUFFER_SIZE) {
i = 0;
}
}
}
audioQueueBuffers[i]->mAudioDataByteSize = (unsigned int)c_data_size;
memcpy(audioQueueBuffers[i]->mAudioData, c_data, c_data_size);
AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffers[i], 0, NULL);
OSStatus status = AudioQueueStart(audioQueue, NULL);
[sysnLock unlock];
}
In which get_data
and get_data_size
can get the uint_8 data and its size to play.
You will setup NotificationCenter
to observe the following keys
AVAudioSessionInterruptionNotification
and AVAudioSessionRouteChangeNotification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interruption:) name:AVAudioSessionInterruptionNotification
object:nil];
- (void)interruption:(NSNotification *)notiz {
// handle stuff when audio interrupts
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
- (void)routeChanged:(NSNotification *)notiz {
// handle stuff when route is changed, aka headphone jack in/out
}
In classic coding style you don't forget to remove the observer in your dealloc method.
[[NSNotificationCenter defaultCenter] removeObserver:self name:... object:nil];
This will allow you to watch when your disturbing events happen and act accordingly with re-creation of your AudioQueue, Start, Stop, Buffer if needed.
Another way is to observe changes of propertys of the AudioQueueBuffer API with...
AudioQueueAddPropertyListener(AudioQueueRef inAQ, AudioQueuePropertyID inID, AudioQueuePropertyListenerProc inProc, void * inUserData);