- Audioqueue Introduction
- Audiostreamer description
- Audioqueue detailed
- Audioqueue Working principle
- Audioqueue Main interface
- Audioqueuenewoutput
- Audioqueueallocatebuffer
- Audioqueueenqueuebuffer
- Audioqueuestart Pause Stop Flush Reset Dispose
- Audioqueuefreebuffer
- Audioqueuegetproperty Audioqueuesetproperty
- Audio playback Localaudioplayer
- Initialization of the player
- Play Audio
- Localaudioplayer related properties
- Read and start parsing audio
- Parsing Audio Information
- Kaudiofilestreamproperty_dataformat
- Kaudiofilestreamproperty_fileformat
- Kaudiofilestreamproperty_audiodatabytecount
- Kaudiofilestreamproperty_bitrate
- Kaudiofilestreamproperty_dataoffset
- Kaudiofilestreamproperty_audiodatapacketcount
- Kaudiofilestreamproperty_readytoproducepackets
- Parsing Audio Frames
- Play audio data
- Clean up Related resources
- End
iOS to play local music, there are many ways, such as Avaudioplayer, these can be very good, some people wonder, why go back to the second, using more complex audioqueue to play local music? Please keep looking down.
Audioqueue Introduction
Audioqueue, that's what Apple's developer documentation says.
"Audio Queue Services provides a straightforward, low overhead way to record and play audio in iOS and Mac OS X."
Audioqueue Official documents
The Audioqueue service provides a direct, low-overhead way to record and play music on iOS and Mac OS x.
The advantage of using Audioqueue to play music is that the overhead is very small and support streaming (side-to-bottom broadcast), but the disadvantage is that the development is difficult, so there is network Audio library Audiostreamer, there are many audioqueue on the internet, but there are examples of code, it is very little , just as the company project has audio requirements, although the project is not the use of my own Write audio playback function, but afterwards still want to study, this in my opinion is more magical and more interesting audioqueue.
Audiostreamer description
A more famous streaming audio player on iOS is Audiostreamer, which uses audioqueue, but the audio library does not support local music playback, and I feel strange why the author does not support it. And in the use of the process, I found that the library is still a bit of a problem, although I do not know much about audio knowledge, and can not be comparable with the master, but I also hope that through their own study, the final completion of a similar Audiostreamer network music library, is perhaps just a vision, In the end, whether or not they have that ability, at least I have tried, but the work is relatively busy recently, coupled with their lack of knowledge, I do not know when to achieve. This time first to make up Audiostreamer no support, use Audioqueue play local music.
Audioqueue detailed Audioqueue Working principle
I truncated the following image from Apple's Official document:
This diagram is a good illustration of how Audioqueue works, as explained below:
1. The user calls the appropriate method, reads the audio data from the hard disk into the audioqueue buffer, and feeds the buffer into the audio queue.
2. User app through the interface provided by Audioqueue, tell the speaker device, the buffer is already have data, can be taken to play.
3. When the audio data in a buffer is played, Audioqueue tells the user that there is currently an empty buffer that can be used to populate the data for you.
4. Repeat the above steps until the data has finished playing.
Here, there must be a lot of students found that Audioqueue is actually the producer-consumer model of the typical application.
Audioqueue Main interface Audioqueuenewoutput
OSStatus AudioQueueNewOutput(const AudioStreamBasicDescription *ininCallbackProc, void *ininininFlags, AudioQueueRef _Nullable *outAQ);
This method is used to create a audioqueue for outputting audio
The parameters and return instructions are as follows:
1. Informat: This parameter indicates the data format of the audio that will be played
2. Incallbackproc: This callback is used to notify the user when Audioqueue has finished using a buffer, and the user can continue to populate the audio data
3. inuserdata: Data pointer passed by the user for passing to the callback function
4. Incallbackrunloop: Indicates which runloop the callback event occurred in, and if NULL is passed, the callback event is executed on the thread where the Audioqueue is located, and in general, NULL is passed.
5. Incallbackrunloopmode: Indicates the runloop pattern of the callback event, passing null equivalent to kcfrunloopcommonmodes, typically passing null
6. Outaq: The reference instance of the Audioqueue,
Returns Osstatus, if the value is Noerr, indicates that there is no error, Audioqueue created successfully.
Audioqueueallocatebuffer
ininBufferByteSize, AudioQueueBufferRef _Nullable *outBuffer);
The function of this method is to open up space for the buffer that holds the audio data.
The parameters and return instructions are as follows:
1. Inaq: reference instance of Audioqueue
2. inbufferbytesize: The size of the buffer that needs to be opened
3. Outbuffer: A reference instance of the open buffer
Returns Osstatus if the value is Noerr, which indicates a successful buffer opening.
Audioqueueenqueuebuffer
inininNumPacketDescs, const AudioStreamPacketDescription *inPacketDescs);
This method is used to queue audioqueuebuffer that have already populated the data to Audioqueue
The parameters and return instructions are as follows:
1. Inaq: reference instance of Audioqueue
2. inbuffer: A buffer instance that needs to be queued
3. Innumpacketdescs: How many frames of audio data exist in the buffer
4. Inpacketdescs: Information about each frame in the buffer, the user needs to indicate the offset value of each frame in the buffer, and the field Mstartoffset to specify
Returns Osstatus if the value is Noerr, indicating that the buffer has been successfully queued. Wait for playback
Audioqueuestart Pause Stop Flush Reset Dispose
inAQ, const AudioTimeStamp *ininininininininImmediate);
As the name implies, the first three methods are used for audio playback, pause and stop. The latter two methods are used to clean and reset the audio queue at last, cleaning to ensure that the data in the queue is fully output. Audioqueudispose is used to clean up audioqueue resources.
The parameters and return instructions are as follows:
1. Inaq: reference instance of Audioqueue
2. instarttime: Indicates the time to start playing audio, and if you want to start now, pass NULL
3. Inimmediate: Indicates whether to stop audio playback immediately, if so, pass True
Returns a osstatus indicating whether the related operation was executed successfully.
Audioqueuefreebuffer
ininBuffer);
This method is used when the scavenging buffer is released at the end of playback
Audioqueuegetproperty Audioqueuesetproperty
ininininID, const void *ininDataSize);
This get/set method, which is used to set the related properties for getting audioqueue, see the instructions in the AudioQueue.h header file.
Audio Playback (Localaudioplayer)
Use Audioqueue play music, generally need to cooperate with Audiofilestream, Audiofilestream is responsible for parsing audio data, Audioqueue is responsible for playing the audio data resolved to.
This time, only the most basic local audio playback function is designed to lay the groundwork for the future, not to deal with any related state (such as pause, stop, SEEK), errors, etc. Playback is similar to streaming, except that audio data originates from a local file rather than a network, and takes several steps:
1. Continuously read part of the data from the file until the end of all data read
2. Submit the data read in the file to Audiofilestream for data parsing
3. Create Audioqueue when data is put into Audioqueuebuffer
4. Place the buffer in the Audioqueue to start playing the audio
5. Play audio end, clean up related resources
Initialization of the player
The init of the player is used primarily to specify the audio file to play, as follows:
The read file operation, using the Nsfilehandle class, Audioinuselock, is a nslock* type that is used to mark when Audioqueue notifies us that an empty buffer can be used.
We initialize the player when the user taps the play button and call the Play method to play
Play Audio
Playing audio is divided into the following steps:
1. Read and start parsing audio
2. Parsing Audio Information
3. Parsing Audio Frames
4. Play Audio data
5. Cleanup of related resources
Let's first define a few macros to specify the size of some buffers
#define kNumberOfBuffers 3 //AudioQueueBuffer数量,一般指明为3#define kAQBufSize 128 * 1024 //每个AudioQueueBuffer的大小#define kAudioFileBufferSize 2048 //文件读取数据的缓冲区大小#define kMaxPacketDesc 512 //最大的AudioStreamPacketDescription个数
Localaudioplayer related properties
The properties defined in Localaudioplayer are as follows:
Read and start parsing audio
We use Audiofilestream to parse the audio information, and after the user calls the play method, first call Audiofilestreamopen and open the Audiofilestream as follows:
extern OSStatus AudioFileStreamOpen (void *inClientData, AudioFileStream_PropertyListenerProc inPropertyListenerProc, AudioFileStream_PacketsProc inPacketsProc, AudioFileTypeID inFileTypeHint, AudioFileStreamID * outAudioFileStream);
The parameters of the Audiofilestreamopen are described below:
1. inclientdata: User-specified data for passing to the callback function, where we specify (__bridge localaudioplayer*) Self
2. Inpropertylistenerproc: When parsing to an audio message, the method is recalled
3. Inpacketsproc: When parsing to an audio frame, the method is recalled
4. infiletypehint: Specify the format of the audio data, if you do not know the format of audio data, you can pass 0
5. Outaudiofilestream: audiofilestreamid instance, to be saved for subsequent use
After reading the data, call Audiofilestreamparsebytes to parse the data, which is prototyped as follows:
ininininFlags);
The parameters are described as follows:
1. Inaudiofilestream: audiofilestreamid instance, opened by Audiofilestreamopen
2. indatabytesize: The size of the data bytes parsed
3. InData: The data size of this resolution
4. inflags: Data parsing flag, where only one value kaudiofilestreamparseflag_discontinuity = 1, indicating whether the parsed data is discontinuous, we can now pass 0.
When the file data is read at the end of the collection, you can close the file.
Parsing Audio Information
If the audio information is resolved, the previously specified callback function is called, as follows:
Each related property can be called Audiofilestreamgetproperty to get the corresponding value, and the prototype is as follows:
ininPropertyID, UInt32 *ioPropertyDataSize, void * outPropertyData);
Parameter description:
1. Inaudiofilestream: audiofilestreamid instance, opened by Audiofilestreamopen
2. Inpropertyid: Name of the property to get, see AudioFileStream.h
3. iopropertydatasize: Indicates the size of the property
4. outpropertydata: space for storing this property value
Kaudiofilestreamproperty_dataformat
This property indicates the format information for the audio data, and the returned data is a audiostreambasicdescription structure. Need to save for use with Audioqueue
Kaudiofilestreamproperty_fileformat
This property indicates the encoding format of the audio data, such as MPEG.
Kaudiofilestreamproperty_audiodatabytecount
This property gets the length of the audio data that can be used to calculate the audio duration, calculated as:
Duration = (audio data byte size * 8)/Sample rate
Kaudiofilestreamproperty_bitrate
This property gets the sample rate to the audio, which can be used to calculate the audio duration
Kaudiofilestreamproperty_dataoffset
This property indicates the offset of the audio data in the entire audio file:
Total audio File size = offset + audio data byte size
Kaudiofilestreamproperty_audiodatapacketcount
This property indicates how many frames are in the audio file
Kaudiofilestreamproperty_readytoproducepackets
This property tells us that the full audio frame data has been parsed, ready to produce an audio frame, and then called to another callback function, where we create the audio queue audioqueue, if there is magic Cookie data in the audio The Audiofilestreamgetpropertyinfo is called first, gets whether the data is writable, if it can be written and then fetches the property value, and writes to Audioqueue. The audio data frame is then parsed.
Parsing Audio Frames
After the audio information is resolved, the audio data frame should be parsed, as shown in the following code:
Here, we use the previously set of Inclientdata, the callback function from the C language form to the form of OBJC, the processing code after parsing to the audio data is as follows:
After parsing to the audio data, we will write the data to Audioqueuebuffer, first of all, the prototype of the callback function is as follows:
typedef void (*AudioFileStream_PacketsProc)(void * ininNumberBytes,UInt32 inNumberPackets, const void *inInputData, AudioStreamPacketDescription *inPacketDescriptions);
Parameter description:
1. inclientdata: User Data set by Audiofilestreamopen
2. innumberbytes: Number of bytes in audio data
3. innumberpackets: The number of audio frames resolved to
4. ininputdata: Data that contains these audio data frames
5. inpacketdescriptions: audiostreampacketdescription array, which contains Mstartoffset, indicating the starting position of the relevant data for the frame, Mdatabytesize indicates the size of the frame data.
At this point we first create an audio queue to play the audio and allocate space for each buffer as follows:
Then we traverse each frame, get the offset position and the data size of each frame, if the current frame data size can not be stored in the current buffer, at this time, we should queue the current buffer, modify the currently used buffer to the next, and reset the relevant data fill information and the data frame information contained.
self.audioQueueCurrentBufferIndex = (++self.audioQueueCurrentBufferIndex) % kNumberOfBuffers;self0;self0;
If you haven't started playing music at this point, you can start playing music
if(self.isPlayingNO) { NULL); self.isPlayingYES;}
If the next specified buffer is already in use, that is, all buffers are full and queued, you should wait
while(inuse[self.audioQueueCurrentBufferIndex]);
Finally, if the buffer space can hold a frame of data, we use memcpy to copy the data to the corresponding position in the buffer, save the relevant information for each frame, set the offset of each frame in the buffer (Mstartoffset), set the data size of the current buffer's stored party, And the amount of frames already included.
When a buffer is used, Audioqueue will invoke the callback function set by Audioqueuenewoutput, as follows:
In this callback, we iterate through each buffer, find an empty buffer, and modify the flag of that buffer to be unused.
Play audio data
When processing a data frame, if the buffer is full (the buffer space is not enough to hold the next frame of data), you can start playing audio at this time
if(self.isPlayingNO) { NULL); self.isPlayingYES;}
We can also call Audioqueuepause and other related methods to pause and terminate the audio playback as follows:
Clean up Related resources
Finally, we clean up the resources, in front of which we used the Audioqueueaddpropertylistener to set a listener for the Kaudioqueueproperty_isrunning property, This function is called when the Audioqueue is started or terminated:
The callback function is as follows:
In this method, we use Audioqueuereset to reset the play queue, call Audioqueuefreebuffer to free the buffer space, release all audioqueue resources, and turn off Audiofilestream.
However, in the actual use of the process, I found that the data is empty when the Audioqueue does not actively terminate, that is not actively invoke the callback, so I think, it should be to get ourselves to the current playback progress, when the play is finished call Audioqueuestop stop play it, The question remains to be studied later.
End
Finally finished all the code, the user just click Play, you can hear "distant her" this wonderful music. Since there is only one play button on the interface, do not put on the demo, put on headphones, quietly enjoy their achievements with this wonderful music.
iOS audio playback audioqueue (a): Play local music