winamp/Src/Plugins/Input/in_cdda/VeritasPlay.cpp

636 lines
13 KiB
C++

#include "Main.h"
#include <windows.h>
#include "VeritasPlay.h"
#include "CDDB.h"
#include "workorder.h"
#include "api.h"
VeritasPlay::VeritasPlay(bool _ripping)
: opened(false), ripping(_ripping),
primo(0), padStart(false), padEnd(false)
{
submitHandle=0;
hThread = NULL;
overflowBuffer = NULL;
overflow = 0;
buffers = NULL;
currentBuffer = 0;
}
VeritasPlay::~VeritasPlay()
{
if (opened)
Abort();
Close();
DestroyBuffers();
if (primo)
{
waServiceFactory *sf = (line.service ? line.service->service_getServiceByGuid(obj_primo::getServiceGuid()) : NULL);
if (sf) sf->releaseInterface(primo);
}
primo = 0;
if (submitHandle)
{
int x = workorder->CloseSig(submitHandle);
submitHandle=0;
}
}
void VeritasPlay::CreateBuffers()
{
if (buffers)
return;
if (ripping)
{
buf_size = config_rip_buffersize;
nb_veritas_buf = config_rip_buffers;
}
else
{
buf_size = config_play_buffersize;
nb_veritas_buf = config_play_buffers;
}
overflowBuffer = new BYTE[2352 * buf_size];
overflow = 0;
buffers = new VeritasBuffer[nb_veritas_buf];
for (int i = 0;i < nb_veritas_buf;i++)
{
buffers[i].Create(buf_size);
}
}
void VeritasPlay::DestroyBuffers()
{
if (buffers)
{
for (int i = 0;i < nb_veritas_buf;i++)
{
buffers[i].Destroy();
}
delete [] buffers;
}
buffers = 0;
delete overflowBuffer;
overflowBuffer = 0;
}
void VeritasPlay::setvolume(int a_v, int a_p)
{
line.outMod->SetVolume(a_v);
line.outMod->SetPan(a_p);
}
void VeritasPlay::setoutputtime(int time_in_ms)
{
need_seek = time_in_ms;
}
void VeritasPlay::stop()
{
killswitch = 1;
Close();
if (hThread)
{
WaitForEvent(hThread, INFINITE);
hThread=0;
}
line.outMod->Close();
}
void VeritasPlay::SeekAndFlush()
{
int destsector = start_sector + ((need_seek * 75) / 1000);
Abort();
openVeritasTrack(destsector, end_sector - destsector);
decode_pos_ms = need_seek;
// wait for a buffer before we flush
DWORD cursec = 0, totsec = 0;
while (true)
{
if (primo->RunningStatus(PRIMOSDK_GETSTATUS, &cursec, &totsec) != PRIMOSDK_RUNNING
&& primo->UnitStatus(&unit, NULL, NULL, NULL, NULL) == PRIMOSDK_UNITERROR)
return;
// check how far we've gone
if (cursec < buffers[currentBuffer].sector)
Sleep(1);
else
break;
}
overflow = 0;
lastseek = destsector;
line.outMod->Flush(need_seek);
need_seek = -1;
}
void VeritasPlay::Seek()
{
int destsector = start_sector + ((need_seek * 75) / 1000);
Abort();
openVeritasTrack(destsector, end_sector - destsector);
overflow = 0;
decode_pos_ms = need_seek;
need_seek = -1;
lastseek = destsector;
}
void VeritasPlay::Abort()
{
AbortAsync();
WaitForAbort(66);
}
void VeritasPlay::AbortAsync()
{
primo->RunningStatus(PRIMOSDK_ABORT, NULL, NULL);
}
void VeritasPlay::WaitForAbort(int time)
{
while (primo->RunningStatus(PRIMOSDK_GETSTATUS, NULL, NULL) == PRIMOSDK_RUNNING)
Sleep(time);
opened=false;
}
int VeritasPlay::openVeritasTrack(DWORD start, DWORD length)
{
int speedChoice = config_maxextractspeed;
DWORD speed;
if (ripping)
{
switch (speedChoice)
{
case 0: // 0.5x
case 1: // 1x
speed = 1; // can't do 0.5x in the SDK
break;
case 2: // 2x
speed = 2;
break;
case 3: // 4x
speed = 4;
break;
case 4: // 8x
speed = 8;
break;
case 5: // 16x
if (getRegVer() <= 0)
speed = 8;
else
speed = 16;
break;
default:
if (speedChoice < 0)
speed = PRIMOSDK_MIN;
else
{
if (getRegVer() <= 0)
speed = 8;
else
speed = PRIMOSDK_MAX;
}
break;
}
}
else
speed = 4;//PRIMOSDK_MAX;
if (primo->ExtractAudioToBuffer(&unit, start, length, speed, 0, 0, 0) != PRIMOSDK_OK)
return 0;
for (int i = 0;i < nb_veritas_buf;i++)
{
buffers[i].internal = buffers[i].buffer;
if (i==0 && padStart)
{
buffers[i].offset = buf_size*2352 + config_offset*4;
buffers[i].readSize = buf_size*2352;
memset((char *)(buffers[i].internal)+buffers[i].offset, 0, buffers[i].readSize);
buffers[i].sector=0;
continue;
}
if (i==0 && ripping)
buffers[i].offset = ((2352 + config_offset*4) % 2352);
if (primo->NextExtractAudioBuffer(buffers[i].buffer, buf_size*2352, &buffers[i].readSize, &buffers[i].sector) != PRIMOSDK_OK)
return 0;
}
currentBuffer = 0;
opened = true;
return 1;
}
int VeritasPlay::CopyOverflow(char *sample_buffer, int len)
{
if (overflow)
{
len = min(len, overflow);
memset(sample_buffer, 0, len);
memcpy(sample_buffer, overflowBuffer, len);
overflow -= len;
return len;
}
return 0;
}
void VeritasPlay::OutputOverflow()
{
while (overflow)
{
char sample_buffer[576*4*2] = {0};
int bytes = 576 * 4;
int len = min(bytes, overflow);
memset(sample_buffer, 0, bytes);
memcpy(sample_buffer, overflowBuffer, len);
Output(sample_buffer, bytes);
overflow -= len;
}
}
#include <assert.h>
void VeritasPlay::OutputBuffer(VeritasBuffer &buffer)
{
char sample_buffer[576*4*2] = {0};
size_t bytes = 576 * 4;
char *bufferPosition = sample_buffer;
if (overflow)
{
assert(overflow < (long)bytes);
memcpy(bufferPosition, overflowBuffer, overflow);
bytes -= overflow;
bufferPosition += overflow;
overflow = 0;
}
BYTE *bufferInput = buffer.buffer;
while (buffer.readSize)
{
if (buffer.readSize < bytes) // if we don't have enough left, save it to overflow
{
// if there was overflow last time, and the passed buffer didn't fill us up
// then we'll have to save both
BYTE *temp = overflowBuffer;
int samplesLeft = 576 * 4 - bytes;
if (samplesLeft)
{
memcpy(temp, sample_buffer, samplesLeft);
temp += samplesLeft;
overflow += samplesLeft;
}
// copy any leftovers of the passed buffer
memcpy(temp, bufferInput, buffer.readSize);
bufferInput += buffer.readSize;
overflow += buffer.readSize;
buffer.readSize = 0;
return ;
}
else
{
memcpy(bufferPosition, bufferInput, bytes);
bufferPosition = sample_buffer;
bufferInput += bytes;
buffer.readSize -= bytes;
bytes = 576 * 4;
Output(sample_buffer, 576*4);
}
}
}
size_t VeritasPlay::CopyBuffer(VeritasBuffer &buffer, char *&sample_buffer, int &bytes)
{
// since a buffer is only copied once, this is safe
buffer.readSize -= buffer.offset;
buffer.internal += buffer.offset;
buffer.offset=0;
size_t len = min((size_t)bytes, buffer.readSize);
memcpy(sample_buffer, buffer.internal, len);
buffer.internal += len;
buffer.readSize -= len;
sample_buffer += len;
bytes -= len;
return len;
}
void VeritasPlay::Output(char *buffer, int len)
{
line.VSAAddPCMData(buffer, g_nch, 16, line.outMod->GetWrittenTime() /*decode_pos_ms*/);
line.SAAddPCMData(buffer, g_nch, 16, line.outMod->GetWrittenTime() /*decode_pos_ms*/);
int bytes = len;
if (line.dsp_isactive())
bytes = line.dsp_dosamples((short *)buffer, len / g_nch / 2, 16, g_nch, 44100) * (g_nch * 2);
while (line.outMod->CanWrite() < bytes && !killswitch) Sleep(10);
line.outMod->Write(buffer, bytes);
decode_pos_ms += ((len / g_nch / 2) * 1000) / 44100;
}
int VeritasPlay::read(char *dest, int len, int *killswitch) //called by winampGetExtendedRead_getData
{
bool noAbort=false;
int bytesCopied = 0;
speedLimiter.Limit(killswitch);
while (!*killswitch)
{
DWORD cursec = 0, totsec = 0;
int r = primo->RunningStatus(PRIMOSDK_GETSTATUS, &cursec, &totsec);
switch(r)
{
case PRIMOSDK_RUNNING:
break;
case PRIMOSDK_OK:
noAbort=true;
break;
default:
goto readabort; // benski> Mr. Faulkner's high school BASIC class prepared me for the real world!
}
int a = primo->UnitStatus(&unit, NULL, NULL, NULL, NULL);
switch(a)
{
case PRIMOSDK_OK:
break;
default: // todo is everything else really an error? maybe message box for testing purposes
goto readabort;
}
// check how far we've gone
if (cursec >= buffers[currentBuffer].sector)
{
char *olddest=dest;
int bytes = CopyBuffer(buffers[currentBuffer], dest, len);
if (submitHandle && bytes)
{
int res = workorder->WriteSigData(submitHandle, olddest, bytes);
switch(res)
{
case S_OK:
break;
case SG_SignatureAcquired:
default:
workorder->CloseSig(submitHandle);
submitHandle=0;
break;
}
}
speedLimiter.MoreBytesRead(bytes);
bytesCopied += bytes;
if (!len || end == 1)
{
return bytesCopied;
}
if ((buffers[currentBuffer].sector + lastseek) == end_sector) // are we done?
{
bytesCopied -= ((2352 - config_offset*4) % 2352);
end = 1;
return bytesCopied;
}
buffers[currentBuffer].internal = buffers[currentBuffer].buffer;
buffers[currentBuffer].offset = 0;
if (!noAbort)
if (primo) primo->NextExtractAudioBuffer(buffers[currentBuffer].buffer, buf_size*2352, &buffers[currentBuffer].readSize, &buffers[currentBuffer].sector);
currentBuffer = (currentBuffer + 1) % nb_veritas_buf;
}
else if (bytesCopied != 0)
return bytesCopied;
else
Sleep(13*buf_size);
}
// TODO: we can only get here if killswitch got set or if there was an error
readabort:
if (submitHandle)
{
workorder->AbortSig(submitHandle);
submitHandle=0;
}
AbortAsync();
return -1;
}
int VeritasPlay::threadProc2()
{
bool noAbort=false;
while (!killswitch)
{
if (need_seek != -1)
SeekAndFlush();
DWORD cursec = 0, totsec = 0;
int r = primo->RunningStatus(PRIMOSDK_GETSTATUS, &cursec, &totsec);
switch(r)
{
case PRIMOSDK_RUNNING:
break;
case PRIMOSDK_OK:
noAbort=true;
break;
default:
Abort();
if (!killswitch) Sleep(200);
if (!killswitch) PostMessage(line.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
return 0;
}
int a = primo->UnitStatus(&unit, NULL, NULL, NULL, NULL);
switch(a)
{
case PRIMOSDK_OK:
break;
default: // todo is everything else really an error? maybe message box for testing purposes
Abort();
if (!killswitch) Sleep(200);
if (!killswitch) PostMessage(line.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
return 0;
}
// check how far we've gone
if (cursec >= buffers[currentBuffer].sector)
{
OutputBuffer(buffers[currentBuffer]);
if ((buffers[currentBuffer].sector+lastseek)==end_sector) // are we done?
break;
buffers[currentBuffer].internal = buffers[currentBuffer].buffer;
if (!noAbort)
primo->NextExtractAudioBuffer(buffers[currentBuffer].buffer, buf_size*2352, &buffers[currentBuffer].readSize, &buffers[currentBuffer].sector);
currentBuffer = (currentBuffer + 1) % nb_veritas_buf;
}
else
Sleep(13*buf_size);
}
if (killswitch)
{
Abort();
return 0;
}
if (!noAbort)
AbortAsync();
OutputOverflow();
//wait for output to be finished
line.outMod->Write(NULL, 0);
if (!noAbort)
WaitForAbort(10);
while (!killswitch && line.outMod->IsPlaying()) Sleep(10);
if (!killswitch)
PostMessage(line.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
return 0;
}
#define VERITASPLAY_OPEN_FAIL 1
#define VERITASPLAY_OPEN_NOPRIMO 2
int VeritasPlay::open(char drive, int track) //called by winampGetExtendedRead
{
if ((ripping && !config_rip_veritas))
return VERITASPLAY_OPEN_FAIL;
need_seek = -1;
driveLetter = drive;
unit = drive;
hThread = NULL;
if (!primo)
{
waServiceFactory *sf = line.service->service_getServiceByGuid(obj_primo::getServiceGuid());
if (sf) primo = reinterpret_cast<obj_primo *>(sf->getInterface());
}
if (!primo)
return VERITASPLAY_OPEN_NOPRIMO;
end = 0;
speedLimiter.Reset();
if (getRegVer() <= 0)
speedLimiter.SetLimit(5);
else
speedLimiter.NoLimit();
if (primo->DiscInfoEx(&unit,0, NULL, NULL, NULL, NULL, NULL, NULL) == PRIMOSDK_OK)
{
DWORD sesnum, tracktype, pregap;
if (primo->TrackInfo(track, &sesnum, &tracktype, &pregap, &start_sector, &sec_length) == PRIMOSDK_OK)
{
if (ripping)
{
if (config_offset != 0)
sec_length++; // TODO: some sort of logic for the last track
if (config_offset<0)
{
if (track != 1 || config_read_leadin)
start_sector--;
else
{
sec_length--;
padStart=true;
}
}
}
end_sector = start_sector + sec_length;
#if 0 // TODO: add a config option to skip pregap (maybe separate config for burning and playback)
start_sector+=pregap;
sec_length-=pregap;
#endif
CreateBuffers();
if (openVeritasTrack(start_sector, sec_length))
{
g_nch = 2; // TODO: maybe we should handle red book 4 channel audio?
g_playlength = (sec_length / 75) * 1000;
decode_pos_ms = 0;
lastseek = start_sector;
need_seek = -1;
return 0;
}
}
}
Close();
return VERITASPLAY_OPEN_FAIL;
}
int VeritasPlay::play(char drive, int track) //called by winamp2
{
if (!config_use_dae2 || !config_use_veritas)
return 1;
hThread=NULL;
{
g_playtrack = track;
}
switch(open(drive, track))
{
case VERITASPLAY_OPEN_FAIL:
Sleep(200);
// fall through
case VERITASPLAY_OPEN_NOPRIMO:
Close();
return 1;
}
DWORD thread_id;
hThread = CreateThread(NULL, NULL, &threadProc, (LPVOID)this, CREATE_SUSPENDED, &thread_id);
SetThreadPriority(hThread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
int maxlat = line.outMod->Open(44100, g_nch, 16, -1, -1);
if (maxlat < 0)
{
Sleep(200);
Close();
CloseHandle(hThread);
return 1;
}
killswitch = 0;
line.SetInfo(1411, 44, g_nch, 1);
line.SAVSAInit(maxlat, 44100);
line.VSASetInfo(g_nch, 44100);
line.is_seekable = 1;
ResumeThread(hThread);
return 0;
}
void VeritasPlay::Close()
{
driveLetter=0;
}