the engine uses cmdaaudiooutputstream and mmdaaudiooutputstreamcallback to play the audio. It consists of three main classes:
caudiostreamplayer. It combines cmdaaudiooutputstream, inherits cactive, and implements the mmdaaudiooutputstreamcallback interface. We need to carefully maintain the buffer size for low-latency playback. Cactive constantly creates new tasks, estimates the remaining data in the buffer in the runl function, and appends appropriate data to maintain the expected size of the buffer.
csimplemixer. It implements the caudiogenerator interface. Because cmdaaudiooutputstream is a single stream player, you need to write a mixed speaker for Waveform mixing. Waveform mixing is a simple addition of data. The mixer has many channels ). Each channel records the caudio pointer and current playback position.
caudio. Contains an audio buffer. For each sound file, we also need a class to load it into the memory buffer.
I will not explain how to play audio here. I need a separate Article . If you use this method to play audio, I just want to discuss two issues here.
to learn the basics of sound, see www.newlc.com/article.php? Id_article = 113 . (Unfortunately, I couldn't find the article and Code when I was learning the sound.)
1.5.1. Sound disabling and enabling
Because the entire audio system is a pull structure, audio streams pull data from the mixer and the mixer pulls data from the audio buffer zone. Therefore, you only need to delete the cmdaaudiooutputstream and the cactive object that writes data, and the sound playing stops. In my implementation, that is, the delete caudiostreamplayer object. To enable the sound, you only need to recreate this object.
The benefit of this implementation is thatProgramThe other part does not need to save whether the sound is enabled. Because caudio and csimplemixer objects exist, caudio can insert itself into the mixer channel and feel like it is playing. In fact, because caudiostreamplayer does not pull data from mixer, the sound device is completely stopped.
However, you must note that you need to clear the sound data in the mixer before resuming sound playback. After a long period of operation, the channels in the mixer are filled with various sounds. If it is suddenly opened at this time, there will be various delayed noises.
1.5.2. Special error handling
The callback functions maoscopencomplete, maoscbuffercopied, and maoscplaycomplete in the mmdaaudiooutputstreamcallback interface have an error code parameter. You cannot ignore this parameter.
For example, the maoscplaycomplete function is called when the audio is stopped. There may be many reasons to stop playing. We all know that we need to deal with kerrunderflow. Does this error mean that the mixer does not supply its audio data in time. In this case, you need to restart the sound stream. However, in some cases, kerrdied and kerrinuse are easily ignored. When kerrdied receives a call, the sound thread is dead, and the entire audio system needs to be rebuilt. When kerrinuse receives a text message, the sound device is preemptible to play the text message. At this time, you also need to re-build the entire sound system, but you cannot re-build it immediately. Otherwise, the same result will be returned. You should wait a few seconds before recreating it.
The restart sound stream mentioned above is different from the reconstruction sound system depth. Restart the sound stream. You can see it later in the code. Runaudiol writes the first sound buffer to the audio stream. In my implementation, the sound reconstruction system refers to deleting and then creating a caudiostreamplayer object in newl.
The error handling code is as follows:
// Audio stream API callback: called when playback has finished.
Void caudiostreamplayer: maoscplaycomplete (tint anerror)
{
If (m_bindelay)
Return;
// If we finish due to an underflow, we "ll need to restart playback.
// Normally kerrunderlow is raised at stream end, but in our case the API
// Shocould never see the stream end -- we are continuously feeding it more
// Data! Lower underflow errors mean that the latency target is too low.
If (anerror = kerrunderflow ){
Iobserver-> masomessage (_ L ("play underflow "));
// The number of samples played gets resetted to zero when we restart
// Playback after Underflow
Ibasesamplesplayed = isampleswritten;
// stop and restart
istream-> stop ();
cancel ();
# ifdef rate_16k
istream-> setaudiopropertiesl (tmdaaudiodatasettings: Unknown,
tmdaaudiodatasettings:: echannelsmono);
# else
istream-> setaudiopropertiesl (tmdaaudiodataset Authorization: esamplerate8000hz,
tmdaaudiodatasettings: echannelsmono );
# endif
istream-> setvolume (istream-> maxvolume ()/4);
trapd (error, runaudiol ());
If (error! = Kerrnone) {
User: panic (kplay, error);
}
return;
}< SPAN class = "apple-converted-space">
else if (anerror = kerrdied)
{< br> m_bindelay = etrue;
m_rebuilddelay = 0; // no delay
}< br> else if (anerror = kerrinuse)
{< br> m_bindelay = etrue;
m_rebuilddela Y = 3000; // delay 3 second
}< br> else if (anerror! = Kerrnone) {
// some other error, panic!
User: panic (kplaycomplete, anerror);
}< BR >}< br> the m_rebuilddelay mark is found from the outside, and the caduiostreamplayer object is rebuilt.
except for maoscplaycomplete, I ignored kerrunderflow and kerrabort errors in maoscbuffercopied. The kerrinuse error is also handled in maoscbuffercopied and maoscopencomplete.
after the above processing, my program can safely handle special situations such as incoming calls, text messages, and Switching programs.