mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
625 lines
16 KiB
C++
625 lines
16 KiB
C++
|
|
/*
|
|
* UAE
|
|
*
|
|
* Win32 audio player for CDA emulation
|
|
*
|
|
* Copyright 2010 Toni Wilen
|
|
*
|
|
*/
|
|
|
|
#define CDADS 0
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
|
|
#include "options.h"
|
|
#include "dxwrap.h"
|
|
#include "audio.h"
|
|
#include "blkdev.h"
|
|
#include "threaddep/thread.h"
|
|
|
|
#include <dsound.h>
|
|
#include <mmreg.h>
|
|
#include <sys/timeb.h>
|
|
|
|
#include "win32.h"
|
|
#include "gui.h"
|
|
|
|
#include "cda_play.h"
|
|
|
|
cda_audio::~cda_audio()
|
|
{
|
|
if (active) {
|
|
wait(0);
|
|
wait(1);
|
|
#if CDADS
|
|
if (dsnotify)
|
|
dsnotify->Release();
|
|
if (dsbuf)
|
|
dsbuf->Release();
|
|
if (ds)
|
|
ds->Release();
|
|
if (notifyevent[0])
|
|
CloseHandle(notifyevent[0]);
|
|
if (notifyevent[1])
|
|
CloseHandle(notifyevent[1]);
|
|
#else
|
|
for (int i = 0; i < 2; i++)
|
|
waveOutUnprepareHeader(wavehandle, &whdr[i], sizeof(WAVEHDR));
|
|
if (wavehandle != NULL)
|
|
waveOutClose(wavehandle);
|
|
#endif
|
|
}
|
|
for (int i = 0; i < 2; i++) {
|
|
xfree (buffers[i]);
|
|
buffers[i] = NULL;
|
|
}
|
|
}
|
|
|
|
cda_audio::cda_audio(int num_sectors, int sectorsize, int samplerate, bool internalmode)
|
|
{
|
|
active = false;
|
|
playing = false;
|
|
volume[0] = volume[1] = 0;
|
|
|
|
bufsize = num_sectors * sectorsize;
|
|
this->sectorsize = sectorsize;
|
|
for (int i = 0; i < 2; i++) {
|
|
buffers[i] = xcalloc (uae_u8, num_sectors * ((bufsize + 4095) & ~4095));
|
|
}
|
|
this->num_sectors = num_sectors;
|
|
|
|
if (internalmode)
|
|
return;
|
|
|
|
WAVEFORMATEX wav;
|
|
memset (&wav, 0, sizeof (WAVEFORMATEX));
|
|
|
|
wav.cbSize = 0;
|
|
wav.nChannels = 2;
|
|
wav.nSamplesPerSec = samplerate;
|
|
wav.wBitsPerSample = 16;
|
|
wav.nBlockAlign = wav.wBitsPerSample / 8 * wav.nChannels;
|
|
wav.nAvgBytesPerSec = wav.nBlockAlign * wav.nSamplesPerSec;
|
|
wav.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
#if CDADS
|
|
LPDIRECTSOUNDBUFFER pdsb;
|
|
WAVEFORMATEXTENSIBLE wavfmt;
|
|
DSBUFFERDESC desc;
|
|
HRESULT hr;
|
|
|
|
dsnotify = NULL;
|
|
dsbuf = NULL;
|
|
ds = NULL;
|
|
notifyevent[0] = notifyevent[1] = NULL;
|
|
|
|
hr = DirectSoundCreate8 (&sound_devices[currprefs.win32_soundcard].guid, &ds, NULL);
|
|
if (FAILED (hr)) {
|
|
write_log (_T("CDA: DirectSoundCreate8() failure: %s\n"), DXError (hr));
|
|
return;
|
|
}
|
|
|
|
hr = ds->SetCooperativeLevel (hMainWnd, DSSCL_PRIORITY);
|
|
if (FAILED (hr)) {
|
|
write_log (_T("CDA: Can't set cooperativelevel: %s\n"), DXError (hr));
|
|
return;
|
|
}
|
|
|
|
wavfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
wavfmt.Format.nChannels = 2;
|
|
wavfmt.Format.nSamplesPerSec = 44100;
|
|
wavfmt.Format.wBitsPerSample = 16;
|
|
wavfmt.Format.nBlockAlign = wavfmt.Format.wBitsPerSample / 8 * wavfmt.Format.nChannels;
|
|
wavfmt.Format.nAvgBytesPerSec = wavfmt.Format.nBlockAlign * wavfmt.Format.nSamplesPerSec;
|
|
wavfmt.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
wavfmt.Samples.wValidBitsPerSample = 16;
|
|
wavfmt.Format.cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);
|
|
|
|
memset (&desc, 0, sizeof desc);
|
|
desc.dwSize = sizeof desc;
|
|
desc.dwBufferBytes = 2 * bufsize;
|
|
desc.lpwfxFormat = &wavfmt.Format;
|
|
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
|
|
desc.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLVOLUME;
|
|
desc.guid3DAlgorithm = GUID_NULL;
|
|
|
|
hr = ds->CreateSoundBuffer (&desc, &pdsb, NULL);
|
|
if (FAILED (hr)) {
|
|
write_log (_T("CDA: IDirectSound_CreateSoundBuffer %s\n"), DXError (hr));
|
|
return;
|
|
}
|
|
hr = pdsb->QueryInterface (IID_IDirectSoundBuffer8, (LPVOID*)&dsbuf);
|
|
IDirectSound_Release (pdsb);
|
|
if (FAILED (hr)) {
|
|
write_log (_T("CDA: Secondary QueryInterface() failure: %s\n"), DXError (hr));
|
|
return;
|
|
}
|
|
hr = dsbuf->QueryInterface (IID_IDirectSoundNotify, (LPVOID*)&dsnotify);
|
|
if (FAILED (hr)) {
|
|
write_log (_T("CDA: IID_IDirectSoundNotify QueryInterface() failure: %s\n"), DXError (hr));
|
|
return;
|
|
}
|
|
|
|
notifyevent[0] = CreateEvent (NULL, TRUE, FALSE, NULL);
|
|
notifyevent[1] = CreateEvent (NULL, TRUE, FALSE, NULL);
|
|
DSBPOSITIONNOTIFY nf[2];
|
|
nf[0].dwOffset = bufsize / num_sectors;
|
|
nf[1].dwOffset = bufsize + bufsize / num_sectors;
|
|
nf[0].hEventNotify = notifyevent[0];
|
|
nf[1].hEventNotify = notifyevent[1];
|
|
hr = dsnotify->SetNotificationPositions(2, nf);
|
|
|
|
active = true;
|
|
#else
|
|
MMRESULT mmr;
|
|
mmr = waveOutOpen (&wavehandle, WAVE_MAPPER, &wav, 0, 0, WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT);
|
|
if (mmr != MMSYSERR_NOERROR) {
|
|
write_log (_T("IMAGE CDDA: wave open %d\n"), mmr);
|
|
return;
|
|
}
|
|
for (int i = 0; i < 2; i++) {
|
|
memset (&whdr[i], 0, sizeof(WAVEHDR));
|
|
whdr[i].dwBufferLength = sectorsize * num_sectors;
|
|
whdr[i].lpData = (LPSTR)buffers[i];
|
|
mmr = waveOutPrepareHeader (wavehandle, &whdr[i], sizeof (WAVEHDR));
|
|
if (mmr != MMSYSERR_NOERROR) {
|
|
write_log (_T("IMAGE CDDA: waveOutPrepareHeader %d:%d\n"), i, mmr);
|
|
return;
|
|
}
|
|
whdr[i].dwFlags |= WHDR_DONE;
|
|
}
|
|
active = true;
|
|
playing = true;
|
|
#endif
|
|
}
|
|
|
|
void cda_audio::setvolume(int left, int right)
|
|
{
|
|
for (int j = 0; j < 2; j++) {
|
|
volume[j] = j == 0 ? left : right;
|
|
volume[j] = sound_cd_volume[j] * volume[j] / 32768;
|
|
if (volume[j])
|
|
volume[j]++;
|
|
volume[j] = volume[j] * (100 - currprefs.sound_volume_master) / 100;
|
|
if (volume[j] >= 32768)
|
|
volume[j] = 32768;
|
|
}
|
|
#if CDADS
|
|
LONG vol = DSBVOLUME_MIN;
|
|
int volume = master * left / 32768;
|
|
if (volume < 100)
|
|
vol = (LONG)((DSBVOLUME_MIN / 2) + (-DSBVOLUME_MIN / 2) * log (1 + (2.718281828 - 1) * (1 - volume / 100.0)));
|
|
HRESULT hr = dsbuf->SetVolume(vol);
|
|
if (FAILED (hr))
|
|
write_log (_T("CDA: SetVolume(%d) failed: %s\n"), vol, DXError (hr));
|
|
#endif
|
|
}
|
|
bool cda_audio::play(int bufnum)
|
|
{
|
|
if (!active)
|
|
return false;
|
|
#if CDADS
|
|
DWORD status;
|
|
HRESULT hr = dsbuf->GetStatus (&status);
|
|
if (FAILED (hr)) {
|
|
write_log (_T("CDA: GetStatus() failed: %s\n"), DXError (hr));
|
|
return false;
|
|
}
|
|
if (status & DSBSTATUS_BUFFERLOST) {
|
|
write_log (_T("CDA: bufferlost\n"));
|
|
return false;
|
|
}
|
|
if ((status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING)) != (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING)) {
|
|
dsbuf->SetCurrentPosition((1 - bufnum) * bufsize);
|
|
dsbuf->Play(0, 0, DSBPLAY_LOOPING);
|
|
playing = true;
|
|
}
|
|
PVOID ptr;
|
|
DWORD len;
|
|
if (SUCCEEDED(dsbuf->Lock(bufnum * bufsize, bufsize, &ptr, &len, NULL, NULL, 0))) {
|
|
memcpy (ptr, buffers[bufnum], bufsize);
|
|
dsbuf->Unlock(ptr, len, NULL, NULL);
|
|
}
|
|
return true;
|
|
#else
|
|
uae_s16 *p = (uae_s16*)(buffers[bufnum]);
|
|
if (volume[0] != 32768 || volume[1] != 32768) {
|
|
for (int i = 0; i < num_sectors * sectorsize / 4; i++) {
|
|
p[i * 2 + 0] = p[i * 2 + 0] * volume[0] / 32768;
|
|
p[i * 2 + 1] = p[i * 2 + 1] * volume[1] / 32768;
|
|
}
|
|
}
|
|
MMRESULT mmr = waveOutWrite (wavehandle, &whdr[bufnum], sizeof (WAVEHDR));
|
|
if (mmr != MMSYSERR_NOERROR) {
|
|
write_log (_T("IMAGE CDDA: waveOutWrite %d\n"), mmr);
|
|
return false;
|
|
}
|
|
return true;
|
|
#endif
|
|
}
|
|
void cda_audio::wait(int bufnum)
|
|
{
|
|
if (!active || !playing)
|
|
return;
|
|
#if CDADS
|
|
WaitForSingleObject (notifyevent[bufnum], INFINITE);
|
|
ResetEvent (notifyevent[bufnum]);
|
|
#else
|
|
while (!(whdr[bufnum].dwFlags & WHDR_DONE))
|
|
Sleep (10);
|
|
#endif
|
|
}
|
|
|
|
bool cda_audio::isplaying(int bufnum)
|
|
{
|
|
if (!active || !playing)
|
|
return false;
|
|
return (whdr[bufnum].dwFlags & WHDR_DONE) == 0;
|
|
}
|
|
|
|
|
|
static uae_sem_t play_sem;
|
|
|
|
static void sub_deinterleave(const uae_u8 *s, uae_u8 *d)
|
|
{
|
|
for (int i = 0; i < 8 * 12; i++) {
|
|
int dmask = 0x80;
|
|
int smask = 1 << (7 - (i / 12));
|
|
(*d) = 0;
|
|
for (int j = 0; j < 8; j++) {
|
|
(*d) |= (s[(i % 12) * 8 + j] & smask) ? dmask : 0;
|
|
dmask >>= 1;
|
|
}
|
|
d++;
|
|
}
|
|
}
|
|
|
|
static void sub_to_deinterleaved(const uae_u8 *s, uae_u8 *d)
|
|
{
|
|
for (int i = 0; i < 8 * 12; i++) {
|
|
int dmask = 0x80;
|
|
int smask = 1 << (7 - (i / 12));
|
|
(*d) = 0;
|
|
for (int j = 0; j < 8; j++) {
|
|
(*d) |= (s[(i % 12) * 8 + j] & smask) ? dmask : 0;
|
|
dmask >>= 1;
|
|
}
|
|
d++;
|
|
}
|
|
}
|
|
|
|
static void cdda_closewav(struct cda_play *ciw)
|
|
{
|
|
if (ciw->cdda_wavehandle != NULL)
|
|
waveOutClose(ciw->cdda_wavehandle);
|
|
ciw->cdda_wavehandle = NULL;
|
|
}
|
|
|
|
// DAE CDDA based on Larry Osterman's "Playing Audio CDs" blog series
|
|
|
|
static int cdda_openwav(struct cda_play *ciw)
|
|
{
|
|
WAVEFORMATEX wav = { 0 };
|
|
MMRESULT mmr;
|
|
|
|
wav.cbSize = 0;
|
|
wav.nChannels = 2;
|
|
wav.nSamplesPerSec = 44100;
|
|
wav.wBitsPerSample = 16;
|
|
wav.nBlockAlign = wav.wBitsPerSample / 8 * wav.nChannels;
|
|
wav.nAvgBytesPerSec = wav.nBlockAlign * wav.nSamplesPerSec;
|
|
wav.wFormatTag = WAVE_FORMAT_PCM;
|
|
mmr = waveOutOpen(&ciw->cdda_wavehandle, WAVE_MAPPER, &wav, 0, 0, WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT);
|
|
if (mmr != MMSYSERR_NOERROR) {
|
|
write_log(_T("IOCTL CDDA: wave open %d\n"), mmr);
|
|
cdda_closewav(ciw);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int ciw_cdda_setstate(struct cda_play *ciw, int state, int playpos)
|
|
{
|
|
ciw->cdda_play_state = state;
|
|
if (ciw->cdda_statusfunc)
|
|
return ciw->cdda_statusfunc(ciw->cdda_play_state, playpos);
|
|
return 0;
|
|
}
|
|
|
|
void ioctl_next_cd_audio_buffer_callback(int bufnum, void *param)
|
|
{
|
|
struct cda_play *ciw = (struct cda_play*)param;
|
|
uae_sem_wait(&play_sem);
|
|
if (bufnum >= 0) {
|
|
ciw->cda_bufon[bufnum] = 0;
|
|
bufnum = 1 - bufnum;
|
|
if (ciw->cda_bufon[bufnum])
|
|
audio_cda_new_buffer(&ciw->cas, (uae_s16 *)ciw->cda->buffers[bufnum], CDDA_BUFFERS * 2352 / 4, bufnum, ioctl_next_cd_audio_buffer_callback, ciw);
|
|
else
|
|
bufnum = -1;
|
|
}
|
|
if (bufnum < 0) {
|
|
audio_cda_new_buffer(&ciw->cas, NULL, 0, -1, NULL, ciw);
|
|
}
|
|
uae_sem_post(&play_sem);
|
|
}
|
|
|
|
static bool cdda_play2(struct cda_play *ciw, int *outpos)
|
|
{
|
|
int cdda_pos = ciw->cdda_start;
|
|
int bufnum;
|
|
int buffered;
|
|
int i;
|
|
int oldplay;
|
|
int idleframes;
|
|
int muteframes;
|
|
int readblocksize = 2352 + 96;
|
|
int mode = currprefs.sound_cdaudio;
|
|
bool restart = false;
|
|
|
|
while (ciw->cdda_play == 0)
|
|
sleep_millis(10);
|
|
oldplay = -1;
|
|
|
|
ciw->cda_bufon[0] = ciw->cda_bufon[1] = 0;
|
|
bufnum = 0;
|
|
buffered = 0;
|
|
|
|
memset(&ciw->cas, 0, sizeof(struct cd_audio_state));
|
|
ciw->cda = new cda_audio(CDDA_BUFFERS, 2352, 44100, mode != 0);
|
|
|
|
while (ciw->cdda_play > 0) {
|
|
|
|
if (mode) {
|
|
while (ciw->cda_bufon[bufnum] && ciw->cdda_play > 0) {
|
|
if (cd_audio_mode_changed) {
|
|
restart = true;
|
|
goto end;
|
|
}
|
|
sleep_millis(10);
|
|
}
|
|
} else {
|
|
ciw->cda->wait(bufnum);
|
|
}
|
|
if (ciw->cdda_play <= 0)
|
|
goto end;
|
|
ciw->cda_bufon[bufnum] = 0;
|
|
|
|
if (oldplay != ciw->cdda_play) {
|
|
idleframes = 0;
|
|
muteframes = 0;
|
|
bool seensub = false;
|
|
struct _timeb tb1, tb2;
|
|
_ftime(&tb1);
|
|
cdda_pos = ciw->cdda_start;
|
|
ciw->cd_last_pos = cdda_pos;
|
|
oldplay = ciw->cdda_play;
|
|
write_log(_T("CDDA: playing from %d to %d\n"), ciw->cdda_start, ciw->cdda_end);
|
|
ciw->subcodevalid = false;
|
|
idleframes = ciw->cdda_delay_frames;
|
|
while (ciw->cdda_paused && ciw->cdda_play > 0) {
|
|
sleep_millis(10);
|
|
idleframes = -1;
|
|
}
|
|
// force spin up
|
|
if (isaudiotrack(&ciw->di->toc, cdda_pos))
|
|
ciw->read_block(ciw, ciw->unitnum, ciw->cda->buffers[bufnum], cdda_pos, CDDA_BUFFERS, readblocksize);
|
|
if (!isaudiotrack(&ciw->di->toc, cdda_pos - 150))
|
|
muteframes = 75;
|
|
|
|
if (ciw->cdda_scan == 0) {
|
|
// find possible P-subchannel=1 and fudge starting point so that
|
|
// buggy CD32/CDTV software CD+G handling does not miss any frames
|
|
bool seenindex = false;
|
|
for (int sector = cdda_pos - 200; sector < cdda_pos; sector++) {
|
|
uae_u8 *dst = ciw->cda->buffers[bufnum];
|
|
if (sector >= 0 && isaudiotrack(&ciw->di->toc, sector) && ciw->read_block(ciw, ciw->unitnum, dst, sector, 1, readblocksize) > 0) {
|
|
uae_u8 subbuf[SUB_CHANNEL_SIZE];
|
|
sub_deinterleave(dst + 2352, subbuf);
|
|
if (seenindex) {
|
|
for (int i = 2 * SUB_ENTRY_SIZE; i < SUB_CHANNEL_SIZE; i++) {
|
|
if (subbuf[i]) { // non-zero R-W subchannels?
|
|
int diff = cdda_pos - sector + 2;
|
|
write_log(_T("-> CD+G start pos fudge -> %d (%d)\n"), sector, -diff);
|
|
idleframes -= diff;
|
|
cdda_pos = sector;
|
|
seensub = true;
|
|
break;
|
|
}
|
|
}
|
|
} else if (subbuf[0] == 0xff) { // P == 1?
|
|
seenindex = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cdda_pos -= idleframes;
|
|
|
|
if (*outpos < 0) {
|
|
_ftime(&tb2);
|
|
int diff = (tb2.time * (uae_s64)1000 + tb2.millitm) - (tb1.time * (uae_s64)1000 + tb1.millitm);
|
|
diff -= ciw->cdda_delay;
|
|
if (idleframes >= 0 && diff < 0 && ciw->cdda_play > 0)
|
|
sleep_millis(-diff);
|
|
if (diff > 0 && !seensub) {
|
|
int ch = diff / 7 + 25;
|
|
if (ch > idleframes)
|
|
ch = idleframes;
|
|
idleframes -= ch;
|
|
cdda_pos += ch;
|
|
}
|
|
ciw_cdda_setstate(ciw, AUDIO_STATUS_IN_PROGRESS, cdda_pos);
|
|
}
|
|
}
|
|
|
|
if ((cdda_pos < ciw->cdda_end || ciw->cdda_end == 0xffffffff) && !ciw->cdda_paused && ciw->cdda_play) {
|
|
|
|
if (idleframes <= 0 && cdda_pos >= ciw->cdda_start && !isaudiotrack(&ciw->di->toc, cdda_pos)) {
|
|
ciw_cdda_setstate(ciw, AUDIO_STATUS_PLAY_ERROR, -1);
|
|
write_log(_T("IOCTL: attempted to play data track %d\n"), cdda_pos);
|
|
goto end; // data track?
|
|
}
|
|
|
|
gui_flicker_led(LED_CD, ciw->unitnum - 1, LED_CD_AUDIO);
|
|
|
|
uae_sem_wait(&ciw->sub_sem);
|
|
|
|
ciw->subcodevalid = false;
|
|
memset(ciw->subcode, 0, sizeof ciw->subcode);
|
|
memset(ciw->cda->buffers[bufnum], 0, CDDA_BUFFERS * readblocksize);
|
|
|
|
if (cdda_pos >= 0) {
|
|
|
|
ciw_cdda_setstate(ciw, AUDIO_STATUS_IN_PROGRESS, cdda_pos);
|
|
|
|
int r = ciw->read_block(ciw, ciw->unitnum, ciw->cda->buffers[bufnum], cdda_pos, CDDA_BUFFERS, readblocksize);
|
|
if (r < 0) {
|
|
ciw_cdda_setstate(ciw, AUDIO_STATUS_PLAY_ERROR, -1);
|
|
uae_sem_post(&ciw->sub_sem);
|
|
goto end;
|
|
}
|
|
if (r > 0) {
|
|
for (i = 0; i < CDDA_BUFFERS; i++) {
|
|
memcpy(ciw->subcode + i * SUB_CHANNEL_SIZE, ciw->cda->buffers[bufnum] + readblocksize * i + 2352, SUB_CHANNEL_SIZE);
|
|
}
|
|
for (i = 1; i < CDDA_BUFFERS; i++) {
|
|
memmove(ciw->cda->buffers[bufnum] + 2352 * i, ciw->cda->buffers[bufnum] + readblocksize * i, 2352);
|
|
}
|
|
ciw->subcodevalid = true;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < CDDA_BUFFERS; i++) {
|
|
if (muteframes > 0) {
|
|
memset(ciw->cda->buffers[bufnum] + 2352 * i, 0, 2352);
|
|
muteframes--;
|
|
}
|
|
if (idleframes > 0) {
|
|
idleframes--;
|
|
memset(ciw->cda->buffers[bufnum] + 2352 * i, 0, 2352);
|
|
memset(ciw->subcode + i * SUB_CHANNEL_SIZE, 0, SUB_CHANNEL_SIZE);
|
|
} else if (cdda_pos < ciw->cdda_start && ciw->cdda_scan == 0) {
|
|
memset(ciw->cda->buffers[bufnum] + 2352 * i, 0, 2352);
|
|
}
|
|
}
|
|
if (idleframes > 0)
|
|
ciw->subcodevalid = false;
|
|
|
|
if (ciw->cdda_subfunc)
|
|
ciw->cdda_subfunc(ciw->subcode, CDDA_BUFFERS);
|
|
|
|
uae_sem_post(&ciw->sub_sem);
|
|
|
|
if (ciw->subcodevalid) {
|
|
uae_sem_wait(&ciw->sub_sem2);
|
|
memcpy(ciw->subcodebuf, ciw->subcode + (CDDA_BUFFERS - 1) * SUB_CHANNEL_SIZE, SUB_CHANNEL_SIZE);
|
|
uae_sem_post(&ciw->sub_sem2);
|
|
}
|
|
|
|
if (mode) {
|
|
if (ciw->cda_bufon[0] == 0 && ciw->cda_bufon[1] == 0) {
|
|
ciw->cda_bufon[bufnum] = 1;
|
|
ioctl_next_cd_audio_buffer_callback(1 - bufnum, ciw);
|
|
}
|
|
audio_cda_volume(&ciw->cas, ciw->cdda_volume[0], ciw->cdda_volume[1]);
|
|
ciw->cda_bufon[bufnum] = 1;
|
|
} else {
|
|
ciw->cda_bufon[bufnum] = 1;
|
|
ciw->cda->setvolume(ciw->cdda_volume[0], ciw->cdda_volume[1]);
|
|
if (!ciw->cda->play(bufnum)) {
|
|
ciw_cdda_setstate(ciw, AUDIO_STATUS_PLAY_ERROR, -1);
|
|
goto end; // data track?
|
|
}
|
|
}
|
|
|
|
if (ciw->cdda_scan) {
|
|
cdda_pos += ciw->cdda_scan * CDDA_BUFFERS;
|
|
if (cdda_pos < 0)
|
|
cdda_pos = 0;
|
|
} else {
|
|
if (cdda_pos < 0 && cdda_pos + CDDA_BUFFERS >= 0)
|
|
cdda_pos = 0;
|
|
else
|
|
cdda_pos += CDDA_BUFFERS;
|
|
}
|
|
|
|
if (idleframes <= 0) {
|
|
if (cdda_pos - CDDA_BUFFERS < ciw->cdda_end && cdda_pos >= ciw->cdda_end) {
|
|
cdda_pos = ciw->cdda_end;
|
|
ciw_cdda_setstate(ciw, AUDIO_STATUS_PLAY_COMPLETE, cdda_pos);
|
|
ciw->cdda_play_finished = 1;
|
|
ciw->cdda_play = -1;
|
|
}
|
|
ciw->cd_last_pos = cdda_pos;
|
|
}
|
|
}
|
|
|
|
if (ciw->cda_bufon[0] == 0 && ciw->cda_bufon[1] == 0) {
|
|
while (ciw->cdda_paused && ciw->cdda_play == oldplay)
|
|
sleep_millis(10);
|
|
}
|
|
|
|
if (cd_audio_mode_changed) {
|
|
restart = true;
|
|
goto end;
|
|
}
|
|
|
|
bufnum = 1 - bufnum;
|
|
|
|
}
|
|
|
|
end:
|
|
*outpos = cdda_pos;
|
|
if (mode) {
|
|
ioctl_next_cd_audio_buffer_callback(-1, ciw);
|
|
if (restart)
|
|
audio_cda_new_buffer(&ciw->cas, NULL, -1, -1, NULL, ciw);
|
|
} else {
|
|
ciw->cda->wait(0);
|
|
ciw->cda->wait(1);
|
|
}
|
|
|
|
ciw->subcodevalid = false;
|
|
cd_audio_mode_changed = false;
|
|
delete ciw->cda;
|
|
|
|
write_log(_T("CDDA: thread killed\n"));
|
|
return restart;
|
|
}
|
|
|
|
void ciw_cdda_play(void *v)
|
|
{
|
|
struct cda_play *ciw = (struct cda_play *)v;
|
|
int outpos = -1;
|
|
cd_audio_mode_changed = false;
|
|
for (;;) {
|
|
if (!cdda_play2(ciw, &outpos)) {
|
|
break;
|
|
}
|
|
ciw->cdda_start = outpos;
|
|
if (ciw->cdda_start + 150 >= ciw->cdda_end) {
|
|
if (ciw->cdda_play >= 0)
|
|
ciw_cdda_setstate(ciw, AUDIO_STATUS_PLAY_COMPLETE, ciw->cdda_end + 1);
|
|
ciw->cdda_play = -1;
|
|
break;
|
|
}
|
|
ciw->cdda_play = 1;
|
|
}
|
|
ciw->cdda_play = 0;
|
|
}
|
|
|
|
void ciw_cdda_stop(struct cda_play *ciw)
|
|
{
|
|
if (ciw->cdda_play != 0) {
|
|
ciw->cdda_play = -1;
|
|
while (ciw->cdda_play) {
|
|
sleep_millis(10);
|
|
}
|
|
}
|
|
ciw->cdda_play_finished = 0;
|
|
ciw->cdda_paused = 0;
|
|
ciw->subcodevalid = 0;
|
|
}
|