Low latency audio streaming
| Sun, 2003-05-25 21:08 | |
|
Hi! I am currently trying to implement low latency audio output for my game library. I am using CMdaAudioOutputStream to output sound. But the problem is that I get very high latency (more than 0.5s) even with a very small buffersize as Symbian seems to use an additional internal buffer. I also tried using a circular buffer which I modifed while being written. The results were not satisfying. I read the document "soundforgames.pdf" that was released at the Symbian Expo. I basically did what was described there but the results were not satisfying at all. Could anybody point me to a solution? Tnx, zeep zeep |
|






Forum posts: 363
Forum posts: 31
I am developing for Series 60. If I ever find a solution to this problem I will let you know...
zeep
zeep
Forum posts: 363
Still, even if you do have an N-Gage, the prototypes surely are different from the shelf models.
Forum posts: 31
I think Nokia should be a bit more talkative about such important details.
No, I don't have a n-gage unit, I just do this as a hobby. Do you know if n-gage developers have more detailed information about fast sound/graphics-programming? (I know they probably signed an NDA or something, just want to know if they have to figure everything out by themselves too).
zeep
zeep
Forum posts: 37
I am trying to implement multi-channle sound via CMdaAudioOutputStream.
I found an AudioEngine `sidplay', it does support EPOC/S60,
but it can only play the defined format .sid, and that engile seems pretty complex for S60 only
Are you interested in studying SIDPLAY and writing our S60 AudioEngine?
Cheer,
ouseka
ouseka
Forum posts: 31
But I am very interested in writing a low latency engine.
I already wrote a simple mixer which can play MOD-files.
zeep
Forum posts: 37
Are you reading the source code of MikMod? me 2...
Regards,
ouseka
ouseka
Forum posts: 31
But this is not the first sound engine I am writing. Did some stuff long ago in good old MS-DOS times. So the MOD-format is not completely unfamiliar to me
zeep
Forum posts: 37
Cheer,
ouseka
ouseka
Forum posts: 2006
Looks like I should create a forum dedicated to audio stuff......
Cheers
Eric
Eric Bustarret
NewLC Founder & CEO / Professional Symbian OS Consultant
Forum posts: 6
All of you have good knowledge in Audio. I am facing a problem in AudioConvertUtility of Symbian OS. After successfully calling OpenL() method (I checked ErrorCode) the output buffer size is zero. Why?
I am pasting the code for you.
// TInputStream.cpp
//
// Copyright (c) 2003 Symbian Ltd. All rights reserved.
//
#include <f32file.h>
#include <barsread.h>
#include <eikbtpan.h>
#include <eikcmbut.h>
#include <eiklabel.h>
#include <eikmover.h>
#include <eikbtgpc.h>
#include <eikon.hrh>
#include <eikon.rsg>
#include "TInputStream.h"
#include <MdaAudioSampleEditor.h>
const TInt KBufferSize = 16000;
_LIT(KRecorderFile, "C:\\System\\Apps\\record.wav");
RFs fs;
RFile file;
//
// CTInputStreamAppView
//
CTInputStreamAppView::CTInputStreamAppView():
CCoeControl()
{}
CTInputStreamAppView::~CTInputStreamAppView()
{
delete iBitmapGc;
delete iBitmapDev;
delete iBitmap;
}
void CTInputStreamAppView::ConstructL(const TRect& aRect)
{
CreateWindowL();
SetRect(aRect);
ActivateL();
const CFont* font = ControlEnv()->NormalFont();
iFontHeight = font->HeightInPixels();
iFontAscent = font->AscentInPixels();
iBitmap = new(ELeave) CFbsBitmap;
User::LeaveIfError(iBitmap->Create(aRect.Size(),Window().DisplayMode()));
iBitmapDev = CFbsBitmapDevice::NewL(iBitmap);
iBitmapDev->CreateContext(iBitmapGc);
iBitmapGc->UseFont(font);
iBitmapGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
iBitmapGc->SetBrushColor(KRgbWhite);
iBitmapGc->Clear();
}
void CTInputStreamAppView::AddMessage(const TDesC& aMessage)
{
iBitmapGc->CopyRect(TPoint(0,-iFontHeight),Rect());
const TRect messageRect(0,Rect().iBr.iY - iFontHeight,Rect().iBr.iX,Rect().iBr.iY);
iBitmapGc->DrawText(aMessage,messageRect,iFontAscent);
Window().Invalidate();
}
void CTInputStreamAppView::Draw(const TRect& aRect) const
{
CWindowGc& gc = SystemGc();
gc.BitBlt(aRect.iTl,iBitmap,aRect);
}
//
// CTInputStreamAppUi
//
CTInputStreamAppUi::~CTInputStreamAppUi()
{
delete iInput;
delete iOutput;
iBuffer.ResetAndDestroy();
if (iAppView)
{
RemoveFromStack(iAppView);
delete iAppView;
}
if(iConverter)
delete iConverter;
}
void CTInputStreamAppUi::ConstructL()
{
BaseConstructL();
iAppView = new(ELeave) CTInputStreamAppView;
iAppView->ConstructL(ClientRect());
AddToStackL(iAppView);
iSettings.iChannels = TMdaAudioDataSettings::EChannelsMono;
iSettings.iSampleRate= TMdaAudioDataSettings::ESampleRate8000Hz;
iInput = CMdaAudioInputStream::NewL(*this);
iInput->Open(&iSettings);
iOutput = CMdaAudioOutputStream::NewL(*this);
iOutput->Open(&iSettings);
iConverter = new (ELeave) log_test;
iConverter->ConstructL();
TDes8* buffer = new(ELeave) TBuf8<KBufferSize>;
buffer->SetMax();
CleanupStack::PushL(buffer);
User::LeaveIfError(iBuffer.Append(buffer));
CleanupStack::Pop(buffer);
buffer = new(ELeave) TBuf8<KBufferSize>;
buffer->SetMax();
CleanupStack::PushL(buffer);
User::LeaveIfError(iBuffer.Append(buffer));
CleanupStack::Pop(buffer);
buffer = new(ELeave) TBuf8<KBufferSize>;
buffer->SetMax();
CleanupStack::PushL(buffer);
User::LeaveIfError(iBuffer.Append(buffer));
CleanupStack::Pop(buffer);
}
void CTInputStreamAppUi::HandleCommandL(TInt aCommand)
{
switch (aCommand)
{
case ETInputStreamCmdRecord:
RecordL();
break;
case ETInputStreamCmdPlay:
PlayL();
break;
case EAknSoftkeyBack:
CEikAppUi::Exit();
break;
}
}
void CTInputStreamAppUi::MaiscOpenComplete(TInt aError)
{
if (aError != KErrNone)
{
TBuf<32> buf(_L("Input err "));
buf.AppendNum(aError);
iAppView->AddMessage(buf);
}
OpenCompleteL(aError);
User::InfoPrint( _L( "Open Complete" ) );
}
void CTInputStreamAppUi::MaiscBufferCopied(TInt aError, const TDesC8& aBuffer)
{
TDes8* buffer = new(ELeave) TBuf8<KBufferSize>;
buffer->Trim();
//buffer->Append('v');
TBuf<32> buf(_L("Buf "));
if (&aBuffer == iBuffer[0])
buf.AppendNum(1);
else if (&aBuffer == iBuffer[1])
buf.AppendNum(2);
else if (&aBuffer == iBuffer[2])
{
buf.AppendNum(3);
iState = EOpen;
}
buf.Append(_L(" full, err "));
buf.AppendNum(aError);
iAppView->AddMessage(buf);
iConverter->BufferWriteL(aBuffer,*buffer);
User::LeaveIfError(aError);
}
void CTInputStreamAppUi::MaiscRecordComplete(TInt aError)
{
iState = EOpen;
TBuf<32> buf(_L("Record complete, err "));
file.Flush();
file.Close();
fs.Close();
iAppView->AddMessage(_L("File Closed"));
buf.AppendNum(aError);
iAppView->AddMessage(buf);
User::LeaveIfError(aError);
}
void CTInputStreamAppUi::MaoscOpenComplete(TInt aError)
{
if (aError != KErrNone)
{
TBuf<32> buf(_L("Output err "));
buf.AppendNum(aError);
iAppView->AddMessage(buf);
}
OpenCompleteL(aError);
}
void CTInputStreamAppUi::MaoscBufferCopied(TInt aError, const TDesC8& aBuffer)
{
TBuf<32> buf(_L("Buf "));
if (&aBuffer == iBuffer[0])
buf.AppendNum(1);
else if (&aBuffer == iBuffer[1])
buf.AppendNum(2);
else if (&aBuffer == iBuffer[2])
buf.AppendNum(3);
buf.Append(_L(" played, err "));
buf.AppendNum(aError);
iAppView->AddMessage(buf);
User::LeaveIfError(aError);
}
void CTInputStreamAppUi::MaoscPlayComplete(TInt aError)
{
iState = EOpen;
if (aError == KErrUnderflow)
aError = KErrNone;
TBuf<32> buf(_L("Play complete, err "));
buf.AppendNum(aError);
iAppView->AddMessage(buf);
User::LeaveIfError(aError);
}
void CTInputStreamAppUi::OpenCompleteL(TInt aError)
{
User::LeaveIfError(aError);
if (iState == EClosed)
{
iState = EFirstOpenSuccessful;
}
else
{
iState = EOpen;
iAppView->AddMessage(_L("Ready"));
iInput->SetGain(iInput->MaxGain());
iOutput->SetVolume(iOutput->MaxVolume() / 2);
}
}
void CTInputStreamAppUi::RecordL()
{
User::LeaveIfError(fs.Connect());
//CleanupClosePushL(file);
file.Replace(fs,KRecorderFile, EFileShareAny | EFileStream | EFileWrite);
if (iState != EOpen)
User::Leave(KErrNotReady);
iState = ERecording;
iAppView->AddMessage(_L("File Opened"));
for (TInt index = 0; index < iBuffer.Count(); index++)
{
iInput->ReadL(*iBuffer[index]);
}
}
void CTInputStreamAppUi::PlayL()
{
if (iState != EOpen)
User::Leave(KErrNotReady);
iState = EPlaying;
for (TInt index = 0; index < iBuffer.Count(); index++)
{
iOutput->WriteL(*iBuffer[index]);
}
}
//
// CTInputStreamApp
//
TUid CTInputStreamApp::AppDllUid() const
{
return KUidMdaAudioInputStreamingTestApp;
}
CApaDocument* CTInputStreamApp::CreateDocumentL()
{
return new(ELeave) CTInputStreamDocument(*this);
}
//
// CTInputStreamDocument
//
CTInputStreamDocument::CTInputStreamDocument(CEikApplication& aApp)
: CAknDocument(aApp)
{}
CEikAppUi* CTInputStreamDocument::CreateAppUiL()
{
return new (ELeave) CTInputStreamAppUi;
}
EXPORT_C CApaApplication* NewApplication()
{
return new CTInputStreamApp;
}
GLDEF_C TInt E32Dll(TDllReason)
{
return KErrNone;
}
log_test::log_test()
{
}
void log_test::ConstructL()
{
iAudioConvertUtility = CMdaAudioConvertUtility::NewL(*this);
iDestType = new (ELeave) CMdaAudioType;
iSrcType = new (ELeave) CMdaAudioType;
iSrcType->iFormat = new TMdaRawAudioClipFormat;
iDestType->iFormat = new TMdaWavClipFormat;
iSettings.iChannels = 1; //TMdaAudioDataSettings::EChannelsMono;
iSettings.iSampleRate =8000; //TMdaAudioDataSettings::ESampleRate8000Hz;
iSettings1.iChannels = 1; //TMdaAudioDataSettings::EChannelsMono;
iSettings1.iSampleRate =8000; //TMdaAudioDataSettings::ESampleRate8000Hz;
iCodecSrc = new TMdaU8PcmRawAudioCodec; //(TMdaPcmWavCodec::E16BitPcm);
iCodec = new (ELeave) TMdaPcmWavCodec(TMdaPcmWavCodec::E8BitPcm); /*TMdaMulawWavCodec */
isrcdesc = NULL;
idesdesc = NULL;
};
void log_test::MoscoStateChangeEvent(CBase* aObject, TInt aPreviousState, TInt aCurrentState, TInt aErrorCode)
{
TTimeIntervalMicroSeconds temp(0);
if (aErrorCode == KErrNone)
{
TBuf<32> buf(_L("Input err "));
buf.AppendNum(aErrorCode);
file.Write(*(*idesdesc).iDes);
}
return;
}
log_test::~log_test()
{
if(iAudioConvertUtility)
delete iAudioConvertUtility;
if(isrcfrmt)
delete isrcfrmt;
if(itarfrmt)
delete itarfrmt;
}
void log_test::BufferWriteL(const TDesC8& aTemp1, TDes8& aTemp2)
{
aTemp2.TrimAll();
aTemp2.Zero();
//Temp1.TrimAll();
_LIT(FilenameSrc, "temp.wav");
//aTemp2.Append(aTemp1);
/*_LIT(FilenameDest, "\\system\\note1.wav"); */
if(isrcdesc)
delete isrcdesc;
if(idesdesc)
delete idesdesc;
isrcdesc = new(ELeave) TMdaDesClipLocation(aTemp1);
idesdesc = new(ELeave) TMdaDesClipLocation(aTemp2);
fileSrc=new TMdaFileClipLocation (FilenameSrc);
// fileDest= new TMdaFileClipLocation(FilenameDest); */
iAudioConvertUtility->OpenL(isrcdesc,idesdesc,iSrcType->iFormat,iDestType->iFormat,iCodecSrc,&iSettings1,iCodec,&iSettings);
//User::WaitForAnyRequest();
//iAudioConvertUtility->OpenL(FilenameSrc,FilenameDest); //,iDestType->iFormat,iCodec,&iSettings);
// delete isrcdesc;
// delete idesdesc;
}
Forum posts: 13
But this is not the first sound engine I am writing. Did some stuff long ago in good old MS-DOS times. So the MOD-format is not completely unfamiliar to me
hi guys!
having trouble finding much spare time, i didn't manage to get me some mod player / sound mixer going on my own. i am/was working on some simple games / demos.. so a nice sound player would really be cool..
you may wan't to check these out:
http://www.berlinfactor.com/Symbian.Section/BreakOut.html
http://www.berlinfactor.com/Symbian.Section/StarsTest.html
http://www.berlinfactor.com/Symbian.Section/UridiumDemo.html
would you be willing to share some of your code or libs?
any reply would really be appreciated!
regards,
TFDJ
visit www.berlinfactor.com and www.intensicode.de
Forum posts: 31
Just wanted to let you know I released my sound engine as a LIB. It is free for use in non-commercial applications.
http://symbian.jep.de
I am sorry there is no example project yet.
Have fun,
zeep
zeep
Forum posts: 2006
Btw, I grant you admin right on the sections "game" and "multimedia" section so that you can modify your articles on CrystalPop and audio streaming.
This may generate extra mails to you since you will be noticed of all changes in these sections. Let me know if this is a problem for you.
Cheers
Eric
Eric Bustarret
NewLC Founder & CEO / Professional Symbian OS Consultant
Forum posts: 31
Tnx, I will take the chance and update my multichannel tutorial.
Extra mails are no problem
zeep