diff options
Diffstat (limited to 'Src/Plugins/Input/in_cdda/VeritasPlay.cpp')
-rw-r--r-- | Src/Plugins/Input/in_cdda/VeritasPlay.cpp | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_cdda/VeritasPlay.cpp b/Src/Plugins/Input/in_cdda/VeritasPlay.cpp new file mode 100644 index 00000000..efb10d9a --- /dev/null +++ b/Src/Plugins/Input/in_cdda/VeritasPlay.cpp @@ -0,0 +1,636 @@ +#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; +}
\ No newline at end of file |