×
Namespaces

Variants
Actions
(Difference between revisions)

Archived:Audiostream playback using Symbian C++

From Nokia Developer Wiki
Jump to: navigation, search
toth.b (Talk | contribs)
(External links)
toth.b (Talk | contribs)
Line 1: Line 1:
[[Category:Audio]]Generally there are two opportunities to play back raw audio data on Symbian OS. We can use CMdaAudioPlayerUtility and MMdaAudioPlayerCallback or CMdaAudioOutputStream and MMdaAudioOutputStreamCallback classes. The former requires the whole data audio data with a valid WAVE header (in the header are the bitrate, sampling frequency and length defined), the latter opens a stream and we can write to the stream anytime. Consequently the second solution is much more memory friendly; it is preferable especially in case of longer audio data. It may be also prefered in case of remote audiostream playback (e.g. streaming audio from the internet).
+
[[Category:Audio]]Generally there are two opportunities to play back raw audio data on Symbian OS. We can use  
  
This article deals with the second solution, also, with audiostream playback. In the first part of the article the main idea and functionality of streaming playback is introduced and the second part discusses the occurrent problems on devices. According to my research some of these uprising problems were never publicized before.
+
''CMdaAudioPlayerUtility'' and ''MMdaAudioPlayerCallback'', or ''CMdaAudioOutputStream'' and
 +
 
 +
''MMdaAudioOutputStreamCallback'' classes. The former requires the whole data audio data with a valid WAVE header (in the
 +
 
 +
header are the bitrate, sampling frequency and length defined), the latter opens a stream and we can write to the stream
 +
 
 +
anytime. Consequently the second solution is much more memory friendly; it is preferable especially in case of longer audio
 +
 
 +
data. It may be also prefered in case of remote audiostream playback (e.g. streaming audio from the internet).
 +
 
 +
This article deals with the second solution, also, with audiostream playback. In the first part of the article the main idea  
 +
 
 +
and functionality of streaming playback is introduced and the second part discusses the occurring problems on devices.  
 +
 
 +
According to my findings, some of these problems have never been publish before.
  
 
== The usage of CMdaAudioOutputStream and MMdaAudioPlayerCallback ==
 
== The usage of CMdaAudioOutputStream and MMdaAudioPlayerCallback ==
  
The audio playback class must be inherited from MMdaAudioPlayerCallback. In this class we must define a CMdaAudioOutputStream type object:
+
The audio playback class must be inherited from ''MMdaAudioPlayerCallback''. In this class we must reference a  
 +
 
 +
''CMdaAudioOutputStream'' type object:
 
<code cpp>
 
<code cpp>
CMdaAudioOutputStream* iAudioOutputStream;
+
CMdaAudioOutputStream* iAudioOutputStream;
 
</code>
 
</code>
Than we can create it:
+
Then we can instantiate an object:
 
<code cpp>
 
<code cpp>
 
TRAPD(err, iAudioOutputStream = CMdaAudioOutputStream::NewL(*this););
 
TRAPD(err, iAudioOutputStream = CMdaAudioOutputStream::NewL(*this););
 
PanicIfError(err);
 
PanicIfError(err);
 
</code>
 
</code>
Where *this is the inherited class from MMdaAudioPlayerCallback. Now we have to open the stream:
+
where *this is the inherited class from ''MMdaAudioPlayerCallback''. Now we have to open the stream:
 
<code cpp>
 
<code cpp>
 
TMdaAudioDataSettings iStreamSettings;
 
TMdaAudioDataSettings iStreamSettings;
Line 21: Line 37:
 
iAudioOutputStream->Open(&iStreamSettings);
 
iAudioOutputStream->Open(&iStreamSettings);
 
</code>
 
</code>
When the stream is opened, the MaoscOpenComplete(TInt aError) callback function is called, we must implement it in our class. In this function we can start to write into the stream (CPlayer is the inherited class from MMdaAudioPlayerCallback):
+
When the stream is opened, the ''MaoscOpenComplete(TInt aError)'' callback function is called, so we must implement it in our  
 +
 
 +
class. In this function we can start to write into the stream (''CAudiostreamPlayer'' is derived from  
 +
 
 +
''MMdaAudioPlayerCallback''):
 
<code cpp>
 
<code cpp>
void CPlayer::MaoscOpenComplete(TInt aError)
+
void CAudiostreamPlayer::MaoscOpenComplete(TInt aError)
 
{
 
{
 
if (aError == KErrNone)
 
if (aError == KErrNone)
Line 32: Line 52:
 
}
 
}
 
</code>
 
