271 lines
5.8 KiB
C++
271 lines
5.8 KiB
C++
|
/** (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;
|