| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- //
- // Copyright (c) 2008-2017 the Urho3D project.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #include "../Precompiled.h"
- #include "../Audio/OggVorbisSoundStream.h"
- #include "../Audio/Sound.h"
- #include "../Core/Context.h"
- #include "../Core/Profiler.h"
- #include "../IO/FileSystem.h"
- #include "../IO/Log.h"
- #include "../Resource/ResourceCache.h"
- #include "../Resource/XMLFile.h"
- #ifndef STB_VORBIS_HEADER_ONLY
- #define STB_VORBIS_HEADER_ONLY
- #endif
- #include <STB/stb_vorbis.h>
- #include "../DebugNew.h"
- namespace Urho3D
- {
- /// WAV format header.
- struct WavHeader
- {
- unsigned char riffText_[4];
- unsigned totalLength_;
- unsigned char waveText_[4];
- unsigned char formatText_[4];
- unsigned formatLength_;
- unsigned short format_;
- unsigned short channels_;
- unsigned frequency_;
- unsigned avgBytes_;
- unsigned short blockAlign_;
- unsigned short bits_;
- unsigned char dataText_[4];
- unsigned dataLength_;
- };
- static const unsigned IP_SAFETY = 4;
- Sound::Sound(Context* context) :
- ResourceWithMetadata(context),
- repeat_(nullptr),
- end_(nullptr),
- dataSize_(0),
- frequency_(44100),
- looped_(false),
- sixteenBit_(false),
- stereo_(false),
- compressed_(false),
- compressedLength_(0.0f)
- {
- }
- Sound::~Sound()
- {
- }
- void Sound::RegisterObject(Context* context)
- {
- context->RegisterFactory<Sound>();
- }
- bool Sound::BeginLoad(Deserializer& source)
- {
- URHO3D_PROFILE(LoadSound);
- bool success;
- if (GetExtension(source.GetName()) == ".ogg")
- success = LoadOggVorbis(source);
- else if (GetExtension(source.GetName()) == ".wav")
- success = LoadWav(source);
- else
- success = LoadRaw(source);
- // Load optional parameters
- if (success)
- LoadParameters();
- return success;
- }
- bool Sound::LoadOggVorbis(Deserializer& source)
- {
- unsigned dataSize = source.GetSize();
- SharedArrayPtr<signed char> data(new signed char[dataSize]);
- source.Read(data.Get(), dataSize);
- // Check for validity of data
- int error;
- stb_vorbis* vorbis = stb_vorbis_open_memory((unsigned char*)data.Get(), dataSize, &error, nullptr);
- if (!vorbis)
- {
- URHO3D_LOGERROR("Could not read Ogg Vorbis data from " + source.GetName());
- return false;
- }
- // Store length, frequency and stereo flag
- stb_vorbis_info info = stb_vorbis_get_info(vorbis);
- compressedLength_ = stb_vorbis_stream_length_in_seconds(vorbis);
- frequency_ = info.sample_rate;
- stereo_ = info.channels > 1;
- stb_vorbis_close(vorbis);
- data_ = data;
- dataSize_ = dataSize;
- sixteenBit_ = true;
- compressed_ = true;
- SetMemoryUse(dataSize);
- return true;
- }
- bool Sound::LoadWav(Deserializer& source)
- {
- WavHeader header;
- // Try to open
- memset(&header, 0, sizeof header);
- source.Read(&header.riffText_, 4);
- header.totalLength_ = source.ReadUInt();
- source.Read(&header.waveText_, 4);
- if (memcmp("RIFF", header.riffText_, 4) || memcmp("WAVE", header.waveText_, 4))
- {
- URHO3D_LOGERROR("Could not read WAV data from " + source.GetName());
- return false;
- }
- // Search for the FORMAT chunk
- for (;;)
- {
- source.Read(&header.formatText_, 4);
- header.formatLength_ = source.ReadUInt();
- if (!memcmp("fmt ", &header.formatText_, 4))
- break;
- source.Seek(source.GetPosition() + header.formatLength_);
- if (!header.formatLength_ || source.GetPosition() >= source.GetSize())
- {
- URHO3D_LOGERROR("Could not read WAV data from " + source.GetName());
- return false;
- }
- }
- // Read the FORMAT chunk
- header.format_ = source.ReadUShort();
- header.channels_ = source.ReadUShort();
- header.frequency_ = source.ReadUInt();
- header.avgBytes_ = source.ReadUInt();
- header.blockAlign_ = source.ReadUShort();
- header.bits_ = source.ReadUShort();
- // Skip data if the format chunk was bigger than what we use
- source.Seek(source.GetPosition() + header.formatLength_ - 16);
- // Check for correct format
- if (header.format_ != 1)
- {
- URHO3D_LOGERROR("Could not read WAV data from " + source.GetName());
- return false;
- }
- // Search for the DATA chunk
- for (;;)
- {
- source.Read(&header.dataText_, 4);
- header.dataLength_ = source.ReadUInt();
- if (!memcmp("data", &header.dataText_, 4))
- break;
- source.Seek(source.GetPosition() + header.dataLength_);
- if (!header.dataLength_ || source.GetPosition() >= source.GetSize())
- {
- URHO3D_LOGERROR("Could not read WAV data from " + source.GetName());
- return false;
- }
- }
- // Allocate sound and load audio data
- unsigned length = header.dataLength_;
- SetSize(length);
- SetFormat(header.frequency_, header.bits_ == 16, header.channels_ == 2);
- source.Read(data_.Get(), length);
- // Convert 8-bit audio to signed
- if (!sixteenBit_)
- {
- for (unsigned i = 0; i < length; ++i)
- data_[i] -= 128;
- }
- return true;
- }
- bool Sound::LoadRaw(Deserializer& source)
- {
- unsigned dataSize = source.GetSize();
- SetSize(dataSize);
- return source.Read(data_.Get(), dataSize) == dataSize;
- }
- void Sound::SetSize(unsigned dataSize)
- {
- if (!dataSize)
- return;
- data_ = new signed char[dataSize + IP_SAFETY];
- dataSize_ = dataSize;
- compressed_ = false;
- SetLooped(false);
- SetMemoryUse(dataSize + IP_SAFETY);
- }
- void Sound::SetData(const void* data, unsigned dataSize)
- {
- if (!dataSize)
- return;
- SetSize(dataSize);
- memcpy(data_.Get(), data, dataSize);
- }
- void Sound::SetFormat(unsigned frequency, bool sixteenBit, bool stereo)
- {
- frequency_ = frequency;
- sixteenBit_ = sixteenBit;
- stereo_ = stereo;
- compressed_ = false;
- }
- void Sound::SetLooped(bool enable)
- {
- if (enable)
- SetLoop(0, dataSize_);
- else
- {
- if (!compressed_)
- {
- end_ = data_.Get() + dataSize_;
- looped_ = false;
- FixInterpolation();
- }
- else
- looped_ = false;
- }
- }
- void Sound::SetLoop(unsigned repeatOffset, unsigned endOffset)
- {
- if (!compressed_)
- {
- if (repeatOffset > dataSize_)
- repeatOffset = dataSize_;
- if (endOffset > dataSize_)
- endOffset = dataSize_;
- // Align repeat and end on sample boundaries
- int sampleSize = GetSampleSize();
- repeatOffset &= -sampleSize;
- endOffset &= -sampleSize;
- repeat_ = data_.Get() + repeatOffset;
- end_ = data_.Get() + endOffset;
- looped_ = true;
- FixInterpolation();
- }
- else
- looped_ = true;
- }
- void Sound::FixInterpolation()
- {
- if (!data_ || compressed_)
- return;
- // If looped, copy loop start to loop end. If oneshot, insert silence to end
- if (looped_)
- {
- for (unsigned i = 0; i < IP_SAFETY; ++i)
- end_[i] = repeat_[i];
- }
- else
- {
- for (unsigned i = 0; i < IP_SAFETY; ++i)
- end_[i] = 0;
- }
- }
- SharedPtr<SoundStream> Sound::GetDecoderStream() const
- {
- return compressed_ ? SharedPtr<SoundStream>(new OggVorbisSoundStream(this)) : SharedPtr<SoundStream>();
- }
- float Sound::GetLength() const
- {
- if (!compressed_)
- {
- if (!frequency_)
- return 0.0f;
- else
- return ((float)dataSize_) / GetSampleSize() / frequency_;
- }
- else
- return compressedLength_;
- }
- unsigned Sound::GetSampleSize() const
- {
- unsigned size = 1;
- if (sixteenBit_)
- size <<= 1;
- if (stereo_)
- size <<= 1;
- return size;
- }
- void Sound::LoadParameters()
- {
- auto* cache = GetSubsystem<ResourceCache>();
- String xmlName = ReplaceExtension(GetName(), ".xml");
- SharedPtr<XMLFile> file(cache->GetTempResource<XMLFile>(xmlName, false));
- if (!file)
- return;
- XMLElement rootElem = file->GetRoot();
- LoadMetadataFromXML(rootElem);
- for (XMLElement paramElem = rootElem.GetChild(); paramElem; paramElem = paramElem.GetNext())
- {
- String name = paramElem.GetName();
- if (name == "format" && !compressed_)
- {
- if (paramElem.HasAttribute("frequency"))
- frequency_ = (unsigned)paramElem.GetInt("frequency");
- if (paramElem.HasAttribute("sixteenbit"))
- sixteenBit_ = paramElem.GetBool("sixteenbit");
- if (paramElem.HasAttribute("16bit"))
- sixteenBit_ = paramElem.GetBool("16bit");
- if (paramElem.HasAttribute("stereo"))
- stereo_ = paramElem.GetBool("stereo");
- }
- if (name == "loop")
- {
- if (paramElem.HasAttribute("enable"))
- SetLooped(paramElem.GetBool("enable"));
- if (paramElem.HasAttribute("start") && paramElem.HasAttribute("end"))
- SetLoop((unsigned)paramElem.GetInt("start"), (unsigned)paramElem.GetInt("end"));
- }
- }
- }
- }
|