/** (c) Nullsoft, Inc. C O N F I D E N T I A L ** Filename: ** Project: ** Description: ** Author: Ben Allison benski@nullsoft.com ** Created: **/ #include "main.h" #include "OutputPluginAudioStream.h" #include "out.h" #include "api.h" #include "WinampAttributes.h" int m_converting = 0; static volatile int streamsInUse = 0; static void * volatile streamBuffer = 0; static volatile size_t streamCanWrite = 0; static volatile HANDLE streamWait = 0; static volatile HANDLE streamGo = 0; static volatile HANDLE streamKill = 0; static volatile bool streamPlaying = false; static volatile AudioParameters *streamParameters = 0; static In_Module * volatile streamIn = 0; static volatile int opens = 0; void ConvertEOF() { streamPlaying = false; //if (--opens==0) SetEvent(streamWait); } static int StreamOpen(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms) { streamParameters->bitsPerSample = bitspersamp; streamParameters->channels = numchannels; streamParameters->sampleRate = samplerate; streamParameters->sizeBytes = (size_t) - 1; // we will try to use GetFileInfo to get a length int lengthMS; InW_GetFileInfo(streamIn, 0, 0, &lengthMS); if (lengthMS > 0) { streamParameters->sizeBytes = MulDiv(lengthMS, numchannels * bitspersamp * samplerate, 8000); } streamPlaying = true; return 0; } static void StreamClose() { streamPlaying = false; // SetEvent(streamWait); } static int StreamWrite(char *buf, int len) { again: // null buffer means EOF if (buf == NULL) { streamPlaying = false; //SetEvent(streamWait); return 0; } if (streamCanWrite == 0) { //Sleep(10); // input plugin isn't checking StreamCanWrite() properly, so we'll sleep for them //return 1; } // copy into user-supplied buffer int toCopy = min((int)streamCanWrite, len); memcpy(streamBuffer, buf, toCopy); streamCanWrite -= toCopy; streamBuffer = ((char *)streamBuffer) + toCopy; // the input plugin may have given us too much data, so we'll have to check that // increment the user's stuff len -= toCopy; buf += toCopy; if (len) // len>0 implies streamCanWrite == 0 { ResetEvent(streamGo); SetEvent(streamWait); /* benski> this Sleep() code causes a major slowdown, probably because of high thread priorities in some plugins while (streamCanWrite == 0) Sleep(100); */ HANDLE events[2] = {streamKill, streamGo}; switch (WaitForMultipleObjects(2, events, FALSE, INFINITE)) { case WAIT_OBJECT_0 + 1: goto again; default: return 0; } } // signal event to let ReadAudio() return, if the buffer is full if (streamCanWrite == 0) SetEvent(streamWait); return 0; } static int StreamCanWrite() { if (streamCanWrite) return 65536; else return 0; } static int StreamIsPlaying() { return 0; } static void StreamSet(int nothing) {} static int StreamGetOutputTime() { return 0; } static int StreamGetWrittenTime() { return 0; } static Out_Module streamOut = { OUT_VER, "dummy output", 14000, NULL, NULL, NULL, NULL, NULL, NULL, StreamOpen, StreamClose, StreamWrite, StreamCanWrite, StreamIsPlaying, NULL, //pause StreamSet, //setvolume StreamSet, //setpan StreamSet, //flush StreamGetOutputTime, StreamGetWrittenTime, }; OutputPluginAudioStream::OutputPluginAudioStream() { oldBits=config_audio_bits; oldSurround=config_audio_surround; oldMono=config_audio_mono; oldRG=config_replaygain; } bool OutputPluginAudioStream::Open(In_Module *in, const wchar_t *filename, AudioParameters *parameters) { // set some globals, since winamp output plugins don't have user data/context pointers opens++; m_converting = 1; // this will ask the input plugin to produce our output format (not a guarantee, though) config_audio_bits = parameters->bitsPerSample; config_audio_surround = (parameters->channels > 2); config_audio_mono = (parameters->channels == 1); config_replaygain=false; streamWait = CreateEvent(NULL, FALSE, FALSE, NULL); streamGo = CreateEvent(NULL, FALSE, FALSE, NULL); streamKill = CreateEvent(NULL, TRUE, FALSE, NULL); streamCanWrite = 0; streamBuffer = 0; streamPlaying = false; streamParameters = parameters; streamIn = in; in->outMod = &streamOut; int ret = InW_Play(in, filename); if (ret) { parameters->errorCode = API_DECODEFILE_FAILURE; opens--; return false; } if (in->UsesOutputPlug&IN_MODULE_FLAG_USES_OUTPUT_PLUGIN) { int cnt = 5000; while (!streamPlaying && cnt > 0) { MSG msg; if (PeekMessage(&msg, NULL, 0, 0, FALSE)) WASABI_API_APP->app_messageLoopStep(); else { Sleep(1); cnt--; } } } else { parameters->errorCode = API_DECODEFILE_NO_INTERFACE; opens--; return false; } if (!streamPlaying) { parameters->errorCode = API_DECODEFILE_FAILURE; opens--; return false; } return true; } size_t OutputPluginAudioStream::ReadAudio(void *buffer, size_t sizeBytes) { streamBuffer = buffer; streamCanWrite = sizeBytes; SetEvent(streamGo); HANDLE events[2] = {streamKill, streamWait}; switch (WaitForMultipleObjects(2, events, FALSE, INFINITE)) { case WAIT_OBJECT_0 + 1: // streamWait, which gets triggered when buffer is full or output is done return sizeBytes - streamCanWrite; // streamCanWrite will be >0 if there was a partial write, e.g. on EOF default: return 0; // no point } return sizeBytes - streamCanWrite; } OutputPluginAudioStream::~OutputPluginAudioStream() { SetEvent(streamKill); streamIn->Stop(); DeleteObject(streamWait); DeleteObject(streamGo); DeleteObject(streamKill); streamWait = 0; config_audio_bits = oldBits; config_audio_surround = oldSurround; config_audio_mono=oldMono; config_replaygain=oldRG; } #define CBCLASS OutputPluginAudioStream START_DISPATCH; CB(IFC_AUDIOSTREAM_READAUDIO, ReadAudio) END_DISPATCH;