OutputPluginAudioStream.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /** (c) Nullsoft, Inc. C O N F I D E N T I A L
  2. ** Filename:
  3. ** Project:
  4. ** Description:
  5. ** Author: Ben Allison [email protected]
  6. ** Created:
  7. **/
  8. #include "main.h"
  9. #include "OutputPluginAudioStream.h"
  10. #include "out.h"
  11. #include "api.h"
  12. #include "WinampAttributes.h"
  13. int m_converting = 0;
  14. static volatile int streamsInUse = 0;
  15. static void * volatile streamBuffer = 0;
  16. static volatile size_t streamCanWrite = 0;
  17. static volatile HANDLE streamWait = 0;
  18. static volatile HANDLE streamGo = 0;
  19. static volatile HANDLE streamKill = 0;
  20. static volatile bool streamPlaying = false;
  21. static volatile AudioParameters *streamParameters = 0;
  22. static In_Module * volatile streamIn = 0;
  23. static volatile int opens = 0;
  24. void ConvertEOF()
  25. {
  26. streamPlaying = false;
  27. //if (--opens==0)
  28. SetEvent(streamWait);
  29. }
  30. static int StreamOpen(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms)
  31. {
  32. streamParameters->bitsPerSample = bitspersamp;
  33. streamParameters->channels = numchannels;
  34. streamParameters->sampleRate = samplerate;
  35. streamParameters->sizeBytes = (size_t) - 1;
  36. // we will try to use GetFileInfo to get a length
  37. int lengthMS;
  38. InW_GetFileInfo(streamIn, 0, 0, &lengthMS);
  39. if (lengthMS > 0)
  40. {
  41. streamParameters->sizeBytes = MulDiv(lengthMS, numchannels * bitspersamp * samplerate, 8000);
  42. }
  43. streamPlaying = true;
  44. return 0;
  45. }
  46. static void StreamClose()
  47. {
  48. streamPlaying = false;
  49. // SetEvent(streamWait);
  50. }
  51. static int StreamWrite(char *buf, int len)
  52. {
  53. again:
  54. // null buffer means EOF
  55. if (buf == NULL)
  56. {
  57. streamPlaying = false;
  58. //SetEvent(streamWait);
  59. return 0;
  60. }
  61. if (streamCanWrite == 0)
  62. {
  63. //Sleep(10); // input plugin isn't checking StreamCanWrite() properly, so we'll sleep for them
  64. //return 1;
  65. }
  66. // copy into user-supplied buffer
  67. int toCopy = min((int)streamCanWrite, len);
  68. memcpy(streamBuffer, buf, toCopy);
  69. streamCanWrite -= toCopy;
  70. streamBuffer = ((char *)streamBuffer) + toCopy;
  71. // the input plugin may have given us too much data, so we'll have to check that
  72. // increment the user's stuff
  73. len -= toCopy;
  74. buf += toCopy;
  75. if (len) // len>0 implies streamCanWrite == 0
  76. {
  77. ResetEvent(streamGo);
  78. SetEvent(streamWait);
  79. /* benski> this Sleep() code causes a major slowdown,
  80. probably because of high thread priorities in some plugins
  81. while (streamCanWrite == 0)
  82. Sleep(100);
  83. */
  84. HANDLE events[2] = {streamKill, streamGo};
  85. switch (WaitForMultipleObjects(2, events, FALSE, INFINITE))
  86. {
  87. case WAIT_OBJECT_0 + 1:
  88. goto again;
  89. default:
  90. return 0;
  91. }
  92. }
  93. // signal event to let ReadAudio() return, if the buffer is full
  94. if (streamCanWrite == 0)
  95. SetEvent(streamWait);
  96. return 0;
  97. }
  98. static int StreamCanWrite()
  99. {
  100. if (streamCanWrite)
  101. return 65536;
  102. else
  103. return 0;
  104. }
  105. static int StreamIsPlaying()
  106. {
  107. return 0;
  108. }
  109. static void StreamSet(int nothing)
  110. {}
  111. static int StreamGetOutputTime()
  112. {
  113. return 0;
  114. }
  115. static int StreamGetWrittenTime()
  116. {
  117. return 0;
  118. }
  119. static Out_Module streamOut =
  120. {
  121. OUT_VER,
  122. "dummy output",
  123. 14000,
  124. NULL,
  125. NULL,
  126. NULL,
  127. NULL,
  128. NULL,
  129. NULL,
  130. StreamOpen,
  131. StreamClose,
  132. StreamWrite,
  133. StreamCanWrite,
  134. StreamIsPlaying,
  135. NULL, //pause
  136. StreamSet, //setvolume
  137. StreamSet, //setpan
  138. StreamSet, //flush
  139. StreamGetOutputTime,
  140. StreamGetWrittenTime,
  141. };
  142. OutputPluginAudioStream::OutputPluginAudioStream()
  143. {
  144. oldBits=config_audio_bits;
  145. oldSurround=config_audio_surround;
  146. oldMono=config_audio_mono;
  147. oldRG=config_replaygain;
  148. }
  149. bool OutputPluginAudioStream::Open(In_Module *in, const wchar_t *filename, AudioParameters *parameters)
  150. {
  151. // set some globals, since winamp output plugins don't have user data/context pointers
  152. opens++;
  153. m_converting = 1;
  154. // this will ask the input plugin to produce our output format (not a guarantee, though)
  155. config_audio_bits = parameters->bitsPerSample;
  156. config_audio_surround = (parameters->channels > 2);
  157. config_audio_mono = (parameters->channels == 1);
  158. config_replaygain=false;
  159. streamWait = CreateEvent(NULL, FALSE, FALSE, NULL);
  160. streamGo = CreateEvent(NULL, FALSE, FALSE, NULL);
  161. streamKill = CreateEvent(NULL, TRUE, FALSE, NULL);
  162. streamCanWrite = 0;
  163. streamBuffer = 0;
  164. streamPlaying = false;
  165. streamParameters = parameters;
  166. streamIn = in;
  167. in->outMod = &streamOut;
  168. int ret = InW_Play(in, filename);
  169. if (ret)
  170. {
  171. parameters->errorCode = API_DECODEFILE_FAILURE;
  172. opens--;
  173. return false;
  174. }
  175. if (in->UsesOutputPlug&IN_MODULE_FLAG_USES_OUTPUT_PLUGIN)
  176. {
  177. int cnt = 5000;
  178. while (!streamPlaying && cnt > 0)
  179. {
  180. MSG msg;
  181. if (PeekMessage(&msg, NULL, 0, 0, FALSE))
  182. WASABI_API_APP->app_messageLoopStep();
  183. else
  184. {
  185. Sleep(1);
  186. cnt--;
  187. }
  188. }
  189. }
  190. else
  191. {
  192. parameters->errorCode = API_DECODEFILE_NO_INTERFACE;
  193. opens--;
  194. return false;
  195. }
  196. if (!streamPlaying)
  197. {
  198. parameters->errorCode = API_DECODEFILE_FAILURE;
  199. opens--;
  200. return false;
  201. }
  202. return true;
  203. }
  204. size_t OutputPluginAudioStream::ReadAudio(void *buffer, size_t sizeBytes)
  205. {
  206. streamBuffer = buffer;
  207. streamCanWrite = sizeBytes;
  208. SetEvent(streamGo);
  209. HANDLE events[2] = {streamKill, streamWait};
  210. switch (WaitForMultipleObjects(2, events, FALSE, INFINITE))
  211. {
  212. case WAIT_OBJECT_0 + 1: // streamWait, which gets triggered when buffer is full or output is done
  213. return sizeBytes - streamCanWrite; // streamCanWrite will be >0 if there was a partial write, e.g. on EOF
  214. default:
  215. return 0; // no point
  216. }
  217. return sizeBytes - streamCanWrite;
  218. }
  219. OutputPluginAudioStream::~OutputPluginAudioStream()
  220. {
  221. SetEvent(streamKill);
  222. streamIn->Stop();
  223. DeleteObject(streamWait);
  224. DeleteObject(streamGo);
  225. DeleteObject(streamKill);
  226. streamWait = 0;
  227. config_audio_bits = oldBits;
  228. config_audio_surround = oldSurround;
  229. config_audio_mono=oldMono;
  230. config_replaygain=oldRG;
  231. }
  232. #define CBCLASS OutputPluginAudioStream
  233. START_DISPATCH;
  234. CB(IFC_AUDIOSTREAM_READAUDIO, ReadAudio)
  235. END_DISPATCH;