};
/* read table of contents */
-int sys_command_cd_toc (int unitnum, struct cd_toc_head *toc)
+int sys_command_cd_toc (int unitnum, struct cd_toc_head *th)
{
int v;
if (failunit (unitnum))
if (state[unitnum].device_func->toc == NULL) {
uae_u8 buf[4 + 8 * 103];
int size = sizeof buf;
- uae_u8 cmd [10] = { 0x43,0,2,0,0,0,0,(uae_u8)(size>>8),(uae_u8)(size&0xff),0};
- if (do_scsi (unitnum, cmd, sizeof cmd, buf, size)) {
- // toc parse to do
- v = 0;
+ uae_u8 cmd [10] = { 0x43,0,0,0,0,0,0,(uae_u8)(size>>8),(uae_u8)(size&0xff),0};
+ v = do_scsi(unitnum, cmd, sizeof cmd, buf, size);
+ if (v > 0) {
+ th->first_track = buf[2];
+ th->last_track = buf[3];
+ th->tracks = th->last_track - th->first_track + 1;
+ th->firstaddress = 0;
+ th->points = th->tracks + 1;
+ int len = (buf[0] << 8) | buf[1];
+ uae_u8 *p = buf + 4;
+ if ((th->tracks + 1) * 8 > len) {
+ freesem(unitnum);
+ return 0;
+ }
+ struct cd_toc *t = &th->toc[th->first_track];
+ for (int i = th->first_track; i <= th->last_track; i++) {
+ t->adr = p[1] >> 4;
+ t->control = p[1] & 15;
+ t->point = t->track = p[2];
+ t->paddress = (p[5] << 16) | (p[6] << 8) | (p[7] << 0);
+ p += 8;
+ t++;
+ }
+ th->lastaddress = (p[5] << 16) | (p[6] << 8) | (p[7] << 0);
+ t->track = t->point = 0xaa;
+ th->first_track_offset = 0;
+ th->last_track_offset = th->last_track;
+ v = 1;
}
- v = 0;
} else {
- v = state[unitnum].device_func->toc (unitnum, toc);
+ v = state[unitnum].device_func->toc (unitnum, th);
}
freesem (unitnum);
return v;
return status;
}
+static int emulate_cd_audio = 1;
+
+int blkdev_is_audio_command(uae_u8 cmd)
+{
+ if (!emulate_cd_audio)
+ return false;
+ // audio cd command?
+ switch (cmd)
+ {
+ case 0x42:
+ case 0x45:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4b:
+ case 0x4e:
+ case 0xa5:
+ case 0xa9:
+ case 0xba:
+ case 0xbc:
+ case 0xcd:
+ return 1;
+ }
+
+ // commands that won't stop cd audio
+ switch (cmd)
+ {
+ case 0x00:
+ case 0x03:
+ case 0x12:
+ case 0x15:
+ case 0x25:
+ case 0x35:
+ case 0x1a:
+ case 0x1e:
+ case 0x55:
+ case 0x5a:
+ return 0;
+ }
+
+ // all other commands stop cd audio
+ return -1;
+}
+
+
+int blkdev_execute_audio_command(int unitnum, uae_u8 *cdb, int cdblen, uae_u8 *inbuf, int inlen, uae_u8 *sense, int *senselen)
+{
+ int len = inlen;
+ uae_u8 reply[256];
+ int replylen = sizeof(reply);
+ int status = scsi_cd_emulate(unitnum, cdb, cdblen, inbuf, &len, reply, &replylen, sense, senselen, true);
+ if (status)
+ return -1;
+ return len;
+}
+
static int execscsicmd_direct (int unitnum, int type, struct amigascsi *as)
{
int io_error = 0;
get_long_host(scsicmd + 12), as.cmd_len,
as.flags,
get_long_host(scsicmd + 22), as.sense_len);
+ for (int i = 0; i < as.cmd_len; i++) {
+ if (i > 0)
+ write_log(_T("."));
+ write_log(_T("%02x"), as.cmd[i]);
+ }
+ write_log(_T("\n"));
}
ret = sys_command_scsi_direct_native (unitnum, type, &as);
extern struct device_functions devicefunc_scsi_spti;
extern struct device_functions devicefunc_cdimage;
+int blkdev_is_audio_command(uae_u8 cmd);
+int blkdev_execute_audio_command(int unitnum, uae_u8 *cdb, int cdblen, uae_u8 *inbuf, int inlen, uae_u8 *sense, int *senselen);
+
#endif /* UAE_BLKDEV_H */
#ifdef WINDDK
-#include <sys/timeb.h>
-
#include "options.h"
#include "traps.h"
#include "uae.h"
#include <ntddscsi.h>
#define IOCTL_DATA_BUFFER 8192
-#define CDDA_BUFFERS 14
struct dev_info_ioctl {
HANDLE h;
CDROM_TOC cdromtoc;
uae_u8 trackmode[100];
UINT errormode;
- int playend;
int fullaccess;
- int cdda_play_finished;
- int cdda_play;
- int cdda_paused;
- int cdda_volume[2];
- int cdda_scan;
- int cd_last_pos;
- HWAVEOUT cdda_wavehandle;
- int cdda_start, cdda_end;
- uae_u8 subcode[SUB_CHANNEL_SIZE * CDDA_BUFFERS];
- uae_u8 subcodebuf[SUB_CHANNEL_SIZE];
- bool subcodevalid;
- play_subchannel_callback cdda_subfunc;
- play_status_callback cdda_statusfunc;
- int cdda_play_state;
- int cdda_delay, cdda_delay_frames;
struct device_info di;
- uae_sem_t sub_sem, sub_sem2;
bool open;
bool usesptiread;
bool changed;
- cda_audio *cda;
- volatile int cda_bufon[2];
- struct cd_audio_state cas;
+ struct cda_play cda;
};
static struct dev_info_ioctl ciw32[MAX_TOTAL_SCSI_DEVICES];
return ret;
}
-static void cdda_closewav (struct dev_info_ioctl *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 dev_info_ioctl *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;
-}
-
-static int setstate (struct dev_info_ioctl *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 dev_info_ioctl *ciw = (struct dev_info_ioctl*)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 dev_info_ioctl *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("IOCTL%s CDDA: playing from %d to %d\n"),
- ciw->usesptiread ? _T("(SPTI)") : _T(""), 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))
- read_block (ciw, -1, 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) && read_block (ciw, -1, dst, sector, 1, readblocksize)) {
- 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;
- }
- 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)) {
- 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->di.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) {
-
- setstate(ciw, AUDIO_STATUS_IN_PROGRESS, cdda_pos);
-
- if (read_block (ciw, -1, ciw->cda->buffers[bufnum], cdda_pos, CDDA_BUFFERS, readblocksize)) {
- 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)) {
- 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;
- 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("IOCTL CDDA: thread killed\n"));
- return restart;
-}
-
-static void *cdda_play (void *v)
+static int read_block_cda(struct cda_play *cda, int unitnum, uae_u8 *data, int sector, int size, int sectorsize)
{
- int outpos = -1;
- struct dev_info_ioctl *ciw = (struct dev_info_ioctl*)v;
- 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)
- setstate (ciw, AUDIO_STATUS_PLAY_COMPLETE, ciw->cdda_end + 1);
- ciw->cdda_play = -1;
- break;
- }
- ciw->cdda_play = 1;
- }
- ciw->cdda_play = 0;
- return NULL;
-}
-
-static void cdda_stop (struct dev_info_ioctl *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;
+ return read_block(&ciw32[unitnum], unitnum, data, sector, size, sectorsize);
}
/* pause/unpause CD audio */
struct dev_info_ioctl *ciw = unitisopen (unitnum);
if (!ciw)
return -1;
- int old = ciw->cdda_paused;
- if ((paused && ciw->cdda_play) || !paused)
- ciw->cdda_paused = paused;
+ int old = ciw->cda.cdda_paused;
+ if ((paused && ciw->cda.cdda_play) || !paused)
+ ciw->cda.cdda_paused = paused;
return old;
}
if (!ciw)
return 0;
- cdda_stop (ciw);
+ ciw_cdda_stop(&ciw->cda);
return 1;
}
struct dev_info_ioctl *ciw = unitisopen (unitnum);
if (!ciw)
return -1;
- uae_u32 old = (ciw->cdda_volume[1] << 16) | (ciw->cdda_volume[0] << 0);
- ciw->cdda_volume[0] = volume_left;
- ciw->cdda_volume[1] = volume_right;
+ uae_u32 old = (ciw->cda.cdda_volume[1] << 16) | (ciw->cda.cdda_volume[0] << 0);
+ ciw->cda.cdda_volume[0] = volume_left;
+ ciw->cda.cdda_volume[1] = volume_right;
return old;
}
if (!ciw)
return 0;
- ciw->cdda_play_finished = 0;
- ciw->cdda_subfunc = subfunc;
- ciw->cdda_statusfunc = statusfunc;
- ciw->cdda_scan = scan > 0 ? 10 : (scan < 0 ? 10 : 0);
- ciw->cdda_delay = setstate (ciw, -1, -1);
- ciw->cdda_delay_frames = setstate (ciw, -2, -1);
- setstate (ciw, AUDIO_STATUS_NOT_SUPPORTED, -1);
+ ciw->cda.di = &ciw->di;
+ ciw->cda.cdda_play_finished = 0;
+ ciw->cda.cdda_subfunc = subfunc;
+ ciw->cda.cdda_statusfunc = statusfunc;
+ ciw->cda.cdda_scan = scan > 0 ? 10 : (scan < 0 ? 10 : 0);
+ ciw->cda.cdda_delay = ciw_cdda_setstate(&ciw->cda, -1, -1);
+ ciw->cda.cdda_delay_frames = ciw_cdda_setstate(&ciw->cda, -2, -1);
+ ciw_cdda_setstate(&ciw->cda, AUDIO_STATUS_NOT_SUPPORTED, -1);
+ ciw->cda.read_block = read_block_cda;
if (!open_createfile (ciw, 0)) {
- setstate (ciw, AUDIO_STATUS_PLAY_ERROR, -1);
+ ciw_cdda_setstate(&ciw->cda, AUDIO_STATUS_PLAY_ERROR, -1);
return 0;
}
if (!isaudiotrack (&ciw->di.toc, startlsn)) {
- setstate (ciw, AUDIO_STATUS_PLAY_ERROR, -1);
+ ciw_cdda_setstate(&ciw->cda, AUDIO_STATUS_PLAY_ERROR, -1);
return 0;
}
- if (!ciw->cdda_play) {
- uae_start_thread (_T("ioctl_cdda_play"), cdda_play, ciw, NULL);
+ if (!ciw->cda.cdda_play) {
+ uae_start_thread (_T("ioctl_cdda_play"), ciw_cdda_play, &ciw->cda, NULL);
}
- ciw->cdda_start = startlsn;
- ciw->cdda_end = endlsn;
- ciw->cd_last_pos = ciw->cdda_start;
- ciw->cdda_play++;
+ ciw->cda.cdda_start = startlsn;
+ ciw->cda.cdda_end = endlsn;
+ ciw->cda.cd_last_pos = ciw->cda.cdda_start;
+ ciw->cda.cdda_play++;
return 1;
}
p = buf;
status = AUDIO_STATUS_NO_STATUS;
- if (ciw->cdda_play) {
+ if (ciw->cda.cdda_play) {
status = AUDIO_STATUS_IN_PROGRESS;
- if (ciw->cdda_paused)
+ if (ciw->cda.cdda_paused)
status = AUDIO_STATUS_PAUSED;
- } else if (ciw->cdda_play_finished) {
+ } else if (ciw->cda.cdda_play_finished) {
status = AUDIO_STATUS_PLAY_COMPLETE;
}
p = buf + 4;
if (sector < 0)
- pos = ciw->cd_last_pos;
+ pos = ciw->cda.cd_last_pos;
else
pos = sector;
if (!regenerate) {
- if (sector < 0 && ciw->subcodevalid && ciw->cdda_play) {
- uae_sem_wait (&ciw->sub_sem2);
+ if (sector < 0 && ciw->cda.subcodevalid && ciw->cda.cdda_play) {
+ uae_sem_wait (&ciw->cda.sub_sem2);
uae_u8 subbuf[SUB_CHANNEL_SIZE];
- sub_deinterleave (ciw->subcodebuf, subbuf);
+ sub_deinterleave (ciw->cda.subcodebuf, subbuf);
memcpy (p, subbuf + 12, 12);
- uae_sem_post (&ciw->sub_sem2);
+ uae_sem_post (&ciw->cda.sub_sem2);
valid = true;
}
if (!valid && sector >= 0) {
DWORD len;
- uae_sem_wait (&ciw->sub_sem);
+ uae_sem_wait (&ciw->cda.sub_sem);
seterrormode (ciw);
RAW_READ_INFO rri;
rri.DiskOffset.QuadPart = 2048 * (pos + 0);
reseterrormode (ciw);
uae_u8 subbuf[SUB_CHANNEL_SIZE];
sub_deinterleave (ciw->tempbuffer + 2352, subbuf);
- uae_sem_post (&ciw->sub_sem);
+ uae_sem_post (&ciw->cda.sub_sem);
memcpy (p, subbuf + 12, 12);
valid = true;
}
if (log_scsi)
write_log (_T("IOCTL rawread unit=%d sector=%d blocksize=%d\n"), unitnum, sector, sectorsize);
- cdda_stop (ciw);
+ ciw_cdda_stop(&ciw->cda);
gui_flicker_led (LED_CD, unitnum, LED_CD_ACTIVE);
if (sectorsize > 0) {
if (sectorsize != 2336 && sectorsize != 2352 && sectorsize != 2048 &&
while (size-- > 0) {
if (!read_block (ciw, unitnum, data, sector, 1, sectorsize))
break;
- ciw->cd_last_pos = sector;
+ ciw->cda.cd_last_pos = sector;
data += sectorsize;
ret += sectorsize;
sector++;
reseterrormode (ciw);
return ret;
}
- ciw->cd_last_pos = sector;
+ ciw->cda.cd_last_pos = sector;
if (subs == 0) {
memcpy (data, p, blocksize);
if (ciw->usesptiread)
return ioctl_command_rawread (unitnum, data, sector, size, 2048, 0);
- cdda_stop (ciw);
+ ciw_cdda_stop(&ciw->cda);
DWORD dtotal;
int cnt = 3;
if (!open_createfile (ciw, 0))
return 0;
- ciw->cd_last_pos = sector;
+ ciw->cda.cd_last_pos = sector;
while (cnt-- > 0) {
LARGE_INTEGER offset;
gui_flicker_led (LED_CD, unitnum, LED_CD_ACTIVE);
if (!open_createfile (ciw, 0))
return 0;
- uae_sem_wait (&ciw->sub_sem);
+ uae_sem_wait (&ciw->cda.sub_sem);
seterrormode (ciw);
while (cnt-- > 0) {
if (!DeviceIoControl (ciw->h, IOCTL_CDROM_GET_DRIVE_GEOMETRY, NULL, 0, &geom, sizeof (geom), &len, NULL)) {
continue;
}
reseterrormode (ciw);
- uae_sem_post (&ciw->sub_sem);
+ uae_sem_post (&ciw->cda.sub_sem);
return 0;
}
break;
}
reseterrormode (ciw);
- uae_sem_post (&ciw->sub_sem);
+ uae_sem_post (&ciw->cda.sub_sem);
if (di) {
di->cylinders = geom.Cylinders.LowPart;
di->sectorspertrack = geom.SectorsPerTrack;
return 0;
if (!unitisopen (unitnum))
return 0;
- cdda_stop (ciw);
+ ciw_cdda_stop(&ciw->cda);
if (!open_createfile (ciw, 0))
return 0;
int ret = 0;
static int sys_cddev_open (struct dev_info_ioctl *ciw, int unitnum)
{
- ciw->cdda_volume[0] = 0x7fff;
- ciw->cdda_volume[1] = 0x7fff;
+ ciw->cda.cdda_volume[0] = 0x7fff;
+ ciw->cda.cdda_volume[1] = 0x7fff;
/* buffer must be page aligned for device access */
ciw->tempbuffer = (uae_u8*)VirtualAlloc (NULL, IOCTL_DATA_BUFFER, MEM_COMMIT, PAGE_READWRITE);
if (!ciw->tempbuffer) {
write_log (_T("Device blacklisted\n"));
goto error2;
}
- uae_sem_init (&ciw->sub_sem, 0, 1);
- uae_sem_init (&ciw->sub_sem2, 0, 1);
+ uae_sem_init (&ciw->cda.sub_sem, 0, 1);
+ uae_sem_init (&ciw->cda.sub_sem2, 0, 1);
//ciw->usesptiread = true;
ioctl_command_stop (unitnum);
update_device_info (unitnum);
{
if (ciw->open == false)
return;
- cdda_stop (ciw);
+ ciw_cdda_stop(&ciw->cda);
close_createfile (ciw);
VirtualFree (ciw->tempbuffer, 0, MEM_RELEASE);
ciw->tempbuffer = NULL;
- uae_sem_destroy (&ciw->sub_sem);
- uae_sem_destroy (&ciw->sub_sem2);
+ uae_sem_destroy (&ciw->cda.sub_sem);
+ uae_sem_destroy (&ciw->cda.sub_sem2);
ciw->open = false;
write_log (_T("IOCTL: device '%s' closed\n"), ciw->devname, unitnum);
}
*
*/
-
-#define WIN32_LEAN_AND_MEAN
-#define _WIN32_WINNT 0x500
-
#include "sysconfig.h"
#include "sysdeps.h"
#include "options.h"
#include <initguid.h> // Guid definition
#include <devguid.h> // Device guids
#include <setupapi.h> // for SetupDiXxx functions.
-
#include <ntddscsi.h>
+#include <mmsystem.h>
+
+#include "cda_play.h"
#define INQUIRY_SIZE 36
bool open;
bool enabled;
struct device_info di;
+ struct cda_play cda;
+ uae_u8 sense[32];
+ int senselen;
};
static uae_sem_t scgp_sem;
*err = 0;
if (log_scsi) {
- write_log (_T("SCSI, H=%X:%d:%d:%d:%d: "), di->handle, di->bus, di->path, di->target, di->lun);
+ write_log (_T("SCSI, H=%X:%d:%d:%d:%d:\n"), di->handle, di->bus, di->path, di->target, di->lun);
scsi_log_before (swb->spt.Cdb, swb->spt.CdbLength,
swb->spt.DataIn == SCSI_IOCTL_DATA_OUT ? (uae_u8*)swb->spt.DataBuffer : NULL, swb->spt.DataTransferLength);
+ for (int i = 0; i < swb->spt.CdbLength; i++) {
+ if (i > 0)
+ write_log(_T("."));
+ write_log(_T("%02x"), swb->spt.Cdb[i]);
+ }
+ write_log(_T("\n"));
}
gui_flicker_led (LED_CD, unitnum, 1);
swb->spt.ScsiStatus = 0;
#define MODE_SELECT_10 0x55
#define MODE_SENSE_10 0x5A
-static int execscsicmd (struct dev_info_spti *di, int unitnum, uae_u8 *data, int len, uae_u8 *inbuf, int inlen, int timeout, int *errp)
+static int execscsicmd (struct dev_info_spti *di, int unitnum, uae_u8 *data, int len, uae_u8 *inbuf, int inlen, int timeout, int *errp, int bypass)
{
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
DWORD status;
int err = 0;
int dolen;
+ if (!bypass) {
+ if (data[0] == 0x03 && di->senselen > 0) {
+ memcpy(inbuf, di->sense, di->senselen);
+ di->senselen = 0;
+ return di->senselen;
+ }
+ int r = blkdev_is_audio_command(data[0]);
+ if (r > 0) {
+ di->senselen = sizeof(di->sense);
+ return blkdev_execute_audio_command(unitnum, data, len, inbuf, inlen, di->sense, &di->senselen);
+ } else if (r < 0) {
+ ciw_cdda_stop(&di->cda);
+ }
+ }
+
uae_sem_wait (&scgp_sem);
memset (&swb, 0, sizeof (swb));
swb.spt.Length = sizeof (SCSI_PASS_THROUGH);
return dolen;
}
+static int read_block_cda(struct cda_play *cda, int unitnum, uae_u8 *data, int sector, int size, int sectorsize)
+{
+ struct dev_info_spti *di = unitisopen(unitnum);
+ if (!di)
+ return 0;
+ if (sectorsize != 2352 + 96)
+ return 0;
+ uae_u8 cmd[12] = { 0xbe, 0, (uae_u8)(sector >> 24), (uae_u8)(sector >> 16), (uae_u8)(sector >> 8), (uae_u8)(sector >> 0), (uae_u8)(size >> 16), (uae_u8)(size >> 8), (uae_u8)(size >> 0), 0x10, 1, 0 };
+ int err = 0;
+ int len = execscsicmd(di, unitnum, cmd, sizeof(cmd), data, size * sectorsize, -1, &err, true);
+ if (len >= 0)
+ return len;
+ return -1;
+}
+
static int execscsicmd_direct (int unitnum, struct amigascsi *as)
{
struct dev_info_spti *di = unitisopen (unitnum);
if (!di)
return -1;
+ if (as->cmd[0] == 0x03 && di->senselen > 0) {
+ memcpy(as->data, di->sense, di->senselen);
+ as->actual = di->senselen;
+ as->status = 0;
+ as->sactual = 0;
+ as->cmdactual = as->cmd_len;
+ di->senselen = 0;
+ return 0;
+ }
+ int r = blkdev_is_audio_command(as->cmd[0]);
+ if (r > 0) {
+ di->senselen = sizeof(di->sense);
+ int len = blkdev_execute_audio_command(unitnum, as->cmd, as->cmd_len, as->data, as->len, di->sense, &di->senselen);
+ as->actual = len;
+ as->cmdactual = as->cmd_len;
+ as->sactual = 0;
+ as->status = 0;
+ if (len < 0) {
+ /* copy sense? */
+ if (as->sense_len > 32)
+ as->sense_len = 32;
+ int senselen = (as->flags & 4) ? 4 : /* SCSIF_OLDAUTOSENSE */
+ (as->flags & 2) ? as->sense_len : /* SCSIF_AUTOSENSE */ 32;
+ if (senselen > 0) {
+ for (as->sactual = 0; as->sactual < di->senselen && as->sactual < senselen; as->sactual++) {
+ as->sensedata[as->sactual] = di->sense[as->sactual];
+ }
+ }
+ as->actual = 0;
+ as->status = 2;
+ return 45;
+ }
+ return 0;
+ } else if (r < 0) {
+ ciw_cdda_stop(&di->cda);
+ }
+
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
DWORD status;
int sactual = 0, i;
struct dev_info_spti *di = unitisopen (unitnum);
if (!di)
return 0;
- int v = execscsicmd (di, unitnum, data, len, 0, 0, -1, NULL);
+ int v = execscsicmd (di, unitnum, data, len, 0, 0, -1, NULL, false);
if (v < 0)
return 0;
return data;
struct dev_info_spti *di = unitisopen (unitnum);
if (!di)
return 0;
- int v = execscsicmd (di, unitnum, data, len, di->scsibuf, DEVICE_SCSI_BUFSIZE, -1, NULL);
+ int v = execscsicmd (di, unitnum, data, len, di->scsibuf, DEVICE_SCSI_BUFSIZE, -1, NULL, false);
if (v < 0)
return 0;
if (v == 0)
static uae_u8 *execscsicmd_in_internal (struct dev_info_spti *di, int unitnum, uae_u8 *data, int len, int *outlen, int timeout)
{
- int v = execscsicmd (di, unitnum, data, len, di->scsibuf, DEVICE_SCSI_BUFSIZE, timeout, NULL);
+ int v = execscsicmd (di, unitnum, data, len, di->scsibuf, DEVICE_SCSI_BUFSIZE, timeout, NULL, false);
if (v < 0)
return 0;
if (v == 0)
{
if (di->open == false)
return;
- if (di->handle != INVALID_HANDLE_VALUE)
- CloseHandle (di->handle);
+ if (di->handle != INVALID_HANDLE_VALUE) {
+ CloseHandle(di->handle);
+ uae_sem_destroy(&di->cda.sub_sem);
+ uae_sem_destroy(&di->cda.sub_sem2);
+ }
di->handle = INVALID_HANDLE_VALUE;
di->open = false;
}
uae_u8 cmd [6] = { 0,0,0,0,0,0 }; /* TEST UNIT READY */
if (di->open == false)
return -1;
- int v = execscsicmd (di, unitnum, cmd, sizeof cmd, 0, 0, 0, NULL);
+ int v = execscsicmd (di, unitnum, cmd, sizeof cmd, 0, 0, 0, NULL, false);
return v >= 0 ? 1 : 0;
}
dinfo->write_protected = (p[3] & 0x80) ? 1 : 0;
}
}
+ sys_command_cd_toc(unitnum, &di->di.toc);
// write_log (_T("mediacheck_full(%d,%d,%d,%d,%d)\n"),
// di->bytespersector, di->sectorspertrack, di->trackspercylinder, di->cylinders, di->write_protected);
return 1;
int err = 0;
uae_u8 inqdata[INQUIRY_SIZE + 1] = { 0 };
checkcapabilities (di);
- execscsicmd(di, unitnum, inqdata, 6, NULL, 0, 0, &err);
+ execscsicmd(di, unitnum, inqdata, 6, NULL, 0, 0, &err, false);
if (err) {
write_log(_T("SPTI: TUR failed unit %d, err=%d ('%s':%d:%d:%d:%d)\n"), unitnum, err, dev,
di->bus, di->path, di->target, di->lun);
update_device_info (unitnum);
if (di->type == INQ_ROMD)
blkdev_cd_change (unitnum, di->drvletter ? di->drvlettername : di->name);
+ di->cda.cdda_volume[0] = 0x7fff;
+ di->cda.cdda_volume[1] = 0x7fff;
+ uae_sem_init(&di->cda.sub_sem, 0, 1);
+ uae_sem_init(&di->cda.sub_sem2, 0, 1);
+ di->cda.cd_last_pos = 150;
return 1;
}
xfree (dev);
}
+/* pause/unpause CD audio */
+static int ioctl_command_pause(int unitnum, int paused)
+{
+ struct dev_info_spti *di = unitisopen(unitnum);
+ if (!di)
+ return -1;
+ int old = di->cda.cdda_paused;
+ if ((paused && di->cda.cdda_play) || !paused)
+ di->cda.cdda_paused = paused;
+ return old;
+}
+
+
+/* stop CD audio */
+static int ioctl_command_stop(int unitnum)
+{
+ struct dev_info_spti *di = unitisopen(unitnum);
+ if (!di)
+ return 0;
+
+ ciw_cdda_stop(&di->cda);
+
+ return 1;
+}
+
+static uae_u32 ioctl_command_volume(int unitnum, uae_u16 volume_left, uae_u16 volume_right)
+{
+ struct dev_info_spti *di = unitisopen(unitnum);
+ if (!di)
+ return -1;
+ uae_u32 old = (di->cda.cdda_volume[1] << 16) | (di->cda.cdda_volume[0] << 0);
+ di->cda.cdda_volume[0] = volume_left;
+ di->cda.cdda_volume[1] = volume_right;
+ return old;
+}
+
+/* play CD audio */
+static int ioctl_command_play(int unitnum, int startlsn, int endlsn, int scan, play_status_callback statusfunc, play_subchannel_callback subfunc)
+{
+ struct dev_info_spti *di = unitisopen(unitnum);
+ if (!di)
+ return 0;
+
+ di->cda.di = &di->di;
+ di->cda.subcodevalid = false;
+ di->cda.cdda_play_finished = 0;
+ di->cda.cdda_subfunc = subfunc;
+ di->cda.cdda_statusfunc = statusfunc;
+ di->cda.cdda_scan = scan > 0 ? 10 : (scan < 0 ? 10 : 0);
+ di->cda.cdda_delay = ciw_cdda_setstate(&di->cda, -1, -1);
+ di->cda.cdda_delay_frames = ciw_cdda_setstate(&di->cda, -2, -1);
+ ciw_cdda_setstate(&di->cda, AUDIO_STATUS_NOT_SUPPORTED, -1);
+ di->cda.read_block = read_block_cda;
+
+ if (!isaudiotrack(&di->di.toc, startlsn)) {
+ ciw_cdda_setstate(&di->cda, AUDIO_STATUS_PLAY_ERROR, -1);
+ return 0;
+ }
+ if (!di->cda.cdda_play) {
+ uae_start_thread(_T("ioctl_cdda_play"), ciw_cdda_play, &di->cda, NULL);
+ }
+ di->cda.cdda_start = startlsn;
+ di->cda.cdda_end = endlsn;
+ di->cda.cd_last_pos = di->cda.cdda_start;
+ di->cda.cdda_play++;
+
+ return 1;
+}
+
+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++;
+ }
+}
+
+/* read qcode */
+static int ioctl_command_qcode(int unitnum, uae_u8 *buf, int sector, bool all)
+{
+ struct dev_info_spti *di = unitisopen(unitnum);
+ if (!di)
+ return 0;
+ struct cda_play *cd = &di->cda;
+
+ if (all)
+ return 0;
+
+ memset(buf, 0, SUBQ_SIZE);
+ uae_u8 *p = buf;
+
+ int status = AUDIO_STATUS_NO_STATUS;
+ if (di->cda.cdda_play) {
+ status = AUDIO_STATUS_IN_PROGRESS;
+ if (di->cda.cdda_paused)
+ status = AUDIO_STATUS_PAUSED;
+ } else if (di->cda.cdda_play_finished) {
+ status = AUDIO_STATUS_PLAY_COMPLETE;
+ }
+
+ p[1] = status;
+ p[3] = 12;
+
+ p = buf + 4;
+
+ if (cd->subcodevalid) {
+ uae_sem_wait(&di->cda.sub_sem2);
+ uae_u8 subbuf[SUB_CHANNEL_SIZE];
+ sub_deinterleave(di->cda.subcodebuf, subbuf);
+ memcpy(p, subbuf + 12, 12);
+ uae_sem_post(&di->cda.sub_sem2);
+ } else {
+ int pos = di->cda.cd_last_pos;
+ int trk = cdtracknumber(&di->di.toc, pos);
+ struct cd_toc *t = &di->di.toc.toc[trk];
+ p[0] = (t->control << 4) | (t->adr << 0);
+ p[1] = tobcd(trk);
+ p[2] = tobcd(1);
+ uae_u32 msf = lsn2msf(pos);
+ tolongbcd(p + 7, msf);
+ msf = lsn2msf(pos - t->paddress - 150);
+ tolongbcd(p + 3, msf);
+ }
+
+ return 1;
+}
#endif
_T("SPTI"),
open_scsi_bus, close_scsi_bus, open_scsi_device, close_scsi_device, info_device,
execscsicmd_out, execscsicmd_in, execscsicmd_direct,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, check_isatapi, 0, 0
+ ioctl_command_pause, ioctl_command_stop, ioctl_command_play, ioctl_command_volume, ioctl_command_qcode,
+ 0, 0, 0, 0, check_isatapi, 0, 0
};
#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"
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;
+ return NULL;
+}
+
+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;
+}
#include <dsound.h>
#endif
+#include "audio.h"
+#include "blkdev.h"
+
extern volatile bool cd_audio_mode_changed;
class cda_audio
bool isplaying(int bufnum);
};
+#define CDDA_BUFFERS 14
+
+typedef int (*cda_play_read_block)(struct cda_play *, int unitnum, uae_u8 *data, int sector, int size, int sectorsize);
+
+struct cda_play
+{
+ int unitnum;
+ int cdda_volume[2];
+ HWAVEOUT cdda_wavehandle;
+ int cd_last_pos;
+ int cdda_play;
+ int cdda_play_finished;
+ int cdda_scan;
+ int cdda_paused;
+ int cdda_start, cdda_end;
+ play_subchannel_callback cdda_subfunc;
+ play_status_callback cdda_statusfunc;
+ int cdda_play_state;
+ int cdda_delay, cdda_delay_frames;
+ cda_audio *cda;
+ volatile int cda_bufon[2];
+ struct cd_audio_state cas;
+ bool subcodevalid;
+ uae_sem_t sub_sem, sub_sem2;
+ uae_u8 subcode[SUB_CHANNEL_SIZE * CDDA_BUFFERS];
+ uae_u8 subcodebuf[SUB_CHANNEL_SIZE];
+ struct device_info *di;
+ cda_play_read_block read_block;
+};
+
+void *ciw_cdda_play(void *ciw);
+void ciw_cdda_stop(struct cda_play *ciw);
+int ciw_cdda_setstate(struct cda_play *ciw, int state, int playpos);
\ No newline at end of file