1
0

AudioOutput.h 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #pragma once
  2. #include <bfc/platform/types.h>
  3. #include "../Winamp/in2.h"
  4. #include "../Winamp/out.h"
  5. #include "SpillBuffer.h"
  6. #include <assert.h>
  7. /* A class to manage Winamp input plugin audio output
  8. ** It handles the following for you:
  9. ** * Ensuring that Vis data is sent in chunks of 576
  10. ** * Dealing with gapless audio
  11. ** (you need to pass in the number of pre-delay and post-delay samples)
  12. ** * dealing with the DSP plugin
  13. ** * Waiting for CanWrite()
  14. ** * dealing with inter-timestamps
  15. ** e.g. you pass it >576 samples and it can give you a timestamp based on the divided chunk position
  16. to use, you need to derive from a class that declares
  17. int WaitOrAbort(int time_in_ms);
  18. return 0 on success, non-zero when you need to abort. the return value is passed back through Write()
  19. */
  20. namespace nu // namespace it since "AudioOutput" isn't a unique enough name
  21. {
  22. template <class wait_t>
  23. class AudioOutput : public wait_t
  24. {
  25. public:
  26. AudioOutput( In_Module *plugin ) : plugin( plugin )
  27. {
  28. Init( nullptr );
  29. }
  30. ~AudioOutput()
  31. {
  32. post_buffer.reset();
  33. buffer576.reset();
  34. }
  35. /* Initializes and sets the output plugin pointer
  36. ** for most input plugins, the nu::AudioOutput object will be a global,
  37. ** so this will be necessary to call at the start of Play thread */
  38. void Init( Out_Module *_output )
  39. {
  40. output = _output;
  41. audio_opened = false;
  42. first_timestamp = 0;
  43. sample_size = 0;
  44. output_latency = 0;
  45. post_buffer.reset();
  46. buffer576.reset();
  47. cut_size = 0;
  48. pre_cut_size = 0;
  49. pre_cut = 0;
  50. decoder_delay = 0;
  51. channels = 0;
  52. sample_rate = 0;
  53. bps = 0;
  54. }
  55. /* sets end-of-stream delay (in samples)
  56. ** WITHOUT componesating for post-delay.
  57. ** some filetypes (e.g. iTunes MP4) store gapless info this way */
  58. void SetPostDelay(int postSize)
  59. {
  60. if (postSize < 0)
  61. {
  62. postSize = 0;
  63. }
  64. else if (postSize)
  65. {
  66. if (sample_size)
  67. post_buffer.reserve(postSize*sample_size);
  68. cut_size = postSize;
  69. }
  70. }
  71. /* set end-of-stream zero padding, in samples
  72. ** compensates for decoder delay */
  73. void SetZeroPadding(int postSize)
  74. {
  75. postSize -= decoder_delay;
  76. if (postSize < 0)
  77. {
  78. postSize = 0;
  79. }
  80. SetPostDelay(postSize);
  81. }
  82. /* set decoder delay, initial zero samples and end-of-stream zero samples, all in one shot
  83. ** adjusts zero samples for decoder delay. call SetDelays() if your zero samples are already compensated */
  84. void SetGapless(int decoderDelaySize, int preSize, int postSize)
  85. {
  86. decoder_delay = decoderDelaySize;
  87. SetZeroPadding(postSize);
  88. pre_cut_size = preSize;
  89. pre_cut = pre_cut_size + decoder_delay;
  90. }
  91. /* set decoder delay, initial delay and end-of-stream delay, all in one shot
  92. ** WITHOUT componesating for post-delay.
  93. ** some filetypes (e.g. iTunes MP4) store gapless info this way */
  94. void SetDelays(int decoderDelaySize, int preSize, int postSize)
  95. {
  96. decoder_delay = decoderDelaySize;
  97. SetPostDelay(postSize);
  98. pre_cut_size = preSize;
  99. pre_cut = pre_cut_size;
  100. }
  101. /* Call on seek */
  102. void Flush(int time_in_ms)
  103. {
  104. if (audio_opened)
  105. {
  106. pre_cut = pre_cut_size;
  107. output->Flush(time_in_ms);
  108. first_timestamp = 0; // once we've flushed, we should be accurate so no need for this anymore
  109. buffer576.clear();
  110. post_buffer.clear();
  111. }
  112. else
  113. first_timestamp = time_in_ms;
  114. }
  115. bool Opened() const
  116. {
  117. return audio_opened;
  118. }
  119. int GetLatency() const
  120. {
  121. return output_latency;
  122. }
  123. int GetFirstTimestamp() const
  124. {
  125. return first_timestamp;
  126. }
  127. /* timestamp is meant to be the first timestamp according to the containing file format
  128. ** e.g. many MP4 videos start on 12ms or something, for accurate a/v syncing */
  129. bool Open(int timestamp, int channels, int sample_rate, int bps, int buffer_len_ms=-1, int pre_buffer_ms=-1)
  130. {
  131. if (!audio_opened)
  132. {
  133. int latency = output->Open(sample_rate, channels, bps, buffer_len_ms, pre_buffer_ms);
  134. if (latency < 0)
  135. return false;
  136. plugin->SAVSAInit(latency, sample_rate);
  137. plugin->VSASetInfo(sample_rate, channels);
  138. output->SetVolume(-666);
  139. plugin->SetInfo(-1, sample_rate / 1000, channels, /* TODO? 0*/1);
  140. output_latency = latency;
  141. first_timestamp = timestamp;
  142. sample_size = channels*bps / 8;
  143. this->channels=channels;
  144. this->sample_rate=sample_rate;
  145. this->bps=bps;
  146. SetPostDelay((int)cut_size); // set this again now that we know sample_size, so buffers get allocated correctly
  147. buffer576.reserve(576*sample_size);
  148. audio_opened=true;
  149. }
  150. return audio_opened;
  151. }
  152. void Close()
  153. {
  154. if (audio_opened && output)
  155. {
  156. output->Close();
  157. plugin->SAVSADeInit();
  158. }
  159. output = 0;
  160. first_timestamp = 0;
  161. }
  162. /* outSize is in bytes
  163. ** */
  164. int Write(char *out, size_t outSize)
  165. {
  166. if (!out && !outSize)
  167. {
  168. /* --- write contents of buffered audio (end-zero-padding buffer) */
  169. if (!post_buffer.empty())
  170. {
  171. void *buffer = 0;
  172. size_t len = 0;
  173. if (post_buffer.get(&buffer, &len))
  174. {
  175. int ret = Write576((char *)buffer, len);
  176. if (ret != 0)
  177. return ret;
  178. }
  179. }
  180. /* --- write any remaining data in 576 spill buffer (skip vis) */
  181. if (!buffer576.empty())
  182. {
  183. void *buffer = 0;
  184. size_t len = 0;
  185. if (buffer576.get(&buffer, &len))
  186. {
  187. int ret = WriteOutput((char *)buffer, len);
  188. if (ret != 0)
  189. return ret;
  190. }
  191. }
  192. output->Write(0, 0);
  193. return 0;
  194. }
  195. // this probably should not happen but have seen it in some crash reports
  196. if (!sample_size)
  197. return 0;
  198. assert((outSize % sample_size) == 0);
  199. size_t outSamples = outSize / sample_size;
  200. /* --- cut pre samples, if necessary --- */
  201. size_t pre = min(pre_cut, outSamples);
  202. out += pre * sample_size;
  203. outSize -= pre * sample_size;
  204. pre_cut -= pre;
  205. //outSize = outSamples * sample_size;
  206. // do we will have samples to output after cutting pre-delay?
  207. if (!outSize)
  208. return 0;
  209. /* --- if we don't have enough to fully fill the end-zero-padding buffer, go ahead and fill --- */
  210. if (outSize < post_buffer.length())
  211. {
  212. size_t bytes_written = post_buffer.write(out, outSize);
  213. out+=bytes_written;
  214. outSize-=bytes_written;
  215. }
  216. // if we're out of samples, go ahead and bail
  217. if (!outSize)
  218. return 0;
  219. /* --- write contents of buffered audio (end-zero-padding buffer) */
  220. if (!post_buffer.empty())
  221. {
  222. void *buffer = 0;
  223. size_t len = 0;
  224. if (post_buffer.get(&buffer, &len))
  225. {
  226. int ret = Write576((char *)buffer, len);
  227. if (ret != 0)
  228. return ret;
  229. }
  230. }
  231. /* --- make sure we have enough samples left over to fill our post-zero-padding buffer --- */
  232. size_t remainingFill = /*cut_size - */post_buffer.remaining();
  233. int outWrite = max(0, (int)outSize - (int)remainingFill);
  234. /* --- write the output that doesn't end up in the post buffer */
  235. if (outWrite)
  236. {
  237. int ret = Write576(out, outWrite);
  238. if (ret != 0)
  239. return ret;
  240. }
  241. out += outWrite;
  242. outSize -= outWrite;
  243. /* --- write whatever is left over into the end-zero-padding buffer --- */
  244. if (outSize)
  245. {
  246. post_buffer.write(out, outSize);
  247. }
  248. return 0;
  249. }
  250. /* meant to be called after Write(0,0) */
  251. int WaitWhilePlaying()
  252. {
  253. while (output->IsPlaying())
  254. {
  255. int ret = WaitOrAbort(10);
  256. if (ret != 0)
  257. return ret;
  258. output->CanWrite(); // some output drivers need CanWrite
  259. // to be called on a regular basis.
  260. }
  261. return 0;
  262. }
  263. private:
  264. /* helper methods */
  265. int WaitForOutput(int write_size_bytes)
  266. {
  267. while (output->CanWrite() < write_size_bytes)
  268. {
  269. int ret = WaitOrAbort(55);
  270. if (ret != 0)
  271. return ret;
  272. }
  273. return 0;
  274. }
  275. /* writes one chunk (576 samples) to the output plugin, waiting as necessary */
  276. int WriteOutput(char *buffer, size_t len)
  277. {
  278. int ret = WaitForOutput((int)len);
  279. if (ret != 0)
  280. return ret;
  281. // write vis data before so we guarantee 576 samples
  282. if (len == 576*sample_size)
  283. {
  284. plugin->SAAddPCMData(buffer, channels, bps, output->GetWrittenTime() + first_timestamp);
  285. plugin->VSAAddPCMData(buffer, channels, bps, output->GetWrittenTime() + first_timestamp);
  286. }
  287. if (plugin->dsp_isactive())
  288. len = sample_size * plugin->dsp_dosamples((short *)buffer, (int)(len / sample_size), bps, channels, sample_rate);
  289. output->Write(buffer, (int)len);
  290. return 0;
  291. }
  292. /* given a large buffer, writes 576 sample chunks to the vis, dsp and output plugin */
  293. int Write576(char *buffer, size_t out_size)
  294. {
  295. /* if we have some stuff leftover in the 576 sample spill buffer, fill it up */
  296. if (!buffer576.empty())
  297. {
  298. size_t bytes_written = buffer576.write(buffer, out_size);
  299. out_size -= bytes_written;
  300. buffer += bytes_written;
  301. }
  302. if (buffer576.full())
  303. {
  304. void *buffer = 0;
  305. size_t len = 0;
  306. if (buffer576.get(&buffer, &len))
  307. {
  308. int ret = WriteOutput((char *)buffer, len);
  309. if (ret != 0)
  310. return ret;
  311. }
  312. }
  313. while (out_size >= 576*sample_size)
  314. {
  315. int ret = WriteOutput(buffer, 576*sample_size);
  316. if (ret != 0)
  317. return ret;
  318. out_size -= 576*sample_size;
  319. buffer+=576*sample_size;
  320. }
  321. if (out_size)
  322. {
  323. assert(out_size < 576*sample_size);
  324. buffer576.write(buffer, out_size);
  325. }
  326. return 0;
  327. }
  328. private:
  329. Out_Module *output;
  330. In_Module *plugin;
  331. SpillBuffer post_buffer, buffer576;
  332. size_t cut_size;
  333. size_t pre_cut, pre_cut_size, decoder_delay;
  334. bool audio_opened;
  335. int first_timestamp; /* timestamp of the first decoded audio frame, necessary for accurate video syncing */
  336. size_t sample_size; /* size, in bytes, of one sample of audio (channels*bps/8) */
  337. int output_latency; /* as returned from Out_Module::Open() */
  338. int channels, sample_rate, bps;
  339. };
  340. }