]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
SPTI CD/DVD mode emulated CD audio.
authorToni Wilen <twilen@winuae.net>
Sat, 21 Sep 2019 07:36:51 +0000 (10:36 +0300)
committerToni Wilen <twilen@winuae.net>
Sat, 21 Sep 2019 07:36:51 +0000 (10:36 +0300)
blkdev.cpp
include/blkdev.h
od-win32/blkdev_win32_ioctl.cpp
od-win32/blkdev_win32_spti.cpp
od-win32/cda_play.cpp
od-win32/cda_play.h

index 1646ee37541cbc2b1cc02b0287df0292ee66d19a..7005e05de5d9d74573b8dbca2c2e3b1773c13caf 100644 (file)
@@ -880,7 +880,7 @@ int sys_command_cd_qcode (int unitnum, uae_u8 *buf, int sector, bool all)
 };
 
 /* 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))
@@ -890,14 +890,37 @@ int sys_command_cd_toc (int unitnum, struct cd_toc_head *toc)
        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;
@@ -2160,6 +2183,62 @@ end:
        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;
@@ -2276,6 +2355,12 @@ int sys_command_scsi_direct(TrapContext *ctx, int unitnum, int type, uaecptr acm
                        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);
index 112467549450580abc9bb7bb25fb98adbcc224f0..d27456ddcbe0e5b3a9a58572153230d48269dc34 100644 (file)
@@ -227,4 +227,7 @@ extern struct device_functions devicefunc_scsi_ioctl;
 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 */
index 0598ee1208ddf728bd45182340d50cc44990dc9d..90654e37cd1be061722bb7e426ab54b28df232d5 100644 (file)
@@ -12,8 +12,6 @@
 
 #ifdef WINDDK
 
-#include <sys/timeb.h>
-
 #include "options.h"
 #include "traps.h"
 #include "uae.h"
@@ -40,7 +38,6 @@
 #include <ntddscsi.h>
 
 #define IOCTL_DATA_BUFFER 8192
-#define CDDA_BUFFERS 14
 
 struct dev_info_ioctl {
        HANDLE h;
@@ -52,31 +49,12 @@ struct dev_info_ioctl {
        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];
@@ -451,333 +429,9 @@ retry:
        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 */
@@ -786,9 +440,9 @@ static int ioctl_command_pause (int unitnum, int paused)
        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;
 }
 
@@ -800,7 +454,7 @@ static int ioctl_command_stop (int unitnum)
        if (!ciw)
                return 0;
 
-       cdda_stop (ciw);
+       ciw_cdda_stop(&ciw->cda);
                
        return 1;
 }
@@ -810,9 +464,9 @@ static uae_u32 ioctl_command_volume (int unitnum, uae_u16 volume_left, uae_u16 v
        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;
 }
 
@@ -823,29 +477,31 @@ static int ioctl_command_play (int unitnum, int startlsn, int endlsn, int scan,
        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;
 }
@@ -874,11 +530,11 @@ static int ioctl_command_qcode (int unitnum, uae_u8 *buf, int sector, bool all)
        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;
        }
 
@@ -888,22 +544,22 @@ static int ioctl_command_qcode (int unitnum, uae_u8 *buf, int sector, bool all)
        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);
@@ -917,7 +573,7 @@ static int ioctl_command_qcode (int unitnum, uae_u8 *buf, int sector, bool all)
                        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;
                }