</code>
When iSomeMono8khzRawAudioData is copied into the stream, the MaoscBufferCopied(TInt aError, const TDesC8& aBuffer) callback function is called, consequently it must also be implemented:
+
When ''iSomeMono8khzRawAudioData'' is copied into the stream, the ''MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)''
 +
 
 +
callback function is called, consequently it must also be implemented:
 
<code cpp>
 
<code cpp>
void CPlayer::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)
+
void CAudiostreamPlayer::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)
 
{
 
{
 
if (aError==KErrNone)
 
if (aError==KErrNone)
Line 48: Line 70:
 
''(Note: aBuffer points to the copied buffer.)''
 
''(Note: aBuffer points to the copied buffer.)''
  
When there is no more audio data, or the last audio data is played, and new data wasn’t written into the stream, than the MaoscPlayComplete(TInt aError) callback function is called (it is also called in case of any occurent error or if the Stop() function was called):
+
When there is no more audio data, or the last audio data segment has been played, and new data wasn’t written into the  
 +
 
 +
stream, then the ''MaoscPlayComplete(TInt aError)'' callback function is called; it is also called in case of any occuring
 +
 
 +
error or if the ''CMdaAudioOutputStream::Stop()'' function was called:
 
<code cpp>
 
<code cpp>
void CPlayer:: MaoscPlayComplete (TInt aError)
+
void CAudiostreamPlayer:: MaoscPlayComplete (TInt aError)
 
{
 
{
 
if (aError==KErrUnderflow)  
 
if (aError==KErrUnderflow)  
Line 69: Line 95:
 
}
 
}
 
</code>
 
</code>
We can stop playing anytime by:
+
We can stop playing anytime by calling:
 
<code cpp>
 
<code cpp>
 
iAudioOutputStream->Stop();
 
iAudioOutputStream->Stop();
 
</code>
 
</code>
More information, functions and error codes can be found in the [http://www.symbian.com/Developer/techlib/v70docs/SDL_v7.0/doc_source/reference/cpp/AudioStreaming/index.html Symbian SDK].
+
More information on functions and error codes can be found in the  
 +
 
 +
[http://www.symbian.com/Developer/techlib/v70docs/SDL_v7.0/doc_source/reference/cpp/AudioStreaming/index.html Symbian SDK  
 +
 
 +
documentation].
  
 
== Device specific errors ==
 
== Device specific errors ==
  
The audiostream playback as described above works perfectly on the emulator, but unfortunaltey it does not work so on the devices. There are more major issues that are present on some devices. Developers must pay atteintion to these issues to successfully implement audiostream playback in their application. Most of these issues can be solved.  
+
The audiostream playback as described above works perfectly on the emulator, but unfortunaltey it does not work so on the  
 +
 
 +
devices. There are several major issues that are present on some devices. Developers must pay attention to these issues to  
 +
 
 +
successfully implement audiostream playback in their application. Most of these issues can be solved.  
  
 
===Missing MaoscPlayComplete()===
 
===Missing MaoscPlayComplete()===
  
The MaoscPlayComplete() callback function is never called on 2nd edition devices (not tested on 3rd edition devices) in case of KErrUnderflow, so when we stop writing to the stream or when the speed of the audio playback is faster than the speed the buffer is written into the stream. The actual end of playback will happen in MaoscBufferCopied() instead. We can check the returned buffer from MaoscBufferCopied() to compare it with the buffers in the stream (e.g. check, if it is the last buffer or not).
+
The ''MaoscPlayComplete()'' callback function is never called on 2nd edition devices (have not tested it on 3rd edition  
We can still achive MaoscPlayComplete() to be called with aError==KErrCancel , if we call the CMdaAudioOutputStream::Stop() function.
+
 
 +
devices) in case of ''KErrUnderflow'', so when we stop writing to the stream or when the speed of the audio playback is  
 +
 
 +
faster than the speed the buffer is written into the stream. The actual end of playback is indicated by MaoscBufferCopied()  
 +
 
 +
being called instead. We can check the identity of the buffer received by ''MaoscBufferCopied()'', comparing it with the  
 +
 
 +
buffers in the stream (e.g. to see whether it is the last buffer or not).
 +
We can still achieve ''MaoscPlayComplete()'' to be called with aError==KErrCancel, if we call the  
 +
 
 +
CMdaAudioOutputStream::Stop() function.
  
 
===Memory leaking in MaoscPlayComplete()===
 
===Memory leaking in MaoscPlayComplete()===
  
The developer might like to destroy the CMdaAudioOutputStream* object in MaoscPlayComplet(), if the whole audio data was played (and stoped) to free up memory. But if it is destroyed in MaoscPlayComplete(), than it leads to memory leaking.
+
The developer might like to destroy the ''CMdaAudioOutputStream*'' object in ''MaoscPlayComplet()'', when the whole audio  
 +
 
 +
data has been played (and playing has stopped) to free up memory. But if it is destroyed in ''MaoscPlayComplete()'', then it  
 +
 
 +
leads to memory leaking.
  
 
===MaoscOpenComplete() never called when reopening the stream===
 
===MaoscOpenComplete() never called when reopening the stream===
  
On some 2nd edition devices (not tested on 3rd edition devices) the MaoscOpenComplete() callback function is not called, if we reopen the stream (also when we call the Open() function more than once). To overcome the problem we must reconstruct the stream each time before opening it:
+
On some 2nd edition devices (have not tested on 3rd edition devices) the ''MaoscOpenComplete()'' callback function is not  
 +
 
 +
called if we reopen the stream, just like when we call the ''Open()'' function more than once. To overcome the problem we  
 +
 
 +
must reconstruct the stream each time before opening it:
 
<code cpp>
 
<code cpp>
 
if (iAudioOutputStream) delete iAudioOutputStream;
 
if (iAudioOutputStream) delete iAudioOutputStream;
Line 100: Line 152:
 
===Stop() causes crash if it is called in the callback functions===
 
===Stop() causes crash if it is called in the callback functions===
  
Developers may call the CMdaAudioOutputStream::Stop() function in MaoscBufferCopied() to reach MaoscPlayComplete(). But on some 2nd edition devices (not tested on 3rd edition devices) the application freezes when the Stop() function is called in the callback functions. To avoid this problem, the Stop() function should not be called directly from the callbacks. The idea is to use CIdle object for calling the Stop() function. With the help of CIdle the Stop() function is called only when there are no higher priority active objects running. So we should create and start the CIdle object when normally we would like to call the Stop() function and call the Stop() function in the CIdle object.
+
Developers may call the ''CMdaAudioOutputStream::Stop()'' function in ''MaoscBufferCopied()'' to reach  
 +
 
 +
''MaoscPlayComplete()''. But on some 2nd edition devices (have not tested on 3rd edition devices) the application freezes  
 +
 
 +
when the ''Stop()'' function is called in the callback functions. To avoid this problem, the ''Stop()'' function should not  
 +
 
 +
be called directly from the callbacks. A possible solution is to use CIdle object for calling the ''Stop()'' function. With  
 +
 
 +
the help of ''CIdle'' the ''Stop()'' function is called only when there are no higher priority active objects running. So we  
 +
 
 +
should create and start the ''CIdle'' object when normally we would call the ''Stop()'' function and call the ''Stop()''
 +
 
 +
function in the CIdle object.
 
<code cpp>
 
<code cpp>
 
iStop = CIdle::NewL(EPriorityIdle);
 
iStop = CIdle::NewL(EPriorityIdle);
Line 106: Line 170:
 
</code>
 
</code>
 
<code cpp>
 
<code cpp>
TInt CPlayer::BackgroundStop(TAny *aStream)
+
TInt CAudiostreamPlayer::BackgroundStop(TAny *aStream)
 
{
 
{
return ((CPlayer*)aStream)->Stop();
+
return ((CAudiostreamPlayer*)aStream)->Stop();
 
}
 
}
  
TBool CPlayer::Stop()
+
TBool CAudiostreamPlayer::Stop()
 
{  
 
{  
 
iAudioOutputStream->Stop();   
 
iAudioOutputStream->Stop();   
Line 119: Line 183:
 
===Memory leaking in MaoscBufferCopied() callback function===
 
===Memory leaking in MaoscBufferCopied() callback function===
  
One might like to do some extra calculations in MaoscBufferCopied() callback function (e.g. MP3 decoding, downloading from the Internet, text-to-speech conversion, etc.). In this case it is favorable to do the calculations after the last audio data was written into the stream. Unfortunately it causes memory leaking on 2nd edition devices (not tested on 3rd edition devices). We can easily reproduce the problem by putting a for loop after the CMdaAudioOutputStream::WriteL() function:
+
One might like to do some extra calculations in ''MaoscBufferCopied()'' callback function (e.g. MP3 decoding, downloading  
 +
 
 +
from the Internet, text-to-speech conversion, etc.). In this case it is favorable to do the calculations after the last audio  
 +
 
 +
data segment was written into the stream. Unfortunately it causes memory leaking on 2nd edition devices (have not tested on  
 +
 
 +
3rd edition devices). We can easily reproduce the problem by putting a for loop after the ''CMdaAudioOutputStream::WriteL()''
 +
 
 +
function:
 
<code cpp>
 
<code cpp>
void CPlayer:: MaoscPlayComplete (TInt aError)
+
void CAudiostreamPlayer:: MaoscPlayComplete (TInt aError)
 
{
 
{
 
if (aError==KErrUnderflow)  
 
if (aError==KErrUnderflow)  
Line 150: Line 222:
 
}
 
}
 
</code>
 
</code>
Unfortunately this issue is not solved yet. Any idea and suggestion is welcomed.
+
Unfortunately this issue has not yet been solved. Any idea and suggestion is welcome.
  
 
== External links ==
 
== External links ==
* [http://www.symbian.com/Developer/techlib/v70docs/SDL_v7.0/doc_source/reference/cpp/AudioStreaming/index.html Symbian SDK]
+
[http://www.symbian.com/Developer/techlib/v70docs/SDL_v7.0/doc_source/reference/cpp/AudioStreaming/index.html Symbian SDK]
* [http://ncsp.forum.nokia.com/download/?asset_id=11732 Multimedia Framework in Series 60 Developer Platform 2.0]
+
[http://ncsp.forum.nokia.com/download/?asset_id=11732 Multimedia Framework in Series 60 Developer Platform 2.0]
* [http://www.forum.nokia.com/info/sw.nokia.com/id/471b4e01-d115-406a-b144-7b3b2832a804/S60_Platform_Audio_Streaming_Example_v2_0_en.zip.html S60 Platform: Audio Streaming Example v2.0]
+
[http://www.forum.nokia.com/info/sw.nokia.com/id/471b4e01-d115-406a-b144-7b3b2832a804/S60_Platform_Audio_Streaming_Example_v2_0_en.zip.html S60 Platform: Audio Streaming Example v2.0]

Revision as of 15:07, 7 June 2007

Generally there are two opportunities to play back raw audio data on Symbian OS. We can use

CMdaAudioPlayerUtility and MMdaAudioPlayerCallback, or CMdaAudioOutputStream and

MMdaAudioOutputStreamCallback classes. The former requires the whole data audio data with a valid WAVE header (in the

header are the bitrate, sampling frequency and length defined), the latter opens a stream and we can write to the stream

anytime. Consequently the second solution is much more memory friendly; it is preferable especially in case of longer audio

data. It may be also prefered in case of remote audiostream playback (e.g. streaming audio from the internet).

This article deals with the second solution, also, with audiostream playback. In the first part of the article the main idea

and functionality of streaming playback is introduced and the second part discusses the occurring problems on devices.

According to my findings, some of these problems have never been publish before.

Contents

The usage of CMdaAudioOutputStream and MMdaAudioPlayerCallback

The audio playback class must be inherited from MMdaAudioPlayerCallback. In this class we must reference a

CMdaAudioOutputStream type object:

CMdaAudioOutputStream* iAudioOutputStream;

Then we can instantiate an object:

TRAPD(err, iAudioOutputStream = CMdaAudioOutputStream::NewL(*this););
PanicIfError(err);

where *this is the inherited class from MMdaAudioPlayerCallback. Now we have to open the stream:

TMdaAudioDataSettings iStreamSettings;
iChannels = TMdaAudioDataSettings::EChannelsMono;
iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz;
iAudioOutputStream->Open(&iStreamSettings);

When the stream is opened, the MaoscOpenComplete(TInt aError) callback function is called, so we must implement it in our

class. In this function we can start to write into the stream (CAudiostreamPlayer is derived from

MMdaAudioPlayerCallback):

void CAudiostreamPlayer::MaoscOpenComplete(TInt aError)
{
if (aError == KErrNone)
{
// the stream is opened
iAudioOutputStream->WriteL(iSomeMono8khzRawAudioData);
}
}

When iSomeMono8khzRawAudioData is copied into the stream, the MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)

callback function is called, consequently it must also be implemented:

void CAudiostreamPlayer::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)
{
if (aError==KErrNone)
{
// if there is more data, then write it into the stream
if (iMoreData==ETrue)
{
iAudioOutputStream->WriteL(iSomeMono8khzRawAudioData);
}
}
}

(Note: aBuffer points to the copied buffer.)

When there is no more audio data, or the last audio data segment has been played, and new data wasn’t written into the

stream, then the MaoscPlayComplete(TInt aError) callback function is called; it is also called in case of any occuring

error or if the CMdaAudioOutputStream::Stop() function was called:

void CAudiostreamPlayer:: MaoscPlayComplete (TInt aError)
{
if (aError==KErrUnderflow)
{
if (iMoreData==ETrue)
{
iAudioOutputStream->WriteL(iSomeMono8khzRawAudioData);
}
else
{
// done
}
}
else if (aError==KErrCancel)
{
// stopped
}
}

We can stop playing anytime by calling:

iAudioOutputStream->Stop();

More information on functions and error codes can be found in the

[http://www.symbian.com/Developer/techlib/v70docs/SDL_v7.0/doc_source/reference/cpp/AudioStreaming/index.html Symbian SDK

documentation].

Device specific errors

The audiostream playback as described above works perfectly on the emulator, but unfortunaltey it does not work so on the

devices. There are several major issues that are present on some devices. Developers must pay attention to these issues to

successfully implement audiostream playback in their application. Most of these issues can be solved.

Missing MaoscPlayComplete()

The MaoscPlayComplete() callback function is never called on 2nd edition devices (have not tested it on 3rd edition

devices) in case of KErrUnderflow, so when we stop writing to the stream or when the speed of the audio playback is

faster than the speed the buffer is written into the stream. The actual end of playback is indicated by MaoscBufferCopied()

being called instead. We can check the identity of the buffer received by MaoscBufferCopied(), comparing it with the

buffers in the stream (e.g. to see whether it is the last buffer or not). We can still achieve MaoscPlayComplete() to be called with aError==KErrCancel, if we call the

CMdaAudioOutputStream::Stop() function.

Memory leaking in MaoscPlayComplete()

The developer might like to destroy the CMdaAudioOutputStream* object in MaoscPlayComplet(), when the whole audio

data has been played (and playing has stopped) to free up memory. But if it is destroyed in MaoscPlayComplete(), then it

leads to memory leaking.

MaoscOpenComplete() never called when reopening the stream

On some 2nd edition devices (have not tested on 3rd edition devices) the MaoscOpenComplete() callback function is not

called if we reopen the stream, just like when we call the Open() function more than once. To overcome the problem we

must reconstruct the stream each time before opening it:

if (iAudioOutputStream) delete iAudioOutputStream;
iAudioOutputStream = NULL; // In case the following NewL leaves
TRAPD(err, iAudioOutputStream = CMdaAudioOutputStream::NewL(*this););
PanicIfError(err);

Stop() causes crash if it is called in the callback functions

Developers may call the CMdaAudioOutputStream::Stop() function in MaoscBufferCopied() to reach

MaoscPlayComplete(). But on some 2nd edition devices (have not tested on 3rd edition devices) the application freezes

when the Stop() function is called in the callback functions. To avoid this problem, the Stop() function should not

be called directly from the callbacks. A possible solution is to use CIdle object for calling the Stop() function. With

the help of CIdle the Stop() function is called only when there are no higher priority active objects running. So we

should create and start the CIdle object when normally we would call the Stop() function and call the Stop()

function in the CIdle object.

iStop = CIdle::NewL(EPriorityIdle);
iStop->Start(TCallBack(BackgroundStop, this));
TInt CAudiostreamPlayer::BackgroundStop(TAny *aStream)
{
return ((CAudiostreamPlayer*)aStream)->Stop();
}
 
TBool CAudiostreamPlayer::Stop()
{
iAudioOutputStream->Stop();
return EFalse;
}

Memory leaking in MaoscBufferCopied() callback function

One might like to do some extra calculations in MaoscBufferCopied() callback function (e.g. MP3 decoding, downloading

from the Internet, text-to-speech conversion, etc.). In this case it is favorable to do the calculations after the last audio

data segment was written into the stream. Unfortunately it causes memory leaking on 2nd edition devices (have not tested on

3rd edition devices). We can easily reproduce the problem by putting a for loop after the CMdaAudioOutputStream::WriteL()

function:

void CAudiostreamPlayer:: MaoscPlayComplete (TInt aError)
{
if (aError==KErrUnderflow)
{
if (iMoreData==ETrue)
{
iAudioOutputStream->WriteL(iSomeMono8khzRawAudioData);
for (TInt k=0; k<100000; k++)
{
k++;
for (TInt l=0; l<100; l++)
{
k++;
}
k-=101;
}
 
}
else
{
// done
}
}
else if (aError==KErrCancel)
{
// stopped
}
}

Unfortunately this issue has not yet been solved. Any idea and suggestion is welcome.

External links

Symbian SDK Multimedia Framework in Series 60 Developer Platform 2.0 S60 Platform: Audio Streaming Example v2.0

334 page views in the last 30 days.
×