Sound.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. //
  2. // Copyright (c) 2008-2014 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Precompiled.h"
  23. #include "../Core/Context.h"
  24. #include "../IO/FileSystem.h"
  25. #include "../IO/Log.h"
  26. #include "../Audio/OggVorbisSoundStream.h"
  27. #include "../Core/Profiler.h"
  28. #include "../Resource/ResourceCache.h"
  29. #include "../Audio/Sound.h"
  30. #include "../Resource/XMLFile.h"
  31. #include <cstring>
  32. #include <STB/stb_vorbis.h>
  33. #include "../Container/DebugNew.h"
  34. namespace Urho3D
  35. {
  36. /// WAV format header.
  37. struct WavHeader
  38. {
  39. unsigned char riffText_[4];
  40. unsigned totalLength_;
  41. unsigned char waveText_[4];
  42. unsigned char formatText_[4];
  43. unsigned formatLength_;
  44. unsigned short format_;
  45. unsigned short channels_;
  46. unsigned frequency_;
  47. unsigned avgBytes_;
  48. unsigned short blockAlign_;
  49. unsigned short bits_;
  50. unsigned char dataText_[4];
  51. unsigned dataLength_;
  52. };
  53. static const unsigned IP_SAFETY = 4;
  54. Sound::Sound(Context* context) :
  55. Resource(context),
  56. repeat_(0),
  57. end_(0),
  58. dataSize_(0),
  59. frequency_(44100),
  60. looped_(false),
  61. sixteenBit_(false),
  62. stereo_(false),
  63. compressed_(false),
  64. compressedLength_(0.0f)
  65. {
  66. }
  67. Sound::~Sound()
  68. {
  69. }
  70. void Sound::RegisterObject(Context* context)
  71. {
  72. context->RegisterFactory<Sound>();
  73. }
  74. bool Sound::BeginLoad(Deserializer& source)
  75. {
  76. PROFILE(LoadSound);
  77. bool success = false;
  78. if (GetExtension(source.GetName()) == ".ogg")
  79. success = LoadOggVorbis(source);
  80. else if (GetExtension(source.GetName()) == ".wav")
  81. success = LoadWav(source);
  82. else
  83. success = LoadRaw(source);
  84. // Load optional parameters
  85. if (success)
  86. LoadParameters();
  87. return success;
  88. }
  89. bool Sound::LoadOggVorbis(Deserializer& source)
  90. {
  91. unsigned dataSize = source.GetSize();
  92. SharedArrayPtr<signed char> data(new signed char[dataSize]);
  93. source.Read(data.Get(), dataSize);
  94. // Check for validity of data
  95. int error;
  96. stb_vorbis* vorbis = stb_vorbis_open_memory((unsigned char*)data.Get(), dataSize, &error, 0);
  97. if (!vorbis)
  98. {
  99. LOGERROR("Could not read Ogg Vorbis data from " + source.GetName());
  100. return false;
  101. }
  102. // Store length, frequency and stereo flag
  103. stb_vorbis_info info = stb_vorbis_get_info(vorbis);
  104. compressedLength_ = stb_vorbis_stream_length_in_seconds(vorbis);
  105. frequency_ = info.sample_rate;
  106. stereo_ = info.channels > 1;
  107. stb_vorbis_close(vorbis);
  108. data_ = data;
  109. dataSize_ = dataSize;
  110. sixteenBit_ = true;
  111. compressed_ = true;
  112. SetMemoryUse(dataSize);
  113. return true;
  114. }
  115. bool Sound::LoadWav(Deserializer& source)
  116. {
  117. WavHeader header;
  118. // Try to open
  119. memset(&header, 0, sizeof header);
  120. source.Read(&header.riffText_, 4);
  121. header.totalLength_ = source.ReadUInt();
  122. source.Read(&header.waveText_, 4);
  123. if (memcmp("RIFF", header.riffText_, 4) || memcmp("WAVE", header.waveText_, 4))
  124. {
  125. LOGERROR("Could not read WAV data from " + source.GetName());
  126. return false;
  127. }
  128. // Search for the FORMAT chunk
  129. for (;;)
  130. {
  131. source.Read(&header.formatText_, 4);
  132. header.formatLength_ = source.ReadUInt();
  133. if (!memcmp("fmt ", &header.formatText_, 4))
  134. break;
  135. source.Seek(source.GetPosition() + header.formatLength_);
  136. if (!header.formatLength_ || source.GetPosition() >= source.GetSize())
  137. {
  138. LOGERROR("Could not read WAV data from " + source.GetName());
  139. return false;
  140. }
  141. }
  142. // Read the FORMAT chunk
  143. header.format_ = source.ReadUShort();
  144. header.channels_ = source.ReadUShort();
  145. header.frequency_ = source.ReadUInt();
  146. header.avgBytes_ = source.ReadUInt();
  147. header.blockAlign_ = source.ReadUShort();
  148. header.bits_ = source.ReadUShort();
  149. // Skip data if the format chunk was bigger than what we use
  150. source.Seek(source.GetPosition() + header.formatLength_ - 16);
  151. // Check for correct format
  152. if (header.format_ != 1)
  153. {
  154. LOGERROR("Could not read WAV data from " + source.GetName());
  155. return false;
  156. }
  157. // Search for the DATA chunk
  158. for (;;)
  159. {
  160. source.Read(&header.dataText_, 4);
  161. header.dataLength_ = source.ReadUInt();
  162. if (!memcmp("data", &header.dataText_, 4))
  163. break;
  164. source.Seek(source.GetPosition() + header.dataLength_);
  165. if (!header.dataLength_ || source.GetPosition() >= source.GetSize())
  166. {
  167. LOGERROR("Could not read WAV data from " + source.GetName());
  168. return false;
  169. }
  170. }
  171. // Allocate sound and load audio data
  172. unsigned length = header.dataLength_;
  173. SetSize(length);
  174. SetFormat(header.frequency_, header.bits_ == 16, header.channels_ == 2);
  175. source.Read(data_.Get(), length);
  176. // Convert 8-bit audio to signed
  177. if (!sixteenBit_)
  178. {
  179. for (unsigned i = 0; i < length; ++i)
  180. data_[i] -= 128;
  181. }
  182. return true;
  183. }
  184. bool Sound::LoadRaw(Deserializer& source)
  185. {
  186. unsigned dataSize = source.GetSize();
  187. SetSize(dataSize);
  188. return source.Read(data_.Get(), dataSize) == dataSize;
  189. }
  190. void Sound::SetSize(unsigned dataSize)
  191. {
  192. if (!dataSize)
  193. return;
  194. data_ = new signed char[dataSize + IP_SAFETY];
  195. dataSize_ = dataSize;
  196. compressed_ = false;
  197. SetLooped(false);
  198. SetMemoryUse(dataSize + IP_SAFETY);
  199. }
  200. void Sound::SetData(const void* data, unsigned dataSize)
  201. {
  202. if (!dataSize)
  203. return;
  204. SetSize(dataSize);
  205. memcpy(data_.Get(), data, dataSize);
  206. }
  207. void Sound::SetFormat(unsigned frequency, bool sixteenBit, bool stereo)
  208. {
  209. frequency_ = frequency;
  210. sixteenBit_ = sixteenBit;
  211. stereo_ = stereo;
  212. compressed_ = false;
  213. }
  214. void Sound::SetLooped(bool enable)
  215. {
  216. if (enable)
  217. SetLoop(0, dataSize_);
  218. else
  219. {
  220. if (!compressed_)
  221. {
  222. end_ = data_.Get() + dataSize_;
  223. looped_ = false;
  224. FixInterpolation();
  225. }
  226. else
  227. looped_ = false;
  228. }
  229. }
  230. void Sound::SetLoop(unsigned repeatOffset, unsigned endOffset)
  231. {
  232. if (!compressed_)
  233. {
  234. if (repeatOffset > dataSize_)
  235. repeatOffset = dataSize_;
  236. if (endOffset > dataSize_)
  237. endOffset = dataSize_;
  238. // Align repeat and end on sample boundaries
  239. int sampleSize = GetSampleSize();
  240. repeatOffset &= -sampleSize;
  241. endOffset &= -sampleSize;
  242. repeat_ = data_.Get() + repeatOffset;
  243. end_ = data_.Get() + endOffset;
  244. looped_ = true;
  245. FixInterpolation();
  246. }
  247. else
  248. looped_ = true;
  249. }
  250. void Sound::FixInterpolation()
  251. {
  252. if (!data_)
  253. return;
  254. // If looped, copy loop start to loop end. If oneshot, insert silence to end
  255. if (looped_)
  256. {
  257. for (unsigned i = 0; i < IP_SAFETY; ++i)
  258. end_[i] = repeat_[i];
  259. }
  260. else
  261. {
  262. for (unsigned i = 0; i < IP_SAFETY; ++i)
  263. end_[i] = 0;
  264. }
  265. }
  266. SharedPtr<SoundStream> Sound::GetDecoderStream() const
  267. {
  268. return compressed_ ? SharedPtr<SoundStream>(new OggVorbisSoundStream(this)) : SharedPtr<SoundStream>();
  269. }
  270. float Sound::GetLength() const
  271. {
  272. if (!compressed_)
  273. {
  274. if (!frequency_)
  275. return 0.0f;
  276. else
  277. return ((float)dataSize_) / GetSampleSize() / frequency_;
  278. }
  279. else
  280. return compressedLength_;
  281. }
  282. unsigned Sound::GetSampleSize() const
  283. {
  284. unsigned size = 1;
  285. if (sixteenBit_)
  286. size <<= 1;
  287. if (stereo_)
  288. size <<= 1;
  289. return size;
  290. }
  291. void Sound::LoadParameters()
  292. {
  293. ResourceCache* cache = GetSubsystem<ResourceCache>();
  294. String xmlName = ReplaceExtension(GetName(), ".xml");
  295. SharedPtr<XMLFile> file(cache->GetTempResource<XMLFile>(xmlName, false));
  296. if (!file)
  297. return;
  298. XMLElement rootElem = file->GetRoot();
  299. XMLElement paramElem = rootElem.GetChild();
  300. while (paramElem)
  301. {
  302. String name = paramElem.GetName();
  303. if (name == "format" && !compressed_)
  304. {
  305. if (paramElem.HasAttribute("frequency"))
  306. frequency_ = paramElem.GetInt("frequency");
  307. if (paramElem.HasAttribute("sixteenbit"))
  308. sixteenBit_ = paramElem.GetBool("sixteenbit");
  309. if (paramElem.HasAttribute("16bit"))
  310. sixteenBit_ = paramElem.GetBool("16bit");
  311. if (paramElem.HasAttribute("stereo"))
  312. stereo_ = paramElem.GetBool("stereo");
  313. }
  314. if (name == "loop")
  315. {
  316. if (paramElem.HasAttribute("enable"))
  317. SetLooped(paramElem.GetBool("enable"));
  318. if (paramElem.HasAttribute("start") && paramElem.HasAttribute("end"))
  319. SetLoop(paramElem.GetInt("start"), paramElem.GetInt("end"));
  320. }
  321. paramElem = paramElem.GetNext();
  322. }
  323. }
  324. }