aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_cdda/VeritasPlay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_cdda/VeritasPlay.cpp')
-rw-r--r--Src/Plugins/Input/in_cdda/VeritasPlay.cpp636
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