winamp/Src/Winamp/OutputPluginAudioStream.cpp
2024-09-24 14:54:57 +02:00

270 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;