Sound.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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 "Context.h"
  24. #include "FileSystem.h"
  25. #include "Log.h"
  26. #include "Profiler.h"
  27. #include "ResourceCache.h"
  28. #include "Sound.h"
  29. #include "XMLFile.h"
  30. #include <cstring>
  31. #include <stb_vorbis.h>
  32. #include "DebugNew.h"
  33. namespace Urho3D
  34. {
  35. /// WAV format header.
  36. struct WavHeader
  37. {
  38. unsigned char riffText_[4];
  39. unsigned totalLength_;
  40. unsigned char waveText_[4];
  41. unsigned char formatText_[4];
  42. unsigned formatLength_;
  43. unsigned short format_;
  44. unsigned short channels_;
  45. unsigned frequency_;
  46. unsigned avgBytes_;
  47. unsigned short blockAlign_;
  48. unsigned short bits_;
  49. unsigned char dataText_[4];
  50. unsigned dataLength_;
  51. };
  52. static const unsigned IP_SAFETY = 4;
  53. Sound::Sound(Context* context) :
  54. Resource(context),
  55. repeat_(0),
  56. end_(0),
  57. dataSize_(0),
  58. frequency_(44100),
  59. looped_(false),
  60. sixteenBit_(false),
  61. stereo_(false),
  62. compressed_(false),
  63. compressedLength_(0.0f)
  64. {
  65. }
  66. Sound::~Sound()
  67. {
  68. }
  69. void Sound::RegisterObject(Context* context)
  70. {
  71. context->RegisterFactory<Sound>();
  72. }
  73. bool Sound::Load(Deserializer& source)
  74. {
  75. PROFILE(LoadSound);
  76. bool success = false;
  77. if (GetExtension(source.GetName()) == ".ogg")
  78. success = LoadOggVorbis(source);
  79. else if (GetExtension(source.GetName()) == ".wav")
  80. success = LoadWav(source);
  81. else
  82. success = LoadRaw(source);
  83. // Load optional parameters
  84. if (success)
  85. LoadParameters();
  86. return success;
  87. }
  88. bool Sound::LoadOggVorbis(Deserializer& source)
  89. {
  90. unsigned dataSize = source.GetSize();
  91. SharedArrayPtr<signed char> data(new signed char[dataSize]);
  92. source.Read(data.Get(), dataSize);
  93. // Check for validity of data
  94. int error;
  95. stb_vorbis* vorbis = stb_vorbis_open_memory((unsigned char*)data.Get(), dataSize, &error, 0);
  96. if (!vorbis)
  97. {
  98. LOGERROR("Could not read Ogg Vorbis data from " + source.GetName());
  99. return false;
  100. }
  101. // Store length, frequency and stereo flag
  102. stb_vorbis_info info = stb_vorbis_get_info(vorbis);
  103. compressedLength_ = stb_vorbis_stream_length_in_seconds(vorbis);
  104. frequency_ = info.sample_rate;
  105. stereo_ = info.channels > 1;
  106. stb_vorbis_close(vorbis);
  107. data_ = data;
  108. dataSize_ = dataSize;
  109. sixteenBit_ = true;
  110. compressed_ = true;
  111. SetMemoryUse(dataSize);
  112. return true;
  113. }
  114. bool Sound::LoadWav(Deserializer& source)
  115. {
  116. WavHeader header;
  117. // Try to open
  118. memset(&header, 0, sizeof header);
  119. source.Read(&header.riffText_, 4);
  120. header.totalLength_ = source.ReadUInt();
  121. source.Read(&header.waveText_, 4);
  122. if (memcmp("RIFF", header.riffText_, 4) || memcmp("WAVE", header.waveText_, 4))
  123. {
  124. LOGERROR("Could not read WAV data from " + source.GetName());
  125. return false;
  126. }
  127. // Search for the FORMAT chunk
  128. for (;;)
  129. {
  130. source.Read(&header.formatText_, 4);
  131. header.formatLength_ = source.ReadUInt();
  132. if (!memcmp("fmt ", &header.formatText_, 4))
  133. break;
  134. source.Seek(source.GetPosition() + header.formatLength_);
  135. if (!header.formatLength_ || source.GetPosition() >= source.GetSize())
  136. {
  137. LOGERROR("Could not read WAV data from " + source.GetName());
  138. return false;
  139. }
  140. }
  141. // Read the FORMAT chunk
  142. header.format_ = source.ReadUShort();
  143. header.channels_ = source.ReadUShort();
  144. header.frequency_ = source.ReadUInt();
  145. header.avgBytes_ = source.ReadUInt();
  146. header.blockAlign_ = source.ReadUShort();
  147. header.bits_ = source.ReadUShort();
  148. // Skip data if the format chunk was bigger than what we use
  149. source.Seek(source.GetPosition() + header.formatLength_ - 16);
  150. // Check for correct format
  151. if (header.format_ != 1)
  152. {
  153. LOGERROR("Could not read WAV data from " + source.GetName());
  154. return false;
  155. }
  156. // Search for the DATA chunk
  157. for (;;)
  158. {
  159. source.Read(&header.dataText_, 4);
  160. header.dataLength_ = source.ReadUInt();
  161. if (!memcmp("data", &header.dataText_, 4))
  162. break;
  163. source.Seek(source.GetPosition() + header.dataLength_);
  164. if (!header.dataLength_ || source.GetPosition() >= source.GetSize())
  165. {
  166. LOGERROR("Could not read WAV data from " + source.GetName());
  167. return false;
  168. }
  169. }
  170. // Allocate sound and load audio data
  171. unsigned length = header.dataLength_;
  172. SetSize(length);
  173. SetFormat(header.frequency_, header.bits_ == 16, header.channels_ == 2);
  174. source.Read(data_.Get(), length);
  175. // Convert 8-bit audio to signed
  176. if (!sixteenBit_)
  177. {
  178. for (unsigned i = 0; i < length; ++i)
  179. data_[i] -= 128;
  180. }
  181. return true;
  182. }
  183. bool Sound::LoadRaw(Deserializer& source)
  184. {
  185. unsigned dataSize = source.GetSize();
  186. SetSize(dataSize);
  187. return source.Read(data_.Get(), dataSize) == dataSize;
  188. }
  189. void Sound::SetSize(unsigned dataSize)
  190. {
  191. if (!dataSize)
  192. return;
  193. data_ = new signed char[dataSize + IP_SAFETY];
  194. dataSize_ = dataSize;
  195. compressed_ = false;
  196. SetLooped(false);
  197. SetMemoryUse(dataSize + IP_SAFETY);
  198. }
  199. void Sound::SetData(const void* data, unsigned dataSize)
  200. {
  201. if (!dataSize)
  202. return;
  203. SetSize(dataSize);
  204. memcpy(data_.Get(), data, dataSize);
  205. }
  206. void Sound::SetFormat(unsigned frequency, bool sixteenBit, bool stereo)
  207. {
  208. frequency_ = frequency;
  209. sixteenBit_ = sixteenBit;
  210. stereo_ = stereo;
  211. compressed_ = false;
  212. }
  213. void Sound::SetLooped(bool enable)
  214. {
  215. if (enable)
  216. SetLoop(0, dataSize_);
  217. else
  218. {
  219. if (!compressed_)
  220. {
  221. end_ = data_.Get() + dataSize_;
  222. looped_ = false;
  223. FixInterpolation();
  224. }
  225. else
  226. looped_ = false;
  227. }
  228. }
  229. void Sound::SetLoop(unsigned repeatOffset, unsigned endOffset)
  230. {
  231. if (!compressed_)
  232. {
  233. if (repeatOffset > dataSize_)
  234. repeatOffset = dataSize_;
  235. if (endOffset > dataSize_)
  236. endOffset = dataSize_;
  237. // Align repeat and end on sample boundaries
  238. int sampleSize = GetSampleSize();
  239. repeatOffset &= -sampleSize;
  240. endOffset &= -sampleSize;
  241. repeat_ = data_.Get() + repeatOffset;
  242. end_ = data_.Get() + endOffset;
  243. looped_ = true;
  244. FixInterpolation();
  245. }
  246. else
  247. looped_ = true;
  248. }
  249. void Sound::FixInterpolation()
  250. {
  251. if (!data_)
  252. return;
  253. // If looped, copy loop start to loop end. If oneshot, insert silence to end
  254. if (looped_)
  255. {
  256. for (unsigned i = 0; i < IP_SAFETY; ++i)
  257. end_[i] = repeat_[i];
  258. }
  259. else
  260. {
  261. for (unsigned i = 0; i < IP_SAFETY; ++i)
  262. end_[i] = 0;
  263. }
  264. }
  265. void* Sound::AllocateDecoder()
  266. {
  267. if (!compressed_)
  268. return 0;
  269. int error;
  270. stb_vorbis* vorbis = stb_vorbis_open_memory((unsigned char*)data_.Get(), dataSize_, &error, 0);
  271. return vorbis;
  272. }
  273. unsigned Sound::Decode(void* decoder, signed char* dest, unsigned bytes)
  274. {
  275. if (!decoder)
  276. return 0;
  277. unsigned soundSources = stereo_ ? 2 : 1;
  278. stb_vorbis* vorbis = static_cast<stb_vorbis*>(decoder);
  279. unsigned outSamples = stb_vorbis_get_samples_short_interleaved(vorbis, soundSources, (short*)dest, bytes >> 1);
  280. return (outSamples * soundSources) << 1;
  281. }
  282. void Sound::RewindDecoder(void* decoder)
  283. {
  284. if (!decoder)
  285. return;
  286. stb_vorbis* vorbis = static_cast<stb_vorbis*>(decoder);
  287. stb_vorbis_seek_start(vorbis);
  288. }
  289. void Sound::FreeDecoder(void* decoder)
  290. {
  291. if (!decoder)
  292. return;
  293. stb_vorbis* vorbis = static_cast<stb_vorbis*>(decoder);
  294. stb_vorbis_close(vorbis);
  295. }
  296. float Sound::GetLength() const
  297. {
  298. if (!compressed_)
  299. {
  300. if (!frequency_)
  301. return 0.0f;
  302. else
  303. return ((float)dataSize_) / GetSampleSize() / frequency_;
  304. }
  305. else
  306. return compressedLength_;
  307. }
  308. unsigned Sound::GetSampleSize() const
  309. {
  310. unsigned size = 1;
  311. if (sixteenBit_)
  312. size <<= 1;
  313. if (stereo_)
  314. size <<= 1;
  315. return size;
  316. }
  317. void Sound::LoadParameters()
  318. {
  319. ResourceCache* cache = GetSubsystem<ResourceCache>();
  320. String xmlName = ReplaceExtension(GetName(), ".xml");
  321. if (!cache->Exists(xmlName))
  322. return;
  323. XMLFile* file = cache->GetResource<XMLFile>(xmlName);
  324. if (!file)
  325. return;
  326. XMLElement rootElem = file->GetRoot();
  327. XMLElement paramElem = rootElem.GetChild();
  328. while (paramElem)
  329. {
  330. String name = paramElem.GetName();
  331. if (name == "format" && !compressed_)
  332. {
  333. if (paramElem.HasAttribute("frequency"))
  334. frequency_ = paramElem.GetInt("frequency");
  335. if (paramElem.HasAttribute("sixteenbit"))
  336. sixteenBit_ = paramElem.GetBool("sixteenbit");
  337. if (paramElem.HasAttribute("16bit"))
  338. sixteenBit_ = paramElem.GetBool("16bit");
  339. if (paramElem.HasAttribute("stereo"))
  340. stereo_ = paramElem.GetBool("stereo");
  341. }
  342. if (name == "loop")
  343. {
  344. if (paramElem.HasAttribute("enable"))
  345. SetLooped(paramElem.GetBool("enable"));
  346. if (paramElem.HasAttribute("start") && paramElem.HasAttribute("end"))
  347. SetLoop(paramElem.GetInt("start"), paramElem.GetInt("end"));
  348. }
  349. paramElem = paramElem.GetNext();
  350. }
  351. }
  352. }