@@ -966,7 +622,7 @@ static int ioctl_command_rawread (int unitnum, uae_u8 *data, int sector, int siz
 
        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 &&
@@ -975,7 +631,7 @@ static int ioctl_command_rawread (int unitnum, uae_u8 *data, int sector, int siz
                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++;
@@ -1009,7 +665,7 @@ static int ioctl_command_rawread (int unitnum, uae_u8 *data, int sector, int siz
                                        reseterrormode (ciw);
                                        return ret;
                                }
-                               ciw->cd_last_pos = sector;
+                               ciw->cda.cd_last_pos = sector;
 
                                if (subs == 0) {
                                        memcpy (data, p, blocksize);
@@ -1050,7 +706,7 @@ static int ioctl_command_readwrite (int unitnum, int sector, int size, int write
        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;
@@ -1059,7 +715,7 @@ static int ioctl_command_readwrite (int unitnum, int sector, int size, int write
 
        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);
@@ -1137,7 +793,7 @@ static int fetch_geometry (struct dev_info_ioctl *ciw, int unitnum, struct devic
 
        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)) {
@@ -1148,13 +804,13 @@ static int fetch_geometry (struct dev_info_ioctl *ciw, int unitnum, struct devic
                                        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;
@@ -1178,7 +834,7 @@ static int eject (int unitnum, bool eject)
                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;
@@ -1328,8 +984,8 @@ static void trim (TCHAR *s)
 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) {
@@ -1436,8 +1092,8 @@ static int sys_cddev_open (struct dev_info_ioctl *ciw, int unitnum)
                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);
@@ -1458,12 +1114,12 @@ static void sys_cddev_close (struct dev_info_ioctl *ciw, int 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);
 }
index 1dfc0d0018ee12acbf02dd996978de44a9794a4c..502fd94d8e949f80ffa1597085942d28dee98bd2 100644 (file)
@@ -7,10 +7,6 @@
 *
 */
 
-
-#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
 
@@ -64,6 +62,9 @@ struct dev_info_spti {
        bool open;
        bool enabled;
        struct device_info di;
+       struct cda_play cda;
+       uae_u8 sense[32];
+       int senselen;
 };
 
 static uae_sem_t scgp_sem;
@@ -112,9 +113,15 @@ static int doscsi (struct dev_info_spti *di, int unitnum, SCSI_PASS_THROUGH_DIRE
 
        *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;
@@ -152,13 +159,28 @@ static int doscsi (struct dev_info_spti *di, int unitnum, SCSI_PASS_THROUGH_DIRE
 #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);
@@ -185,12 +207,64 @@ static int execscsicmd (struct dev_info_spti *di, int unitnum, uae_u8 *data, int
        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;
@@ -267,7 +341,7 @@ static uae_u8 *execscsicmd_out (int unitnum, uae_u8 *data, int len)
        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;
@@ -278,7 +352,7 @@ static uae_u8 *execscsicmd_in (int unitnum, uae_u8 *data, int len, int *outlen)
        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)
@@ -290,7 +364,7 @@ static uae_u8 *execscsicmd_in (int unitnum, uae_u8 *data, int len, int *outlen)
 
 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)
@@ -304,8 +378,11 @@ static void close_scsi_device2 (struct dev_info_spti *di)
 {
        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;
 }
@@ -370,7 +447,7 @@ static int mediacheck (struct dev_info_spti *di, int unitnum)
        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;
 }
 
@@ -403,6 +480,7 @@ static int mediacheck_full (struct dev_info_spti *di, int unitnum, struct device
                        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;
@@ -514,7 +592,7 @@ static int open_scsi_device2 (struct dev_info_spti *di, int unitnum)
                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);
@@ -556,6 +634,11 @@ static int open_scsi_device2 (struct dev_info_spti *di, int unitnum)
                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);
@@ -893,6 +976,138 @@ static int rescan (void)
 }
 
 
+/* 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
 
 
@@ -900,5 +1115,6 @@ struct device_functions devicefunc_scsi_spti = {
        _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
 };
index 35c630993461249e8932a48f8f61a2659843c61e..c73243573da894279511cd4010d8d173145ab2c6 100644 (file)
 #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"
 
@@ -254,3 +258,368 @@ bool cda_audio::isplaying(int bufnum)
                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;
+}
index d36d2a7863f24f93a16ca7b35af11b9d1be047af..d290fc8c944cb39a9ddb8d6b2f1932a0730f3dc8 100644 (file)
@@ -3,6 +3,9 @@
 #include <dsound.h>
 #endif
 
+#include "audio.h"
+#include "blkdev.h"
+
 extern volatile bool cd_audio_mode_changed;
 
 class cda_audio
@@ -35,3 +38,36 @@ public:
        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