From b02784f55f76ae894b5170740e4a509fbb681a85 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 11 Apr 2012 11:52:28 +0200 Subject: [PATCH] Moved: #795 CAudioDecoder from stream ogg vorbis sample to nlsound --- code/nel/include/nel/sound/audio_decoder.h | 104 +++++ .../include/nel/sound/audio_decoder_vorbis.h | 108 +++++ .../stream_ogg_vorbis/stream_ogg_vorbis.cpp | 394 +----------------- code/nel/src/sound/CMakeLists.txt | 6 + code/nel/src/sound/audio_decoder.cpp | 139 ++++++ code/nel/src/sound/audio_decoder_vorbis.cpp | 224 ++++++++++ 6 files changed, 582 insertions(+), 393 deletions(-) create mode 100644 code/nel/include/nel/sound/audio_decoder.h create mode 100644 code/nel/include/nel/sound/audio_decoder_vorbis.h create mode 100644 code/nel/src/sound/audio_decoder.cpp create mode 100644 code/nel/src/sound/audio_decoder_vorbis.cpp diff --git a/code/nel/include/nel/sound/audio_decoder.h b/code/nel/include/nel/sound/audio_decoder.h new file mode 100644 index 000000000..bded79cfe --- /dev/null +++ b/code/nel/include/nel/sound/audio_decoder.h @@ -0,0 +1,104 @@ +/** + * \file audio_decoder.h + * \brief IAudioDecoder + * \date 2012-04-11 09:34GMT + * \author Jan Boon (Kaetemi) + * IAudioDecoder + */ + +/* + * Copyright (C) 2008-2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE 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. + * + * RYZOM CORE 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 RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_AUDIO_DECODER_H +#define NLSOUND_AUDIO_DECODER_H +#include + +// STL includes + +// NeL includes + +// Project includes + +namespace NLSOUND { + +/** + * \brief IAudioDecoder + * \date 2008-08-30 11:38GMT + * \author Jan Boon (Kaetemi) + * IAudioDecoder is only used by the driver implementation to stream + * music files into a readable format (it's a simple decoder interface). + * You should not call these functions (getSongTitle) on nlsound or user level, + * as a driver might have additional music types implemented. + * TODO: Split IAudioDecoder into IAudioDecoder (actual decoding) and IMediaDemuxer (stream splitter), and change the interface to make more sense. + * TODO: Allow user application to register more decoders. + * TODO: Look into libavcodec for decoding audio? + */ +class IAudioDecoder +{ +private: + // pointers + /// Stream from file created by IAudioDecoder + NLMISC::IStream *_InternalStream; + +public: + IAudioDecoder(); + virtual ~IAudioDecoder(); + + /// Create a new music buffer, may return NULL if unknown type, destroy with delete. Filepath lookup done here. If async is true, it will stream from hd, else it will load in memory first. + static IAudioDecoder *createAudioDecoder(const std::string &filepath, bool async, bool loop); + + /// Create a new music buffer from a stream, type is file extension like "ogg" etc. + static IAudioDecoder *createAudioDecoder(const std::string &type, NLMISC::IStream *stream, bool loop); + + /// Get information on a music file (only artist and title at the moment). + static bool getInfo(const std::string &filepath, std::string &artist, std::string &title); + + /// Get audio/container extensions that are currently supported by the nel sound library. + static void getMusicExtensions(std::vector &extensions); + + /// Return if a music extension is supported by the nel sound library. + static bool isMusicExtensionSupported(const std::string &extension); + + /// Get how many bytes the music buffer requires for output minimum. + virtual uint32 getRequiredBytes() = 0; + + /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). + virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) = 0; + + /// Get the amount of channels (2 is stereo) in output. + virtual uint8 getChannels() = 0; + + /// Get the samples per second (often 44100) in output. + virtual uint getSamplesPerSec() = 0; + + /// Get the bits per sample (often 16) in output. + 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; +}; /* class IAudioDecoder */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_AUDIO_DECODER_H */ + +/* end of file */ diff --git a/code/nel/include/nel/sound/audio_decoder_vorbis.h b/code/nel/include/nel/sound/audio_decoder_vorbis.h new file mode 100644 index 000000000..c65c4de82 --- /dev/null +++ b/code/nel/include/nel/sound/audio_decoder_vorbis.h @@ -0,0 +1,108 @@ +/** + * \file audio_decoder_vorbis.h + * \brief CAudioDecoderVorbis + * \date 2012-04-11 09:35GMT + * \author Jan Boon (Kaetemi) + * CAudioDecoderVorbis + */ + +/* + * Copyright (C) 2008-2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE 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. + * + * RYZOM CORE 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 RYZOM CORE. If not, see + * . + */ + +#ifndef NLSOUND_AUDIO_DECODER_VORBIS_H +#define NLSOUND_AUDIO_DECODER_VORBIS_H +#include + +// STL includes + +// 3rd Party includes +#ifdef NL_OS_WINDOWS +# pragma warning( push ) +# pragma warning( disable : 4244 ) +#endif +#include +#ifdef NL_OS_WINDOWS +# pragma warning( pop ) +#endif + +// NeL includes +#include + +// Project includes + +namespace NLSOUND { + +/** + * \brief CAudioDecoderVorbis + * \date 2008-08-30 11:38GMT + * \author Jan Boon (Kaetemi) + * CAudioDecoderVorbis + * Create trough IAudioDecoder, type "ogg" + */ +class CAudioDecoderVorbis : public IAudioDecoder +{ +protected: + // outside pointers + NLMISC::IStream *_Stream; + + // pointers + + // instances + OggVorbis_File _OggVorbisFile; + bool _Loop; + bool _IsMusicEnded; + sint32 _StreamOffset; + sint32 _StreamSize; +public: + CAudioDecoderVorbis(NLMISC::IStream *stream, bool loop); + virtual ~CAudioDecoderVorbis(); + inline NLMISC::IStream *getStream() { return _Stream; } + inline sint32 getStreamSize() { return _StreamSize; } + inline sint32 getStreamOffset() { return _StreamOffset; } + + /// Get information on a music file (only artist and title at the moment). + static bool getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title); + + /// Get how many bytes the music buffer requires for output minimum. + virtual uint32 getRequiredBytes(); + + /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). + virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum); + + /// Get the amount of channels (2 is stereo) in output. + virtual uint8 getChannels(); + + /// Get the samples per second (often 44100) in output. + virtual uint getSamplesPerSec(); + + /// Get the bits per sample (often 16) in output. + 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(); +}; /* class CAudioDecoderVorbis */ + +} /* namespace NLSOUND */ + +#endif /* #ifndef NLSOUND_AUDIO_DECODER_VORBIS_H */ + +/* end of file */ diff --git a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp index 13cf936ef..c82589471 100644 --- a/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp +++ b/code/nel/samples/sound/stream_ogg_vorbis/stream_ogg_vorbis.cpp @@ -20,16 +20,6 @@ #include #include -// 3rd Party includes -#ifdef NL_OS_WINDOWS -# pragma warning( push ) -# pragma warning( disable : 4244 ) -#endif -#include -#ifdef NL_OS_WINDOWS -# pragma warning( pop ) -#endif - // NeL includes #include #include @@ -41,6 +31,7 @@ #include #include #include +#include #include // Project includes @@ -57,272 +48,6 @@ using namespace NLSOUND; namespace NLSAMPLE { -/** - * \brief IAudioDecoder - * \date 2008-08-30 11:38GMT - * \author Jan Boon (Kaetemi) - * IAudioDecoder is only used by the driver implementation to stream - * music files into a readable format (it's a simple decoder interface). - * You should not call these functions (getSongTitle) on nlsound or user level, - * as a driver might have additional music types implemented. - * TODO: Split IAudioDecoder into IAudioDecoder (actual decoding) and IMediaDemuxer (stream splitter), and change the interface to make more sense. - * TODO: Allow user application to register more decoders. - * TODO: Look into libavcodec for decoding audio? - */ -class IAudioDecoder -{ -private: - // pointers - /// Stream from file created by IAudioDecoder - NLMISC::IStream *_InternalStream; - -public: - IAudioDecoder(); - virtual ~IAudioDecoder(); - - /// Create a new music buffer, may return NULL if unknown type, destroy with delete. Filepath lookup done here. If async is true, it will stream from hd, else it will load in memory first. - static IAudioDecoder *createAudioDecoder(const std::string &filepath, bool async, bool loop); - - /// Create a new music buffer from a stream, type is file extension like "ogg" etc. - static IAudioDecoder *createAudioDecoder(const std::string &type, NLMISC::IStream *stream, bool loop); - - /// Get information on a music file (only artist and title at the moment). - static bool getInfo(const std::string &filepath, std::string &artist, std::string &title); - - /// Get audio/container extensions that are currently supported by the nel sound library. - static void getMusicExtensions(std::vector &extensions); - - /// Return if a music extension is supported by the nel sound library. - static bool isMusicExtensionSupported(const std::string &extension); - - /// Get how many bytes the music buffer requires for output minimum. - virtual uint32 getRequiredBytes() = 0; - - /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). - virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) = 0; - - /// Get the amount of channels (2 is stereo) in output. - virtual uint8 getChannels() = 0; - - /// Get the samples per second (often 44100) in output. - virtual uint getSamplesPerSec() = 0; - - /// Get the bits per sample (often 16) in output. - 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; -}; /* class IAudioDecoder */ - -/** - * \brief CAudioDecoderVorbis - * \date 2008-08-30 11:38GMT - * \author Jan Boon (Kaetemi) - * CAudioDecoderVorbis - * Create trough IAudioDecoder, type "ogg" - */ -class CAudioDecoderVorbis : public IAudioDecoder -{ -protected: - // outside pointers - NLMISC::IStream *_Stream; - - // pointers - - // instances - OggVorbis_File _OggVorbisFile; - bool _Loop; - bool _IsMusicEnded; - sint32 _StreamOffset; - sint32 _StreamSize; -public: - CAudioDecoderVorbis(NLMISC::IStream *stream, bool loop); - virtual ~CAudioDecoderVorbis(); - inline NLMISC::IStream *getStream() { return _Stream; } - inline sint32 getStreamSize() { return _StreamSize; } - inline sint32 getStreamOffset() { return _StreamOffset; } - - /// Get information on a music file (only artist and title at the moment). - static bool getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title); - - /// Get how many bytes the music buffer requires for output minimum. - virtual uint32 getRequiredBytes(); - - /// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end). - virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum); - - /// Get the amount of channels (2 is stereo) in output. - virtual uint8 getChannels(); - - /// Get the samples per second (often 44100) in output. - virtual uint getSamplesPerSec(); - - /// Get the bits per sample (often 16) in output. - 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(); -}; /* class CAudioDecoderVorbis */ - -IAudioDecoder::IAudioDecoder() : _InternalStream(NULL) -{ - -} - -IAudioDecoder::~IAudioDecoder() -{ - if (_InternalStream) { delete _InternalStream; _InternalStream = NULL; } -} - -IAudioDecoder *IAudioDecoder::createAudioDecoder(const std::string &filepath, bool async, bool loop) -{ - string lookup = CPath::lookup(filepath, false); - if (lookup.empty()) - { - nlwarning("Music file %s does not exist!", filepath.c_str()); - return NULL; - } - string type = CFile::getExtension(filepath); - - CIFile *ifile = new CIFile(); - ifile->setCacheFileOnOpen(!async); - ifile->allowBNPCacheFileOnOpen(!async); - ifile->open(lookup); - - IAudioDecoder *mb = createAudioDecoder(type, ifile, loop); - - if (mb) mb->_InternalStream = ifile; - else delete ifile; - - return mb; -} - -IAudioDecoder *IAudioDecoder::createAudioDecoder(const std::string &type, NLMISC::IStream *stream, bool loop) -{ - if (!stream) - { - nlwarning("Stream is NULL"); - return NULL; - } - string type_lower = toLower(type); - if (type_lower == "ogg") - { - return new CAudioDecoderVorbis(stream, loop); - } - else - { - nlwarning("Music file type unknown: '%s'", type_lower.c_str()); - return NULL; - } -} - -bool IAudioDecoder::getInfo(const std::string &filepath, std::string &artist, std::string &title) -{ - string lookup = CPath::lookup(filepath, false); - if (lookup.empty()) - { - nlwarning("Music file %s does not exist!", filepath.c_str()); - return false; - } - string type = CFile::getExtension(filepath); - string type_lower = toLower(type); - - if (type_lower == "ogg") - { - CIFile ifile; - ifile.setCacheFileOnOpen(false); - ifile.allowBNPCacheFileOnOpen(false); - ifile.open(lookup); - return CAudioDecoderVorbis::getInfo(&ifile, artist, title); - } - else - { - nlwarning("Music file type unknown: '%s'", type_lower.c_str()); - artist.clear(); title.clear(); - return false; - } -} - -/// Get audio/container extensions that are currently supported by the nel sound library. -void IAudioDecoder::getMusicExtensions(std::vector &extensions) -{ - extensions.push_back("ogg"); - // extensions.push_back("wav"); // TODO: Easy. -} - -/// Return if a music extension is supported by the nel sound library. -bool IAudioDecoder::isMusicExtensionSupported(const std::string &extension) -{ - return (extension == "ogg"); -} - -size_t vorbisReadFunc(void *ptr, size_t size, size_t nmemb, void *datasource) -{ - CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; - NLMISC::IStream *stream = audio_decoder_vorbis->getStream(); - nlassert(stream->isReading()); - sint32 length = (sint32)(size * nmemb); - if (length > audio_decoder_vorbis->getStreamSize() - stream->getPos()) - length = audio_decoder_vorbis->getStreamSize() - stream->getPos(); - stream->serialBuffer((uint8 *)ptr, length); - return length; -} - -int vorbisSeekFunc(void *datasource, ogg_int64_t offset, int whence) -{ - if (whence == SEEK_CUR && offset == 0) - { - // nlwarning(NLSOUND_XAUDIO2_PREFIX "This seek call doesn't do a damn thing, wtf."); - return 0; // ooookkaaaaaayyy - } - - CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; - - NLMISC::IStream::TSeekOrigin origin; - switch (whence) - { - case SEEK_SET: - origin = NLMISC::IStream::begin; - break; - case SEEK_CUR: - origin = NLMISC::IStream::current; - break; - case SEEK_END: - origin = NLMISC::IStream::end; - break; - default: - // nlwarning(NLSOUND_XAUDIO2_PREFIX "Seeking to fake origin."); - return -1; - } - - if (audio_decoder_vorbis->getStream()->seek(SEEK_SET ? audio_decoder_vorbis->getStreamOffset() + (sint32)offset : (sint32)offset, origin)) return 0; - else return -1; -} - -//int vorbisCloseFunc(void *datasource) -//{ -// //CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; -//} - -long vorbisTellFunc(void *datasource) -{ - CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; - return (long)(audio_decoder_vorbis->getStream()->getPos() - audio_decoder_vorbis->getStreamOffset()); -} - -static ov_callbacks OV_CALLBACKS_NLMISC_STREAM = { - (size_t (*)(void *, size_t, size_t, void *)) vorbisReadFunc, - (int (*)(void *, ogg_int64_t, int)) vorbisSeekFunc, - (int (*)(void *)) NULL, //vorbisCloseFunc, - (long (*)(void *)) vorbisTellFunc -}; - static UAudioMixer *s_AudioMixer = NULL; static UStreamSource *s_StreamSource = NULL; static IAudioDecoder *s_AudioDecoder = NULL; @@ -460,123 +185,6 @@ static void releaseSample() delete s_AudioMixer; s_AudioMixer = NULL; } -CAudioDecoderVorbis::CAudioDecoderVorbis(NLMISC::IStream *stream, bool loop) -: _Stream(stream), _Loop(loop), _StreamSize(0), _IsMusicEnded(false) -{ - _StreamOffset = stream->getPos(); - stream->seek(0, NLMISC::IStream::end); - _StreamSize = stream->getPos(); - stream->seek(_StreamOffset, NLMISC::IStream::begin); - ov_open_callbacks(this, &_OggVorbisFile, NULL, 0, OV_CALLBACKS_NLMISC_STREAM); -} - -CAudioDecoderVorbis::~CAudioDecoderVorbis() -{ - ov_clear(&_OggVorbisFile); -} - -/// Get information on a music file (only artist and title at the moment). -bool CAudioDecoderVorbis::getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title) -{ - CAudioDecoderVorbis mbv(stream, false); // just opens and closes the oggvorbisfile thing :) - vorbis_comment *vc = ov_comment(&mbv._OggVorbisFile, -1); - char *title_c = vorbis_comment_query(vc, "title", 0); - if (title_c) title = title_c; else title.clear(); - char *artist_c = vorbis_comment_query(vc, "artist", 0); - if (artist_c) artist = artist_c; else artist.clear(); - return true; -} - -uint32 CAudioDecoderVorbis::getRequiredBytes() -{ - return 0; // no minimum requirement of bytes to buffer out -} - -uint32 CAudioDecoderVorbis::getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) -{ - sint current_section = 0; // ??? - if (_IsMusicEnded) return 0; - nlassert(minimum <= maximum); // can't have this.. - uint32 bytes_read = 0; -#ifdef NL_BIG_ENDIAN - sint endianness = 1; -#else - sint endianness = 0; -#endif - do - { - // signed 16-bit or unsigned 8-bit little-endian samples - sint br = ov_read(&_OggVorbisFile, (char *)&buffer[bytes_read], maximum - bytes_read, - endianness, // Specifies big or little endian byte packing. 0 for little endian, 1 for b ig endian. Typical value is 0. - getBitsPerSample() == 8 ? 1 : 2, - getBitsPerSample() == 8 ? 0 : 1, // Signed or unsigned data. 0 for unsigned, 1 for signed. Typically 1. - ¤t_section); - // nlinfo(NLSOUND_XAUDIO2_PREFIX "current_section: %i", current_section); - if (br > 0) - { - bytes_read += (uint32)br; - } - else if (br == 0) // EOF - { - if (_Loop) - { - ov_pcm_seek(&_OggVorbisFile, 0); - //_Stream->seek(0, NLMISC::IStream::begin); - } - else - { - _IsMusicEnded = true; - break; - } - } - else - { - // error - switch(br) - { - case OV_HOLE: - nlwarning("ov_read returned OV_HOLE"); - break; - case OV_EINVAL: - nlwarning("ov_read returned OV_EINVAL"); - break; - case OV_EBADLINK: - nlwarning("ov_read returned OV_EBADLINK"); - break; - default: - nlwarning("ov_read returned %d", br); - } - } - } while (bytes_read < minimum); - return bytes_read; -} - -uint8 CAudioDecoderVorbis::getChannels() -{ - vorbis_info *vi = ov_info(&_OggVorbisFile, -1); - return (uint8)vi->channels; -} - -uint CAudioDecoderVorbis::getSamplesPerSec() -{ - vorbis_info *vi = ov_info(&_OggVorbisFile, -1); - return (uint)vi->rate; -} - -uint8 CAudioDecoderVorbis::getBitsPerSample() -{ - return 16; -} - -bool CAudioDecoderVorbis::isMusicEnded() -{ - return _IsMusicEnded; -} - -float CAudioDecoderVorbis::getLength() -{ - return (float)ov_time_total(&_OggVorbisFile, -1); -} } /* namespace NLSAMPLE */ diff --git a/code/nel/src/sound/CMakeLists.txt b/code/nel/src/sound/CMakeLists.txt index efeb15fee..80e545d25 100644 --- a/code/nel/src/sound/CMakeLists.txt +++ b/code/nel/src/sound/CMakeLists.txt @@ -55,6 +55,11 @@ FILE(GLOB STREAM stream_source.cpp ../../include/nel/sound/stream_source.h ) +FILE(GLOB STREAM_FILE + audio_decoder.cpp ../../include/nel/sound/audio_decoder.h + audio_decoder_vorbis.cpp ../../include/nel/sound/audio_decoder_vorbis.h +) + FILE(GLOB USER_CLASSES ../../include/nel/sound/u_audio_mixer.h ../../include/nel/sound/u_group_controller.h @@ -71,6 +76,7 @@ SOURCE_GROUP("mixer" FILES ${MIXER}) SOURCE_GROUP("music_deprecated" FILES ${MUSIC}) SOURCE_GROUP("sound" FILES ${SOUND}) SOURCE_GROUP("stream" FILES ${STREAM}) +SOURCE_GROUP("stream_file" FILES ${STREAM_FILE}) SOURCE_GROUP("user_classes" FILES ${USER_CLASSES}) diff --git a/code/nel/src/sound/audio_decoder.cpp b/code/nel/src/sound/audio_decoder.cpp new file mode 100644 index 000000000..eef031417 --- /dev/null +++ b/code/nel/src/sound/audio_decoder.cpp @@ -0,0 +1,139 @@ +/** + * \file audio_decoder.cpp + * \brief IAudioDecoder + * \date 2012-04-11 09:34GMT + * \author Jan Boon (Kaetemi) + * IAudioDecoder + */ + +/* + * Copyright (C) 2008-2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE 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. + * + * RYZOM CORE 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 RYZOM CORE. If not, see + * . + */ + +#include "stdsound.h" +#include + +// STL includes + +// NeL includes +#include +#include + +// Project includes +#include + +using namespace std; +using namespace NLMISC; + +namespace NLSOUND { + +IAudioDecoder::IAudioDecoder() : _InternalStream(NULL) +{ + +} + +IAudioDecoder::~IAudioDecoder() +{ + if (_InternalStream) { delete _InternalStream; _InternalStream = NULL; } +} + +IAudioDecoder *IAudioDecoder::createAudioDecoder(const std::string &filepath, bool async, bool loop) +{ + std::string lookup = CPath::lookup(filepath, false); + if (lookup.empty()) + { + nlwarning("Music file %s does not exist!", filepath.c_str()); + return NULL; + } + std::string type = CFile::getExtension(filepath); + + CIFile *ifile = new CIFile(); + ifile->setCacheFileOnOpen(!async); + ifile->allowBNPCacheFileOnOpen(!async); + ifile->open(lookup); + + IAudioDecoder *mb = createAudioDecoder(type, ifile, loop); + + if (mb) mb->_InternalStream = ifile; + else delete ifile; + + return mb; +} + +IAudioDecoder *IAudioDecoder::createAudioDecoder(const std::string &type, NLMISC::IStream *stream, bool loop) +{ + if (!stream) + { + nlwarning("Stream is NULL"); + return NULL; + } + std::string type_lower = toLower(type); + if (type_lower == "ogg") + { + return new CAudioDecoderVorbis(stream, loop); + } + else + { + nlwarning("Music file type unknown: '%s'", type_lower.c_str()); + return NULL; + } +} + +bool IAudioDecoder::getInfo(const std::string &filepath, std::string &artist, std::string &title) +{ + std::string lookup = CPath::lookup(filepath, false); + if (lookup.empty()) + { + nlwarning("Music file %s does not exist!", filepath.c_str()); + return false; + } + std::string type = CFile::getExtension(filepath); + std::string type_lower = NLMISC::toLower(type); + + if (type_lower == "ogg") + { + CIFile ifile; + ifile.setCacheFileOnOpen(false); + ifile.allowBNPCacheFileOnOpen(false); + ifile.open(lookup); + return CAudioDecoderVorbis::getInfo(&ifile, artist, title); + } + else + { + nlwarning("Music file type unknown: '%s'", type_lower.c_str()); + artist.clear(); title.clear(); + return false; + } +} + +/// Get audio/container extensions that are currently supported by the nel sound library. +void IAudioDecoder::getMusicExtensions(std::vector &extensions) +{ + extensions.push_back("ogg"); + // extensions.push_back("wav"); // TODO: Easy. +} + +/// Return if a music extension is supported by the nel sound library. +bool IAudioDecoder::isMusicExtensionSupported(const std::string &extension) +{ + return (extension == "ogg"); +} + +} /* namespace NLSOUND */ + +/* end of file */ diff --git a/code/nel/src/sound/audio_decoder_vorbis.cpp b/code/nel/src/sound/audio_decoder_vorbis.cpp new file mode 100644 index 000000000..1f79b0ddc --- /dev/null +++ b/code/nel/src/sound/audio_decoder_vorbis.cpp @@ -0,0 +1,224 @@ +/** + * \file audio_decoder_vorbis.cpp + * \brief CAudioDecoderVorbis + * \date 2012-04-11 09:35GMT + * \author Jan Boon (Kaetemi) + * CAudioDecoderVorbis + */ + +/* + * Copyright (C) 2008-2012 by authors + * + * This file is part of RYZOM CORE. + * RYZOM CORE 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. + * + * RYZOM CORE 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 RYZOM CORE. If not, see + * . + */ + +#include "stdsound.h" +#include + +// STL includes + +// NeL includes +#include + +// Project includes + +using namespace std; +using namespace NLMISC; + +namespace NLSOUND { + +size_t vorbisReadFunc(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; + NLMISC::IStream *stream = audio_decoder_vorbis->getStream(); + nlassert(stream->isReading()); + sint32 length = (sint32)(size * nmemb); + if (length > audio_decoder_vorbis->getStreamSize() - stream->getPos()) + length = audio_decoder_vorbis->getStreamSize() - stream->getPos(); + stream->serialBuffer((uint8 *)ptr, length); + return length; +} + +int vorbisSeekFunc(void *datasource, ogg_int64_t offset, int whence) +{ + if (whence == SEEK_CUR && offset == 0) + { + // nlwarning(NLSOUND_XAUDIO2_PREFIX "This seek call doesn't do a damn thing, wtf."); + return 0; // ooookkaaaaaayyy + } + + CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; + + NLMISC::IStream::TSeekOrigin origin; + switch (whence) + { + case SEEK_SET: + origin = NLMISC::IStream::begin; + break; + case SEEK_CUR: + origin = NLMISC::IStream::current; + break; + case SEEK_END: + origin = NLMISC::IStream::end; + break; + default: + // nlwarning(NLSOUND_XAUDIO2_PREFIX "Seeking to fake origin."); + return -1; + } + + if (audio_decoder_vorbis->getStream()->seek(SEEK_SET ? audio_decoder_vorbis->getStreamOffset() + (sint32)offset : (sint32)offset, origin)) return 0; + else return -1; +} + +//int vorbisCloseFunc(void *datasource) +//{ +// //CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; +//} + +long vorbisTellFunc(void *datasource) +{ + CAudioDecoderVorbis *audio_decoder_vorbis = (CAudioDecoderVorbis *)datasource; + return (long)(audio_decoder_vorbis->getStream()->getPos() - audio_decoder_vorbis->getStreamOffset()); +} + +static ov_callbacks OV_CALLBACKS_NLMISC_STREAM = { + (size_t (*)(void *, size_t, size_t, void *)) vorbisReadFunc, + (int (*)(void *, ogg_int64_t, int)) vorbisSeekFunc, + (int (*)(void *)) NULL, //vorbisCloseFunc, + (long (*)(void *)) vorbisTellFunc +}; + +CAudioDecoderVorbis::CAudioDecoderVorbis(NLMISC::IStream *stream, bool loop) +: _Stream(stream), _Loop(loop), _StreamSize(0), _IsMusicEnded(false) +{ + _StreamOffset = stream->getPos(); + stream->seek(0, NLMISC::IStream::end); + _StreamSize = stream->getPos(); + stream->seek(_StreamOffset, NLMISC::IStream::begin); + ov_open_callbacks(this, &_OggVorbisFile, NULL, 0, OV_CALLBACKS_NLMISC_STREAM); +} + +CAudioDecoderVorbis::~CAudioDecoderVorbis() +{ + ov_clear(&_OggVorbisFile); +} + +/// Get information on a music file (only artist and title at the moment). +bool CAudioDecoderVorbis::getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title) +{ + CAudioDecoderVorbis mbv(stream, false); // just opens and closes the oggvorbisfile thing :) + vorbis_comment *vc = ov_comment(&mbv._OggVorbisFile, -1); + char *title_c = vorbis_comment_query(vc, "title", 0); + if (title_c) title = title_c; else title.clear(); + char *artist_c = vorbis_comment_query(vc, "artist", 0); + if (artist_c) artist = artist_c; else artist.clear(); + return true; +} + +uint32 CAudioDecoderVorbis::getRequiredBytes() +{ + return 0; // no minimum requirement of bytes to buffer out +} + +uint32 CAudioDecoderVorbis::getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum) +{ + sint current_section = 0; // ??? + if (_IsMusicEnded) return 0; + nlassert(minimum <= maximum); // can't have this.. + uint32 bytes_read = 0; +#ifdef NL_BIG_ENDIAN + sint endianness = 1; +#else + sint endianness = 0; +#endif + do + { + // signed 16-bit or unsigned 8-bit little-endian samples + sint br = ov_read(&_OggVorbisFile, (char *)&buffer[bytes_read], maximum - bytes_read, + endianness, // Specifies big or little endian byte packing. 0 for little endian, 1 for b ig endian. Typical value is 0. + getBitsPerSample() == 8 ? 1 : 2, + getBitsPerSample() == 8 ? 0 : 1, // Signed or unsigned data. 0 for unsigned, 1 for signed. Typically 1. + ¤t_section); + // nlinfo(NLSOUND_XAUDIO2_PREFIX "current_section: %i", current_section); + if (br > 0) + { + bytes_read += (uint32)br; + } + else if (br == 0) // EOF + { + if (_Loop) + { + ov_pcm_seek(&_OggVorbisFile, 0); + //_Stream->seek(0, NLMISC::IStream::begin); + } + else + { + _IsMusicEnded = true; + break; + } + } + else + { + // error + switch(br) + { + case OV_HOLE: + nlwarning("ov_read returned OV_HOLE"); + break; + case OV_EINVAL: + nlwarning("ov_read returned OV_EINVAL"); + break; + case OV_EBADLINK: + nlwarning("ov_read returned OV_EBADLINK"); + break; + default: + nlwarning("ov_read returned %d", br); + } + } + } while (bytes_read < minimum); + return bytes_read; +} + +uint8 CAudioDecoderVorbis::getChannels() +{ + vorbis_info *vi = ov_info(&_OggVorbisFile, -1); + return (uint8)vi->channels; +} + +uint CAudioDecoderVorbis::getSamplesPerSec() +{ + vorbis_info *vi = ov_info(&_OggVorbisFile, -1); + return (uint)vi->rate; +} + +uint8 CAudioDecoderVorbis::getBitsPerSample() +{ + return 16; +} + +bool CAudioDecoderVorbis::isMusicEnded() +{ + return _IsMusicEnded; +} + +float CAudioDecoderVorbis::getLength() +{ + return (float)ov_time_total(&_OggVorbisFile, -1); +} + +} /* namespace NLSOUND */ + +/* end of file */