Changed: #1030 Implement music streaming in OpenAL driver
This commit is contained in:
parent
5950b1f5bf
commit
163e2dc1a5
7 changed files with 436 additions and 10 deletions
|
@ -94,19 +94,22 @@ public:
|
|||
virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) =0;
|
||||
|
||||
/// Get the amount of channels (2 is stereo) in output.
|
||||
virtual uint16 getChannels() =0;
|
||||
virtual uint8 getChannels() =0;
|
||||
|
||||
/// Get the samples per second (often 44100) in output.
|
||||
virtual uint32 getSamplesPerSec() =0;
|
||||
|
||||
/// Get the bits per sample (often 16) in output.
|
||||
virtual uint16 getBitsPerSample() =0;
|
||||
virtual uint8 getBitsPerSample() =0;
|
||||
|
||||
/// Get if the music has ended playing (never true if loop).
|
||||
virtual bool isMusicEnded() =0;
|
||||
|
||||
/// Get the total time in seconds.
|
||||
virtual float getLength() =0;
|
||||
|
||||
/// Get the size of uncompressed data in bytes.
|
||||
virtual uint getUncompressedSize() =0;
|
||||
}; /* class IMusicBuffer */
|
||||
|
||||
} /* namespace NLSOUND */
|
||||
|
|
|
@ -75,19 +75,22 @@ public:
|
|||
virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum);
|
||||
|
||||
/// Get the amount of channels (2 is stereo) in output.
|
||||
virtual uint16 getChannels();
|
||||
virtual uint8 getChannels();
|
||||
|
||||
/// Get the samples per second (often 44100) in output.
|
||||
virtual uint32 getSamplesPerSec();
|
||||
|
||||
/// Get the bits per sample (often 16) in output.
|
||||
virtual uint16 getBitsPerSample();
|
||||
virtual uint8 getBitsPerSample();
|
||||
|
||||
/// Get if the music has ended playing (never true if loop).
|
||||
virtual bool isMusicEnded();
|
||||
|
||||
/// Get the total time in seconds.
|
||||
virtual float getLength();
|
||||
|
||||
/// Get the size of uncompressed data in bytes.
|
||||
virtual uint getUncompressedSize();
|
||||
}; /* class CMusicBufferVorbis */
|
||||
|
||||
} /* namespace NLSOUND */
|
||||
|
|
|
@ -158,7 +158,7 @@ uint32 CMusicBufferVorbis::getNextBytes(uint8 *buffer, uint32 minimum, uint32 ma
|
|||
return bytes_read;
|
||||
}
|
||||
|
||||
uint16 CMusicBufferVorbis::getChannels()
|
||||
uint8 CMusicBufferVorbis::getChannels()
|
||||
{
|
||||
vorbis_info *vi = ov_info(&_OggVorbisFile, -1);
|
||||
return (uint16)vi->channels;
|
||||
|
@ -170,7 +170,7 @@ uint32 CMusicBufferVorbis::getSamplesPerSec()
|
|||
return vi->rate;
|
||||
}
|
||||
|
||||
uint16 CMusicBufferVorbis::getBitsPerSample()
|
||||
uint8 CMusicBufferVorbis::getBitsPerSample()
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
|
@ -185,6 +185,11 @@ float CMusicBufferVorbis::getLength()
|
|||
return (float)ov_time_total(&_OggVorbisFile, -1);
|
||||
}
|
||||
|
||||
uint CMusicBufferVorbis::getUncompressedSize()
|
||||
{
|
||||
return (uint)ov_pcm_total(&_OggVorbisFile, -1) * (getBitsPerSample() / 2) * getChannels();
|
||||
}
|
||||
|
||||
} /* namespace NLSOUND */
|
||||
|
||||
/* end of file */
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="OpenAL32.lib EFX-Util.lib"
|
||||
OutputFile="..\..\..\..\lib\nel_drv_openal_win_d.dll"
|
||||
IgnoreDefaultLibraryNames="msvcrt.lib;libcmt.lib"
|
||||
IgnoreDefaultLibraryNames="msvcrt.lib"
|
||||
ModuleDefinitionFile="$(ProjectName).def"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
|
@ -159,7 +159,7 @@
|
|||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="OpenAL32.lib EFX-Util.lib"
|
||||
OutputFile="..\..\..\..\lib64\nel_drv_openal_win_d.dll"
|
||||
IgnoreDefaultLibraryNames="msvcrt.lib;libcmt.lib"
|
||||
IgnoreDefaultLibraryNames="msvcrt.lib"
|
||||
ModuleDefinitionFile="$(ProjectName).def"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
|
@ -247,7 +247,7 @@
|
|||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="OpenAL32.lib EFX-Util.lib"
|
||||
OutputFile="..\..\..\..\lib\nel_drv_openal_win_r.dll"
|
||||
IgnoreDefaultLibraryNames="libcmt.lib"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
ModuleDefinitionFile="$(ProjectName).def"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
|
@ -336,7 +336,7 @@
|
|||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="OpenAL32.lib EFX-Util.lib"
|
||||
OutputFile="..\..\..\..\lib64\nel_drv_openal_win_r.dll"
|
||||
IgnoreDefaultLibraryNames="libcmt.lib"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
ModuleDefinitionFile="$(ProjectName).def"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
|
@ -401,6 +401,14 @@
|
|||
RelativePath=".\listener_al.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\music_channel_al.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\music_channel_al.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\source_al.cpp"
|
||||
>
|
||||
|
|
303
code/nel/src/sound/driver/openal/music_channel_al.cpp
Normal file
303
code/nel/src/sound/driver/openal/music_channel_al.cpp
Normal file
|
@ -0,0 +1,303 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2010 Winch Gate Property Limited
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "stdopenal.h"
|
||||
|
||||
// Project includes
|
||||
#include "sound_driver_al.h"
|
||||
#include "source_al.h"
|
||||
#include "buffer_al.h"
|
||||
#include "music_channel_al.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace NLMISC;
|
||||
|
||||
namespace NLSOUND
|
||||
{
|
||||
|
||||
CMusicChannelAL::CMusicChannelAL(CSoundDriverAL *soundDriver)
|
||||
: _MusicBuffer(NULL), _SoundDriver(soundDriver), _Gain(1.0), _Source(NULL), _Thread(NULL), _Async(false), _Playing(false), _Buffer(NULL)
|
||||
{
|
||||
// create a default source for music streaming
|
||||
_Source = static_cast<CSourceAL*>(_SoundDriver->createSource());
|
||||
_Source->setPos(CVector(0, 0, 0));
|
||||
_Source->setVelocity(CVector(0, 0, 0));
|
||||
_Source->setDirection(CVector(0, 0, 0));
|
||||
_Source->setSourceRelativeMode(true);
|
||||
_Source->setStreamingBuffersMax(4);
|
||||
_Source->setStreamingBufferSize(32768);
|
||||
// _Source->setStreaming(true);
|
||||
}
|
||||
|
||||
CMusicChannelAL::~CMusicChannelAL()
|
||||
{
|
||||
release();
|
||||
if (_SoundDriver) { _SoundDriver->removeMusicChannel(this); _SoundDriver = NULL; }
|
||||
}
|
||||
|
||||
void CMusicChannelAL::release()
|
||||
{
|
||||
// stop thread before deleting it
|
||||
stop();
|
||||
|
||||
// delete thread
|
||||
if (_Thread)
|
||||
{
|
||||
delete _Thread;
|
||||
_Thread = NULL;
|
||||
}
|
||||
|
||||
// delete source
|
||||
if (_Source)
|
||||
{
|
||||
delete _Source;
|
||||
_Source = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill IBuffer with data from IMusicBuffer
|
||||
bool CMusicChannelAL::fillBuffer(IBuffer *buffer, uint length)
|
||||
{
|
||||
if (!buffer || !length)
|
||||
{
|
||||
nlwarning("AL: No data to stream");
|
||||
return false;
|
||||
}
|
||||
|
||||
// fill buffer with music data
|
||||
uint8 *tmp = buffer->lock(length);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
nlwarning("AL: Can't allocate %u bytes for buffer", length);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 size = _MusicBuffer->getNextBytes(tmp, length, length);
|
||||
buffer->unlock(size);
|
||||
|
||||
// add buffer to streaming buffers queue
|
||||
_Source->submitStreamingBuffer(buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Use buffer format from IMusicBuffer
|
||||
void CMusicChannelAL::setBufferFormat(IBuffer *buffer)
|
||||
{
|
||||
if (!buffer)
|
||||
{
|
||||
nlwarning("AL: No buffer specified");
|
||||
return;
|
||||
}
|
||||
|
||||
// use the same format as music for buffers
|
||||
buffer->setFormat(IBuffer::FormatPcm, _MusicBuffer->getChannels(),
|
||||
_MusicBuffer->getBitsPerSample(), _MusicBuffer->getSamplesPerSec());
|
||||
}
|
||||
|
||||
void CMusicChannelAL::run()
|
||||
{
|
||||
|
||||
if (_Async)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
// use queued buffers
|
||||
do
|
||||
{
|
||||
// buffers to update
|
||||
std::vector<CBufferAL*> buffers;
|
||||
|
||||
if (first)
|
||||
{
|
||||
// get all buffers to queue
|
||||
_Source->getStreamingBuffers(buffers);
|
||||
|
||||
// set format for each buffer
|
||||
for(uint i = 0; i < buffers.size(); ++i)
|
||||
setBufferFormat(buffers[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// get unqueued buffers
|
||||
_Source->getProcessedStreamingBuffers(buffers);
|
||||
}
|
||||
|
||||
// fill buffers
|
||||
for(uint i = 0; i < buffers.size(); ++i)
|
||||
fillBuffer(buffers[i], _Source->getStreamingBufferSize());
|
||||
|
||||
// play the source
|
||||
if (first)
|
||||
{
|
||||
_Source->play();
|
||||
first = false;
|
||||
}
|
||||
|
||||
// wait 100ms before rechecking buffers
|
||||
nlSleep(100);
|
||||
}
|
||||
while(!_MusicBuffer->isMusicEnded() && _Playing);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use an unique buffer managed by CMusicChannelAL
|
||||
_Buffer = _SoundDriver->createBuffer();
|
||||
|
||||
// set format
|
||||
setBufferFormat(_Buffer);
|
||||
|
||||
// fill data
|
||||
fillBuffer(_Buffer, _MusicBuffer->getUncompressedSize());
|
||||
|
||||
// we don't need _MusicBuffer anymore because all is loaded into memory
|
||||
if (_MusicBuffer)
|
||||
{
|
||||
delete _MusicBuffer;
|
||||
_MusicBuffer = NULL;
|
||||
}
|
||||
|
||||
// use this buffer as source
|
||||
_Source->setStaticBuffer(_Buffer);
|
||||
|
||||
// play the source
|
||||
_Source->play();
|
||||
}
|
||||
|
||||
// music finished without interruption
|
||||
if (_Playing)
|
||||
{
|
||||
// wait until source is not playing
|
||||
while(_Source->isPlaying() && _Playing) nlSleep(1000);
|
||||
|
||||
_Source->stop();
|
||||
|
||||
_Playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Play some music (.ogg etc...)
|
||||
* NB: if an old music was played, it is first stop with stopMusic()
|
||||
* \param filepath file path, CPath::lookup is done here
|
||||
* \param async stream music from hard disk, preload in memory if false
|
||||
* \param loop must be true to play the music in loop.
|
||||
*/
|
||||
bool CMusicChannelAL::play(const std::string &filepath, bool async, bool loop)
|
||||
{
|
||||
// stop a previous music
|
||||
stop();
|
||||
|
||||
// when not using async, we must load the whole file once
|
||||
_MusicBuffer = IMusicBuffer::createMusicBuffer(filepath, async, async ? loop:false);
|
||||
|
||||
if (_MusicBuffer)
|
||||
{
|
||||
// create the thread if it's not yet created
|
||||
if (!_Thread) _Thread = IThread::create(this);
|
||||
|
||||
if (!_Thread)
|
||||
{
|
||||
nlwarning("AL: Can't create a new thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
_Async = async;
|
||||
_Playing = true;
|
||||
|
||||
// we need to loop the source only if not async
|
||||
_Source->setLooping(async ? false:loop);
|
||||
|
||||
// start the thread
|
||||
_Thread->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
nlwarning("AL: Can't stream file %s", filepath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Stop the music previously loaded and played (the Memory is also freed)
|
||||
void CMusicChannelAL::stop()
|
||||
{
|
||||
_Playing = false;
|
||||
|
||||
_Source->stop();
|
||||
|
||||
// if not using async streaming, we manage static buffer ourself
|
||||
if (!_Async && _Buffer)
|
||||
{
|
||||
_Source->setStaticBuffer(NULL);
|
||||
delete _Buffer;
|
||||
_Buffer = NULL;
|
||||
}
|
||||
|
||||
// wait until thread is finished
|
||||
if (_Thread)
|
||||
_Thread->wait();
|
||||
|
||||
if (_MusicBuffer)
|
||||
{
|
||||
delete _MusicBuffer;
|
||||
_MusicBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// Pause the music previously loaded and played (the Memory is not freed)
|
||||
void CMusicChannelAL::pause()
|
||||
{
|
||||
_Source->pause();
|
||||
}
|
||||
|
||||
/// Resume the music previously paused
|
||||
void CMusicChannelAL::resume()
|
||||
{
|
||||
_Source->play();
|
||||
}
|
||||
|
||||
/// Return true if a song is finished.
|
||||
bool CMusicChannelAL::isEnded()
|
||||
{
|
||||
return !_Playing;
|
||||
}
|
||||
|
||||
/// Return true if the song is still loading asynchronously and hasn't started playing yet (false if not async), used to delay fading
|
||||
bool CMusicChannelAL::isLoadingAsync()
|
||||
{
|
||||
return _Async && _Playing;
|
||||
}
|
||||
|
||||
/// Return the total length (in second) of the music currently played
|
||||
float CMusicChannelAL::getLength()
|
||||
{
|
||||
if (_MusicBuffer) return _MusicBuffer->getLength();
|
||||
else return .0f;
|
||||
}
|
||||
|
||||
/** Set the music volume (if any music played). (volume value inside [0 , 1]) (default: 1)
|
||||
* NB: in OpenAL driver, the volume of music IS affected by IListener::setGain()
|
||||
*/
|
||||
void CMusicChannelAL::setVolume(float gain)
|
||||
{
|
||||
_Gain = gain;
|
||||
_Source->setGain(gain);
|
||||
}
|
||||
|
||||
} /* namespace NLSOUND */
|
||||
|
||||
/* end of file */
|
100
code/nel/src/sound/driver/openal/music_channel_al.h
Normal file
100
code/nel/src/sound/driver/openal/music_channel_al.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||
// Copyright (C) 2010 Winch Gate Property Limited
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef NLSOUND_MUSIC_CHANNEL_AL_H
|
||||
#define NLSOUND_MUSIC_CHANNEL_AL_H
|
||||
|
||||
#include "nel/sound/driver/music_channel.h"
|
||||
|
||||
namespace NLSOUND
|
||||
{
|
||||
class CSoundDriverAL;
|
||||
class IMusicBuffer;
|
||||
|
||||
/**
|
||||
* \brief CMusicChannelAL
|
||||
* \date 2010-07-27 16:56GMT
|
||||
* \author Kervala
|
||||
* CMusicChannelAL is an implementation of the IMusicChannel interface to run on OpenAL.
|
||||
*/
|
||||
class CMusicChannelAL : public IMusicChannel, public NLMISC::IRunnable
|
||||
{
|
||||
protected:
|
||||
// outside pointers
|
||||
CSoundDriverAL* _SoundDriver;
|
||||
|
||||
// pointers
|
||||
IMusicBuffer* _MusicBuffer;
|
||||
NLMISC::IThread* _Thread;
|
||||
|
||||
IBuffer* _Buffer;
|
||||
CSourceAL* _Source;
|
||||
bool _Playing;
|
||||
bool _Async;
|
||||
|
||||
float _Gain;
|
||||
|
||||
/// Fill IBuffer with data from IMusicBuffer
|
||||
bool fillBuffer(IBuffer *buffer, uint length);
|
||||
|
||||
/// Use buffer format from IMusicBuffer
|
||||
void setBufferFormat(IBuffer *buffer);
|
||||
|
||||
/// Declared in NLMISC::IRunnable interface
|
||||
virtual void run();
|
||||
|
||||
public:
|
||||
CMusicChannelAL(CSoundDriverAL *soundDriver);
|
||||
virtual ~CMusicChannelAL();
|
||||
void release();
|
||||
|
||||
/** Play some music (.ogg etc...)
|
||||
* NB: if an old music was played, it is first stop with stopMusic()
|
||||
* \param filepath file path, CPath::lookup is done here
|
||||
* \param async stream music from hard disk, preload in memory if false
|
||||
* \param loop must be true to play the music in loop.
|
||||
*/
|
||||
virtual bool play(const std::string &filepath, bool async, bool loop);
|
||||
|
||||
/// Stop the music previously loaded and played (the Memory is also freed)
|
||||
virtual void stop();
|
||||
|
||||
/// Pause the music previously loaded and played (the Memory is not freed)
|
||||
virtual void pause();
|
||||
|
||||
/// Resume the music previously paused
|
||||
virtual void resume();
|
||||
|
||||
/// Return true if a song is finished.
|
||||
virtual bool isEnded();
|
||||
|
||||
/// Return true if the song is still loading asynchronously and hasn't started playing yet (false if not async), used to delay fading
|
||||
virtual bool isLoadingAsync();
|
||||
|
||||
/// Return the total length (in second) of the music currently played
|
||||
virtual float getLength();
|
||||
|
||||
/** Set the music volume (if any music played). (volume value inside [0 , 1]) (default: 1)
|
||||
* NB: in OpenAL driver, the volume of music IS affected by IListener::setGain()
|
||||
*/
|
||||
virtual void setVolume(float gain);
|
||||
}; /* class CMusicChannelAL */
|
||||
|
||||
} /* namespace NLSOUND */
|
||||
|
||||
#endif /* #ifndef NLSOUND_MUSIC_CHANNEL_AL_H */
|
||||
|
||||
/* end of file */
|
|
@ -50,10 +50,14 @@
|
|||
#include "nel/misc/fast_mem.h"
|
||||
#include "nel/misc/path.h"
|
||||
#include "nel/misc/dynloadlib.h"
|
||||
#include "nel/misc/hierarchical_timer.h"
|
||||
#include "nel/misc/thread.h"
|
||||
|
||||
#include "nel/sound/driver/sound_driver.h"
|
||||
#include "nel/sound/driver/buffer.h"
|
||||
#include "nel/sound/driver/source.h"
|
||||
#include "nel/sound/driver/listener.h"
|
||||
#include "nel/sound/driver/effect.h"
|
||||
#include "nel/sound/driver/music_buffer.h"
|
||||
|
||||
/* end of file */
|
||||
|
|
Loading…
Reference in a new issue