Sound.cpp 11 KB

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