📄 oggtremor.cpp
字号:
/*
* Copyright (c) 2003 L. H. Wilden.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// Platform settings
#include <OggOs.h>
// This file is for non PLUGIN_SYSTEM
#if !defined(PLUGIN_SYSTEM)
#ifdef __VC32__
#pragma warning( disable : 4244 ) // conversion from __int64 to unsigned int: Possible loss of data
#endif
#include "OggLog.h"
#include "OggTremor.h"
#include "TremorDecoder.h"
#ifdef MP3_SUPPORT
#include "MadDecoder.h"
#endif
#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 <charconv.h>
#include <hal.h>
#include "ivorbiscodec.h"
#include "ivorbisfile.h"
#include <OggPlay.rsg>
#if defined(MULTI_THREAD_PLAYBACK)
#ifdef __VC32__
#pragma warning( disable : 4355 ) // 'this' used in base member initializer list
#endif
#endif
// COggPlayback
COggPlayback::COggPlayback(COggMsgEnv* anEnv, MPlaybackObserver* anObserver)
#if defined(MULTI_THREAD_PLAYBACK)
: CAbsPlayback(anObserver), iEnv(anEnv), iSharedData(*this, iUIThread, iBufferingThread)
#else
: CAbsPlayback(anObserver), iEnv(anEnv)
#endif
{
#if !defined(MULTI_THREAD_PLAYBACK)
// These basic rates should be supported by all Symbian OS
// Higher quality might not be supported.
iSettings.iChannels = TMdaAudioDataSettings::EChannelsMono;
iSettings.iSampleRate= TMdaAudioDataSettings::ESampleRate8000Hz;
iSettings.iFlags = TMdaAudioDataSettings::ENoNetworkRouting;
#endif
}
void COggPlayback::ConstructL()
{
// Set up the session with the file server
User::LeaveIfError(iFs.Connect());
#if defined(MULTI_THREAD_PLAYBACK)
#if defined(SERIES60V3)
User::LeaveIfError(iFs.ShareAuto());
#else
User::LeaveIfError(iFs.Share());
#endif
User::LeaveIfError(AttachToFs());
#endif
#if defined(MULTI_THREAD_PLAYBACK)
// Create at least one audio buffer
iBuffer[0] = HBufC8::NewL(KBufferSize48K);
// Open thread handles to the UI thread and the buffering thread
// Currently the UI and buffering threads are the same (this thread)
TThreadId uiThreadId = iThread.Id();
User::LeaveIfError(iUIThread.Open(uiThreadId));
User::LeaveIfError(iBufferingThread.Open(uiThreadId));
// Create the streaming thread
User::LeaveIfError(iStreamingThread.Create(_L("OggPlayStream"), StreamingThread, KDefaultStackSize, NULL, &iSharedData));
// Create the streaming thread panic handler
iStreamingThreadPanicHandler = new(ELeave) CThreadPanicHandler(EPriorityHigh, iStreamingThread, *this);
// Create the streaming thread command handler
iStreamingThreadCommandHandler = new(ELeave) CStreamingThreadCommandHandler(iUIThread, iStreamingThread, *iStreamingThreadPanicHandler);
iSharedData.iStreamingThreadCommandHandler = iStreamingThreadCommandHandler;
// Create the streaming thread listener
iStreamingThreadListener = new(ELeave) CStreamingThreadListener(*this, iSharedData);
iSharedData.iStreamingThreadListener = iStreamingThreadListener;
// Launch the streaming thread
User::LeaveIfError(iStreamingThreadCommandHandler->ResumeCommandHandlerThread());
// Record the fact that the streaming thread started ok
iStreamingThreadRunning = ETrue;
// Create the buffering active object
iBufferingThreadAO = new(ELeave) CBufferingThreadAO(iSharedData);
iSharedData.iBufferingThreadAO = iBufferingThreadAO;
// Add it to the buffering threads active scheduler (this thread)
CActiveScheduler::Add(iBufferingThreadAO);
#else
iStream = CMdaAudioOutputStream::NewL(*this);
iStream->Open(&iSettings);
for (TInt i=0; i<KBuffers; i++)
iBuffer[i] = HBufC8::NewL(KBufferSize);
#endif
iStartAudioStreamingTimer = new (ELeave) COggTimer(TCallBack(StartAudioStreamingCallBack, this));
iRestartAudioStreamingTimer = new (ELeave) COggTimer(TCallBack(RestartAudioStreamingCallBack, this));
iStopAudioStreamingTimer = new (ELeave) COggTimer(TCallBack(StopAudioStreamingCallBack, this));
iOggSampleRateConverter = new (ELeave) COggSampleRateConverter;
// Read the device uid
HAL::Get(HALData::EMachineUid, iMachineUid);
TRACEF(COggLog::VA(_L("Phone UID: %x"), iMachineUid ));
}
COggPlayback::~COggPlayback()
{
__ASSERT_ALWAYS((iState <= EStopped), User::Panic(_L("~COggPlayback"), 0));
delete iDecoder;
delete iStartAudioStreamingTimer;
delete iRestartAudioStreamingTimer;
delete iStopAudioStreamingTimer;
delete iOggSampleRateConverter;
#if defined(MULTI_THREAD_PLAYBACK)
if (iBufferingThreadAO)
iBufferingThreadAO->Cancel();
if (iStreamingThreadListener)
iStreamingThreadListener->Cancel();
if (iStreamingThreadRunning)
iStreamingThreadCommandHandler->ShutdownCommandHandlerThread();
if (iStreamingThreadCommandHandler)
iStreamingThreadCommandHandler->Cancel();
if (iStreamingThreadPanicHandler)
iStreamingThreadPanicHandler->Cancel();
iThread.Close();
iUIThread.Close();
iBufferingThread.Close();
iStreamingThread.Close();
delete iBufferingThreadAO;
delete iStreamingThreadListener;
delete iStreamingThreadCommandHandler;
delete iStreamingThreadPanicHandler;
for (TInt i = 0 ; i<KMultiThreadBuffers ; i++)
delete iBuffer[i];
#else
delete iStream;
for (TInt i=0; i<KBuffers; i++)
delete iBuffer[i];
#endif
iFs.Close();
}
MDecoder* COggPlayback::GetDecoderL(const TDesC& aFileName)
{
MDecoder* decoder = NULL;
TParsePtrC p( aFileName);
if(p.Ext().Compare( _L(".ogg"))==0 || p.Ext().Compare( _L(".OGG"))==0) {
decoder=new(ELeave)CTremorDecoder(iFs);
}
#if defined(MP3_SUPPORT)
else if(p.Ext().Compare( _L(".mp3"))==0 || p.Ext().Compare( _L(".MP3"))==0) {
decoder=new(ELeave)CMadDecoder;
}
#endif
else {
_LIT(KPanic,"Panic:");
_LIT(KNotSupported,"File type not supported");
iEnv->OggErrorMsgL(KPanic,KNotSupported);
}
return decoder;
}
TInt COggPlayback::Open(const TDesC& aFileName)
{
TRACEF(COggLog::VA(_L("OPEN") ));
if (iFile)
{
iDecoder->Close();
iFile->Close();
delete iFile;
iFile = NULL;
delete iDecoder;
iDecoder = NULL;
}
iTime= 0;
if (aFileName.Length() == 0) {
//OGGLOG.Write(_L("Oggplay: Filenamelength is 0 (Error20 Error8"));
iEnv->OggErrorMsgL(R_OGG_ERROR_20,R_OGG_ERROR_8);
return -100;
}
iFile = new(ELeave) RFile;
if ((iFile->Open(iFs, aFileName, EFileShareReadersOnly)) != KErrNone)
{
delete iFile;
iFile = NULL;
//OGGLOG.Write(_L("Oggplay: File open returns 0 (Error20 Error14)"));
TRACE(COggLog::VA(_L("COggPlayback::Open(%S). Failed"), &aFileName ));
iEnv->OggErrorMsgL(R_OGG_ERROR_20, R_OGG_ERROR_14);
return KErrOggFileNotFound;
}
iDecoder = GetDecoderL(aFileName);
if(iDecoder->Open(iFile, aFileName) < 0)
{
iDecoder->Close();
iFile->Close();
delete iFile;
iFile = NULL;
delete iDecoder;
iDecoder = NULL;
//OGGLOG.Write(_L("Oggplay: ov_open not successful (Error20 Error9)"));
iEnv->OggErrorMsgL(R_OGG_ERROR_20,R_OGG_ERROR_9);
return -102;
}
iDecoder->ParseTags(iTitle, iArtist, iAlbum, iGenre, iTrackNumber);
iFileName= aFileName;
iRate= iDecoder->Rate();
iChannels=iDecoder->Channels();
iTime=iDecoder->TimeTotal();
iBitRate=iDecoder->Bitrate();
iFileSize= iDecoder->FileSize();
TInt err= SetAudioCaps(iDecoder->Channels(), iDecoder->Rate());
if (err == KErrNone)
iState= EStopped;
else
{
iDecoder->Close();
iFile->Close();
delete iFile;
iFile = NULL;
delete iDecoder;
iDecoder = NULL;
}
return err;
}
TBool COggPlayback::GetNextLowerRate(TInt& usedRate, TMdaAudioDataSettings::TAudioCaps& rt)
{
TBool retValue = ETrue;
switch (usedRate)
{
case 48000:
usedRate = 44100;
rt = TMdaAudioDataSettings::ESampleRate44100Hz;
break;
case 44100:
usedRate = 32000;
rt = TMdaAudioDataSettings::ESampleRate32000Hz;
break;
case 32000:
usedRate = 22050;
rt = TMdaAudioDataSettings::ESampleRate22050Hz;
break;
case 22050:
usedRate = 16000;
rt = TMdaAudioDataSettings::ESampleRate16000Hz;
break;
case 16000:
usedRate = 11025;
rt = TMdaAudioDataSettings::ESampleRate11025Hz;
break;
case 11025:
usedRate = 8000;
rt = TMdaAudioDataSettings::ESampleRate8000Hz;
break;
default:
retValue = EFalse;
break;
}
return retValue;
}
TInt COggPlayback::SetAudioCaps(TInt theChannels, TInt theRate)
{
TMdaAudioDataSettings::TAudioCaps ac;
TMdaAudioDataSettings::TAudioCaps rt;
TBool convertChannel = EFalse;
TBool convertRate = EFalse;
TInt usedRate = theRate;
if (theRate==8000) rt= TMdaAudioDataSettings::ESampleRate8000Hz;
else if (theRate==11025) rt= TMdaAudioDataSettings::ESampleRate11025Hz;
else if (theRate==16000) rt= TMdaAudioDataSettings::ESampleRate16000Hz;
else if (theRate==22050) rt= TMdaAudioDataSettings::ESampleRate22050Hz;
else if (theRate==32000) rt= TMdaAudioDataSettings::ESampleRate32000Hz;
else if (theRate==44100) rt= TMdaAudioDataSettings::ESampleRate44100Hz;
else if (theRate==48000) rt= TMdaAudioDataSettings::ESampleRate48000Hz;
else
{
// Rate not supported by the phone
TRACEF(COggLog::VA(_L("SetAudioCaps: Non standard rate: %d"), theRate));
// Convert to nearest rate
convertRate = ETrue;
if (theRate>48000)
{
usedRate = 48000;
rt = TMdaAudioDataSettings::ESampleRate48000Hz;
}
else if (theRate>44100)
{
usedRate = 44100;
rt = TMdaAudioDataSettings::ESampleRate44100Hz;
}
else if (theRate>32000)
{
usedRate = 32000;
rt = TMdaAudioDataSettings::ESampleRate32000Hz;
}
else if (theRate>22050)
{
usedRate = 22050;
rt = TMdaAudioDataSettings::ESampleRate22050Hz;
}
else if (theRate>16000)
{
usedRate = 16000;
rt = TMdaAudioDataSettings::ESampleRate16000Hz;
}
else if (theRate>11025)
{
usedRate = 11025;
rt = TMdaAudioDataSettings::ESampleRate11025Hz;
}
else if (theRate>8000)
{
usedRate = 8000;
rt = TMdaAudioDataSettings::ESampleRate8000Hz;
}
else
{
// Frequencies less than 8KHz are not supported
iEnv->OggErrorMsgL(R_OGG_ERROR_20, R_OGG_ERROR_12);
return KErrNotSupported;
}
}
TInt usedChannels = theChannels;
if (usedChannels == 1)
ac = TMdaAudioDataSettings::EChannelsMono;
else if (usedChannels == 2)
ac = TMdaAudioDataSettings::EChannelsStereo;
else
{
iEnv->OggErrorMsgL(R_OGG_ERROR_12, R_OGG_ERROR_10);
return KErrNotSupported;
}
// Note current settings
TInt bestRate = usedRate;
TInt convertingRate = convertRate;
TMdaAudioDataSettings::TAudioCaps bestRT = rt;
// Try the current settings.
// Adjust sample rate and channels if necessary
TInt err = KErrNotSupported;
while (err == KErrNotSupported)
{
#if defined(MULTI_THREAD_PLAYBACK)
err = SetAudioProperties(usedRate, usedChannels);
#else
TRAP(err, iStream->SetAudioPropertiesL(rt, ac));
#endif
if (err == KErrNotSupported)
{
// Frequency is not supported
// Try dropping the frequency
convertRate = GetNextLowerRate(usedRate, rt);
// If that doesn't work, try changing stereo to mono
if (!convertRate && (usedChannels == 2))
{
// Reset the sample rate
convertRate = convertingRate;
usedRate = bestRate;
rt = bestRT;
// Drop channels to 1
usedChannels = 1;
ac = TMdaAudioDataSettings::EChannelsMono;
convertChannel = ETrue;
}
else if (!convertRate)
break; // Give up, nothing supported :-(
}
}
TBuf<256> buf,tbuf;
if (err != KErrNone)
{
CEikonEnv::Static()->ReadResource(buf, R_OGG_ERROR_16);
CEikonEnv::Static()->ReadResource(tbuf, R_OGG_ERROR_12);
buf.AppendNum(err);
iEnv->OggErrorMsgL(tbuf,buf);
return err;
}
// Trace the settings
TRACEF(COggLog::VA(_L("SetAudioCaps: theRate: %d, theChannels: %d, usedRate: %d, usedChannels: %d"), theRate, theChannels, usedRate, usedChannels));
if ((convertRate || convertChannel) && iEnv->WarningsEnabled())
{
// Display a warning message
SamplingRateSupportedMessage(convertRate, theRate, convertChannel, theChannels);
// Put the audio properties back the way they were
#if defined(MULTI_THREAD_PLAYBACK)
err = SetAudioProperties(usedRate, usedChannels);
#else
TRAP(err, iStream->SetAudioPropertiesL(rt, ac));
#endif
// Check that it worked (it should have)
if (err != KErrNone)
{
CEikonEnv::Static()->ReadResource(buf, R_OGG_ERROR_16);
CEikonEnv::Static()->ReadResource(tbuf, R_OGG_ERROR_12);
buf.AppendNum(err);
iEnv->OggErrorMsgL(tbuf,buf);
return err;
}
}
// Determine buffer sizes so that we make approx 10-15 calls to the mediaserver per second
// + another 10-15 calls for position if the frequency analyzer is on screen
TInt bufferSize = 0;
switch (usedRate)
{
case 48000:
case 44100:
bufferSize = KBufferSize48K;
break;
case 32000:
bufferSize = KBufferSize32K;
break;
case 22050:
bufferSize = KBufferSize22K;
break;
case 16000:
bufferSize = KBufferSize16K;
break;
case 11025:
case 8000:
bufferSize = KBufferSize11K;
break;
default:
User::Panic(_L("COggPlayback:SAC"), 0);
break;
}
if (usedChannels == 1)
bufferSize /= 2;
iOggSampleRateConverter->Init(this, bufferSize, bufferSize-1024, theRate, usedRate, theChannels, usedChannels);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -