mirror of
https://github.com/kanjitalk755/macemu.git
synced 2025-02-03 22:37:56 +00:00
[Geoffrey Brown]
Add bin/cue support. The following should work: 1) Basilisk and SheepShaver with sdl-audio and bincue on linux and os x 2) SheepShaver with bincue and core audio on os x
This commit is contained in:
parent
7a9a6d18f2
commit
90bdd12839
@ -32,6 +32,10 @@
|
||||
#define DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#if defined(BINCUE)
|
||||
#include "bincue_unix.h"
|
||||
#endif
|
||||
|
||||
|
||||
// The currently selected audio parameters (indices in audio_sample_rates[] etc. vectors)
|
||||
static int audio_sample_rate_index = 0;
|
||||
@ -91,6 +95,11 @@ static bool open_sdl_audio(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(BINCUE)
|
||||
OpenAudio_bincue(audio_spec.freq, audio_spec.format, audio_spec.channels,
|
||||
audio_spec.silence);
|
||||
#endif
|
||||
|
||||
char driver_name[32];
|
||||
printf("Using SDL/%s audio output\n", SDL_AudioDriverName(driver_name, sizeof(driver_name) - 1));
|
||||
silence_byte = audio_spec.silence;
|
||||
@ -218,6 +227,9 @@ static void stream_func(void *arg, uint8 *stream, int stream_len)
|
||||
// Audio not active, play silence
|
||||
silence: memset(stream, silence_byte, stream_len);
|
||||
}
|
||||
#if defined(BINCUE)
|
||||
MixAudio_bincue(stream, stream_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
834
BasiliskII/src/Unix/bincue_unix.cpp
Normal file
834
BasiliskII/src/Unix/bincue_unix.cpp
Normal file
@ -0,0 +1,834 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2010 The DOSBox Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* Geoffrey Brown 2010
|
||||
* Includes ideas from dosbox src/dos/cdrom_image.cpp
|
||||
*
|
||||
* Limitations: 1) cue files must reference single bin file
|
||||
* 2) only supports raw mode1 data and audio
|
||||
* 3) no support for audio flags
|
||||
* 4) requires SDL audio or OS X core audio
|
||||
* 5) limited cue file keyword support
|
||||
*
|
||||
* Creating cue/bin files:
|
||||
* cdrdao read-cd --read-raw --paranoia 3 foo.toc
|
||||
* toc2cue foo.toc
|
||||
*/
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef OSX_CORE_AUDIO
|
||||
#include "../MacOSX/MacOSX_sound_if.h"
|
||||
static int bincue_core_audio_callback(void);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SDL_AUDIO
|
||||
#include <SDL.h>
|
||||
#include <SDL_audio.h>
|
||||
#endif
|
||||
|
||||
#include "bincue_unix.h"
|
||||
#define DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#define MAXTRACK 100
|
||||
#define MAXLINE 512
|
||||
#define CD_FRAMES 75
|
||||
#define RAW_SECTOR_SIZE 2352
|
||||
#define COOKED_SECTOR_SIZE 2048
|
||||
|
||||
// Bits of Track Control Field -- These are standard for scsi cd players
|
||||
|
||||
#define PREMPHASIS 0x1
|
||||
#define COPY 0x2
|
||||
#define DATA 0x4
|
||||
#define AUDIO 0
|
||||
#define FOURTRACK 0x8
|
||||
|
||||
// Audio status -- These are standard for scsi cd players
|
||||
|
||||
#define CDROM_AUDIO_INVALID 0x00
|
||||
#define CDROM_AUDIO_PLAY 0x11
|
||||
#define CDROM_AUDIO_PAUSED 0x12
|
||||
#define CDROM_AUDIO_COMPLETED 0x13
|
||||
#define CDROM_AUDIO_ERROR 0x14
|
||||
#define CDROM_AUDIO_NO_STATUS 0x15
|
||||
|
||||
typedef unsigned char uint8;
|
||||
|
||||
// cuefiles can be challenging as some information is
|
||||
// implied. For example, there may a pregap (also postgap)
|
||||
// of silence that must be generated. Here we implement
|
||||
// only the pregap.
|
||||
|
||||
typedef struct {
|
||||
int number;
|
||||
unsigned int start; // Track start in frames
|
||||
unsigned int length; // Track length in frames
|
||||
loff_t fileoffset; // Track frame start within file
|
||||
unsigned int pregap; // Silence in frames to generate
|
||||
unsigned char tcf; // Track control field
|
||||
} Track;
|
||||
|
||||
typedef struct {
|
||||
char *binfile; // Binary file name
|
||||
unsigned int length; // file length in frames
|
||||
int binfh; // binary file handle
|
||||
int tcnt; // number of tracks
|
||||
Track tracks[MAXTRACK];
|
||||
} CueSheet;
|
||||
|
||||
typedef struct {
|
||||
CueSheet *cs; // cue sheet to play from
|
||||
int audiofh; // file handle for audio data
|
||||
unsigned int audioposition; // current position from audiostart (bytes)
|
||||
unsigned int audiostart; // start position if playing (frame)
|
||||
unsigned int audioend; // end position if playing (frames)
|
||||
unsigned int silence; // pregap (silence) bytes
|
||||
unsigned char audiostatus; // See defines above for status
|
||||
loff_t fileoffset; // offset from file beginning to audiostart
|
||||
#ifdef OSX_CORE_AUDIO
|
||||
OSXsoundOutput soundoutput;
|
||||
#endif
|
||||
} CDPlayer;
|
||||
|
||||
// Minute,Second,Frame data type
|
||||
|
||||
typedef struct {
|
||||
int m, s, f; // note size matters since we scan for %d !
|
||||
} MSF;
|
||||
|
||||
// Parser State
|
||||
|
||||
static unsigned int totalPregap;
|
||||
static unsigned int prestart;
|
||||
|
||||
// Audio System State
|
||||
|
||||
static bool audio_enabled = false;
|
||||
static uint8 silence_byte;
|
||||
|
||||
|
||||
// CD Player state. Note only one player is supported !
|
||||
|
||||
static CDPlayer player;
|
||||
|
||||
static void FramesToMSF(unsigned int frames, MSF *msf)
|
||||
{
|
||||
msf->m = frames/(60 * CD_FRAMES);
|
||||
frames = frames%(60 * CD_FRAMES);
|
||||
msf->s = frames/CD_FRAMES;
|
||||
msf->f = frames%CD_FRAMES;
|
||||
}
|
||||
|
||||
static int MSFToFrames(MSF msf)
|
||||
{
|
||||
return (msf.m * 60 * CD_FRAMES) + (msf.s * CD_FRAMES) + msf.f;
|
||||
}
|
||||
|
||||
|
||||
static int PositionToTrack(CueSheet *cs, unsigned int position)
|
||||
{
|
||||
int i;
|
||||
MSF msf;
|
||||
|
||||
FramesToMSF(position, &msf);
|
||||
|
||||
for (i = 0; i < cs->tcnt; i++) {
|
||||
if ((position >= cs->tracks[i].start) &&
|
||||
(position <= (cs->tracks[i].start + cs->tracks[i].length)))
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static bool AddTrack(CueSheet *cs)
|
||||
{
|
||||
int skip = prestart;
|
||||
Track *prev;
|
||||
Track *curr = &(cs->tracks[cs->tcnt]);
|
||||
|
||||
prestart = 0;
|
||||
|
||||
if (skip > 0) {
|
||||
if (skip > curr->start) {
|
||||
D(bug("AddTrack: prestart > start\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
curr->fileoffset = curr->start * RAW_SECTOR_SIZE;
|
||||
|
||||
// now we patch up the indicated time
|
||||
|
||||
curr->start += totalPregap;
|
||||
|
||||
// curr->pregap is supposed to be part of this track, but it
|
||||
// must be generated as silence
|
||||
|
||||
totalPregap += curr->pregap;
|
||||
|
||||
if (cs->tcnt == 0) {
|
||||
if (curr->number != 1) {
|
||||
D(bug("AddTrack: number != 1\n"));
|
||||
return false;
|
||||
}
|
||||
cs->tcnt++;
|
||||
return true;
|
||||
}
|
||||
|
||||
prev = &(cs->tracks[cs->tcnt - 1]);
|
||||
|
||||
if (prev->start < skip)
|
||||
prev->length = skip - prev->start - curr->pregap;
|
||||
else
|
||||
prev->length = curr->start - prev->start - curr->pregap;
|
||||
|
||||
// error checks
|
||||
|
||||
if (curr->number <= 1) {
|
||||
D(bug("Bad track number %d\n", curr->number));
|
||||
return false;
|
||||
}
|
||||
if ((prev->number + 1 != curr->number) && (curr->number != 0xAA)) {
|
||||
D(bug("Bad track number %d\n", curr->number));
|
||||
return false;
|
||||
}
|
||||
if (curr->start < prev->start + prev->length) {
|
||||
D(bug("unexpected start %d\n", curr->start));
|
||||
return false;
|
||||
}
|
||||
|
||||
cs->tcnt++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseCueSheet(FILE *fh, CueSheet *cs, const char *cuefile)
|
||||
{
|
||||
bool seen1st = false;
|
||||
char line[MAXLINE];
|
||||
unsigned int i_line=0;
|
||||
char *keyword;
|
||||
|
||||
totalPregap = 0;
|
||||
prestart = 0;
|
||||
|
||||
while (fgets(line, MAXLINE, fh) != NULL) {
|
||||
Track *curr = &cs->tracks[cs->tcnt];
|
||||
|
||||
// check for CUE file
|
||||
|
||||
if (!i_line && (strncmp("FILE", line, 4) != 0)) {
|
||||
return false;
|
||||
}
|
||||
i_line++;
|
||||
|
||||
// extract keyword
|
||||
|
||||
if (NULL != (keyword = strtok(line, " \t\n\t"))) {
|
||||
if (!strcmp("FILE", keyword)) {
|
||||
char *filename;
|
||||
char *filetype;
|
||||
|
||||
if (i_line > 1) {
|
||||
D(bug("More than one FILE token\n"));
|
||||
goto fail;
|
||||
}
|
||||
filename = strtok(NULL, "\"\t\n\r");
|
||||
filetype = strtok(NULL, " \"\t\n\r");
|
||||
if (strcmp("BINARY", filetype)) {
|
||||
D(bug("Not binary file %s", filetype));
|
||||
goto fail;
|
||||
}
|
||||
else {
|
||||
char *tmp = strdup(cuefile);
|
||||
char *b = dirname(tmp);
|
||||
cs->binfile = (char *) malloc(strlen(b) + strlen(filename) + 2);
|
||||
sprintf(cs->binfile, "%s/%s", b, filename);
|
||||
free(tmp);
|
||||
}
|
||||
} else if (!strcmp("TRACK", keyword)) {
|
||||
char *field;
|
||||
int i_track;
|
||||
|
||||
if (seen1st) {
|
||||
if (!AddTrack(cs)){
|
||||
D(bug("AddTrack failed \n"));
|
||||
goto fail;
|
||||
}
|
||||
curr = &cs->tracks[cs->tcnt];
|
||||
}
|
||||
|
||||
seen1st = true;
|
||||
|
||||
// parse track number
|
||||
|
||||
field = strtok(NULL, " \t\n\r");
|
||||
if (1 != sscanf(field, "%d", &i_track)) {
|
||||
D(bug("Expected track number\n"));
|
||||
goto fail;
|
||||
}
|
||||
curr->number = i_track;
|
||||
|
||||
// parse track type
|
||||
|
||||
field = strtok(NULL, " \t\n\r");
|
||||
if (!strcmp("MODE1/2352", field)) {
|
||||
curr->tcf = DATA;
|
||||
} else if (!strcmp("AUDIO", field)) {
|
||||
curr->tcf = AUDIO;
|
||||
} else {
|
||||
D(bug("Unexpected track type %s", field));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
} else if (!strcmp("INDEX", keyword)) {
|
||||
char *field;
|
||||
int i_index;
|
||||
MSF msf;
|
||||
|
||||
// parse INDEX number
|
||||
|
||||
field = strtok(NULL, " \t\n\r");
|
||||
if (1 != sscanf(field, "%d", &i_index)) {
|
||||
D(bug("Expected index number"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// parse INDEX start
|
||||
|
||||
field = strtok(NULL, " \t\n\r");
|
||||
if (3 != sscanf(field, "%d:%d:%d",
|
||||
&msf.m, &msf.s, &msf.f)) {
|
||||
D(bug("Expected index start frame\n"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (i_index == 1)
|
||||
curr->start = MSFToFrames(msf);
|
||||
else if (i_index == 0)
|
||||
prestart = MSFToFrames(msf);
|
||||
} else if (!strcmp("PREGAP", keyword)) {
|
||||
MSF msf;
|
||||
char *field = strtok(NULL, " \t\n\r");
|
||||
if (3 != sscanf(field, "%d:%d:%d",
|
||||
&msf.m, &msf.s, &msf.f)) {
|
||||
D(bug("Expected pregap frame\n"));
|
||||
goto fail;
|
||||
}
|
||||
curr->pregap = MSFToFrames(msf);
|
||||
|
||||
// Ignored directives
|
||||
|
||||
} else if (!strcmp("TITLE", keyword)) {
|
||||
} else if (!strcmp("PERFORMER", keyword)) {
|
||||
} else if (!strcmp("REM", keyword)) {
|
||||
} else if (!strcmp("ISRC", keyword)) {
|
||||
} else if (!strcmp("SONGWRITER", keyword)) {
|
||||
} else {
|
||||
D(bug("Unexpected keyword %s\n", keyword));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddTrack(cs); // add final track
|
||||
return true;
|
||||
fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool LoadCueSheet(const char *cuefile, CueSheet *cs)
|
||||
{
|
||||
FILE *fh = NULL;
|
||||
int binfh = -1;
|
||||
struct stat buf;
|
||||
Track *tlast = NULL;
|
||||
|
||||
if (cs) {
|
||||
bzero(cs, sizeof(*cs));
|
||||
if (!(fh = fopen(cuefile, "r")))
|
||||
return false;
|
||||
|
||||
if (!ParseCueSheet(fh, cs, cuefile)) goto fail;
|
||||
|
||||
// Open bin file and find length
|
||||
|
||||
if ((binfh = open(cs->binfile,O_RDONLY)) < 0) {
|
||||
D(bug("Can't read bin file %s\n", cs->binfile));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fstat(binfh, &buf)) {
|
||||
D(bug("fstat returned error\n"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// compute length of final track
|
||||
|
||||
|
||||
tlast = &cs->tracks[cs->tcnt - 1];
|
||||
tlast->length = buf.st_size/RAW_SECTOR_SIZE
|
||||
- tlast->start + totalPregap;
|
||||
|
||||
if (tlast->length < 0) {
|
||||
D(bug("Binary file too short \n"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// save bin file length and pointer
|
||||
|
||||
cs->length = buf.st_size/RAW_SECTOR_SIZE;
|
||||
cs->binfh = binfh;
|
||||
|
||||
fclose(fh);
|
||||
return true;
|
||||
|
||||
fail:
|
||||
if (binfh >= 0)
|
||||
close(binfh);
|
||||
fclose(fh);
|
||||
free(cs->binfile);
|
||||
return false;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *open_bincue(const char *name)
|
||||
{
|
||||
CueSheet *cs;
|
||||
|
||||
if (player.cs == NULL) {
|
||||
cs = (CueSheet *) malloc(sizeof(CueSheet));
|
||||
if (!cs) {
|
||||
D(bug("malloc failed\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (LoadCueSheet(name, cs)) {
|
||||
player.cs = cs;
|
||||
#ifdef OSX_CORE_AUDIO
|
||||
audio_enabled = true;
|
||||
#endif
|
||||
if (audio_enabled)
|
||||
player.audiostatus = CDROM_AUDIO_NO_STATUS;
|
||||
else
|
||||
player.audiostatus = CDROM_AUDIO_INVALID;
|
||||
player.audiofh = dup(cs->binfh);
|
||||
return cs;
|
||||
}
|
||||
else
|
||||
free(cs);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void close_bincue(void *fh)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* File read (cooked)
|
||||
* Data are stored in raw sectors of which only COOKED_SECTOR_SIZE
|
||||
* bytes are valid -- the remaining include 16 bytes at the beginning
|
||||
* of each raw sector and RAW_SECTOR_SIZE - COOKED_SECTOR_SIZE - bytes
|
||||
* at the end
|
||||
*
|
||||
* We assume that a read request can land in the middle of
|
||||
* sector. We compute the byte address of that sector (sec)
|
||||
* and the offset of the first byte we want within that sector (secoff)
|
||||
*
|
||||
* Reading is performed one raw sector at a time, extracting as many
|
||||
* valid bytes as possible from that raw sector (available)
|
||||
*/
|
||||
|
||||
size_t read_bincue(void *fh, void *b, loff_t offset, size_t len)
|
||||
{
|
||||
size_t bytes_read = 0; // bytes read so far
|
||||
unsigned char *buf = (unsigned char *) b; // target buffer
|
||||
unsigned char secbuf[RAW_SECTOR_SIZE]; // temporary buffer
|
||||
|
||||
off_t sec = ((offset/COOKED_SECTOR_SIZE) * RAW_SECTOR_SIZE);
|
||||
off_t secoff = offset % COOKED_SECTOR_SIZE;
|
||||
|
||||
// sec contains location (in bytes) of next raw sector to read
|
||||
// secoff contains offset within that sector at which to start
|
||||
// reading since we can request a read that starts in the middle
|
||||
// of a sector
|
||||
|
||||
CueSheet *cs = (CueSheet *) fh;
|
||||
|
||||
if (cs == NULL || lseek(cs->binfh, sec, SEEK_SET) < 0) {
|
||||
return -1;
|
||||
}
|
||||
while (len) {
|
||||
|
||||
// bytes available in next raw sector or len (bytes)
|
||||
// we want whichever is less
|
||||
|
||||
size_t available = COOKED_SECTOR_SIZE - secoff;
|
||||
available = (available > len) ? len : available;
|
||||
|
||||
// read the next raw sector
|
||||
|
||||
if (read(cs->binfh, secbuf, RAW_SECTOR_SIZE) != RAW_SECTOR_SIZE) {
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
// copy cooked sector bytes (skip first 16)
|
||||
// we want out of those available
|
||||
|
||||
bcopy(&secbuf[16+secoff], &buf[bytes_read], available);
|
||||
|
||||
// next sector we start at the beginning
|
||||
|
||||
secoff = 0;
|
||||
|
||||
// increment running count decrement request
|
||||
|
||||
bytes_read += available;
|
||||
len -= available;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
loff_t size_bincue(void *fh)
|
||||
{
|
||||
if (fh) {
|
||||
return ((CueSheet *)fh)->length * COOKED_SECTOR_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
bool readtoc_bincue(void *fh, unsigned char *toc)
|
||||
{
|
||||
CueSheet *cs = (CueSheet *) fh;
|
||||
if (cs) {
|
||||
|
||||
MSF msf;
|
||||
unsigned char *p = toc + 2;
|
||||
*p++ = cs->tracks[0].number;
|
||||
*p++ = cs->tracks[cs->tcnt - 1].number;
|
||||
for (int i = 0; i < cs->tcnt; i++) {
|
||||
|
||||
FramesToMSF(cs->tracks[i].start, &msf);
|
||||
*p++ = 0;
|
||||
*p++ = 0x10 | cs->tracks[i].tcf;
|
||||
*p++ = cs->tracks[i].number;
|
||||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
*p++ = msf.m;
|
||||
*p++ = msf.s;
|
||||
*p++ = msf.f;
|
||||
}
|
||||
FramesToMSF(cs->length, &msf);
|
||||
*p++ = 0;
|
||||
*p++ = 0x14;
|
||||
*p++ = 0xAA;
|
||||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
*p++ = msf.m;
|
||||
*p++ = msf.s;
|
||||
*p++ = msf.f;
|
||||
|
||||
int toc_size = p - toc;
|
||||
*toc++ = toc_size >> 8;
|
||||
*toc++ = toc_size & 0xff;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetPosition_bincue(void *fh, uint8 *pos)
|
||||
{
|
||||
CueSheet *cs = (CueSheet *) fh;
|
||||
if (cs && player.cs == cs) {
|
||||
MSF abs, rel;
|
||||
int fpos = player.audioposition / RAW_SECTOR_SIZE + player.audiostart;
|
||||
int trackno = PositionToTrack(cs, fpos);
|
||||
|
||||
if (!audio_enabled)
|
||||
return false;
|
||||
|
||||
FramesToMSF(fpos, &abs);
|
||||
if (trackno < cs->tcnt) {
|
||||
// compute position relative to start of frame
|
||||
|
||||
unsigned int position = player.audioposition/RAW_SECTOR_SIZE +
|
||||
player.audiostart - player.cs->tracks[trackno].start;
|
||||
|
||||
FramesToMSF(position, &rel);
|
||||
}
|
||||
else
|
||||
FramesToMSF(0, &rel);
|
||||
|
||||
*pos++ = 0;
|
||||
*pos++ = player.audiostatus;
|
||||
*pos++ = 0;
|
||||
*pos++ = 12; // Sub-Q data length
|
||||
*pos++ = 0;
|
||||
if (trackno < cs->tcnt)
|
||||
*pos++ = 0x10 | cs->tracks[trackno].tcf;
|
||||
*pos++ = (trackno < cs->tcnt) ? cs->tracks[trackno].number : 0xAA;
|
||||
*pos++ = 1; // track index
|
||||
*pos++ = 0;
|
||||
*pos++ = abs.m;
|
||||
*pos++ = abs.s;
|
||||
*pos++ = abs.f;
|
||||
*pos++ = 0;
|
||||
*pos++ = rel.m;
|
||||
*pos++ = rel.s;
|
||||
*pos++ = rel.f;
|
||||
*pos++ = 0;
|
||||
// D(bug("CDROM position %02d:%02d:%02d track %02d\n", abs.m, abs.s, abs.f, trackno));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDPause_bincue(void *fh)
|
||||
{
|
||||
CueSheet *cs = (CueSheet *) fh;
|
||||
if (cs && cs == player.cs) {
|
||||
if (player.audiostatus == CDROM_AUDIO_PLAY) {
|
||||
player.audiostatus = CDROM_AUDIO_PAUSED;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDStop_bincue(void *fh)
|
||||
{
|
||||
CueSheet *cs = (CueSheet *) fh;
|
||||
|
||||
if (cs && cs == player.cs) {
|
||||
#ifdef OSX_CORE_AUDIO
|
||||
player.soundoutput.stop();
|
||||
#endif
|
||||
if (player.audiostatus != CDROM_AUDIO_INVALID)
|
||||
player.audiostatus = CDROM_AUDIO_NO_STATUS;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDResume_bincue(void *fh)
|
||||
{
|
||||
CueSheet *cs = (CueSheet *) fh;
|
||||
if (cs && cs == player.cs) {
|
||||
if (player.audiostatus == CDROM_AUDIO_PAUSED) {
|
||||
player.audiostatus = CDROM_AUDIO_PLAY;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDPlay_bincue(void *fh, uint8 start_m, uint8 start_s, uint8 start_f,
|
||||
uint8 end_m, uint8 end_s, uint8 end_f)
|
||||
{
|
||||
CueSheet *cs = (CueSheet *)fh;
|
||||
if (cs && cs == player.cs) {
|
||||
int track;
|
||||
MSF msf;
|
||||
|
||||
#ifdef USE_SDL_AUDIO
|
||||
SDL_LockAudio();
|
||||
#endif
|
||||
|
||||
player.audiostatus = CDROM_AUDIO_NO_STATUS;
|
||||
|
||||
player.audiostart = (start_m * 60 * CD_FRAMES) +
|
||||
(start_s * CD_FRAMES) + start_f;
|
||||
player.audioend = (end_m * 60 * CD_FRAMES) + (end_s * CD_FRAMES) + end_f;
|
||||
|
||||
track = PositionToTrack(player.cs, player.audiostart);
|
||||
|
||||
if (track < player.cs->tcnt) {
|
||||
player.audioposition = 0;
|
||||
|
||||
// here we need to compute silence
|
||||
|
||||
if (player.audiostart - player.cs->tracks[track].start >
|
||||
player.cs->tracks[track].pregap)
|
||||
player.silence = 0;
|
||||
else
|
||||
player.silence = (player.cs->tracks[track].pregap -
|
||||
player.audiostart +
|
||||
player.cs->tracks[track].start) * RAW_SECTOR_SIZE;
|
||||
|
||||
player.fileoffset = player.cs->tracks[track].fileoffset;
|
||||
|
||||
D(bug("file offset %d\n", (unsigned int) player.fileoffset));
|
||||
|
||||
// fix up file offset if beyond the silence bytes
|
||||
|
||||
if (!player.silence) // not at the beginning
|
||||
player.fileoffset += (player.audiostart -
|
||||
player.cs->tracks[track].start -
|
||||
player.cs->tracks[track].pregap) * RAW_SECTOR_SIZE;
|
||||
|
||||
FramesToMSF(player.cs->tracks[track].start, &msf);
|
||||
D(bug("CDPlay_bincue track %02d start %02d:%02d:%02d silence %d",
|
||||
player.cs->tracks[track].number, msf.m, msf.s, msf.f,
|
||||
player.silence/RAW_SECTOR_SIZE));
|
||||
D(bug(" Stop %02u:%02u:%02u\n", end_m, end_s, end_f));
|
||||
}
|
||||
else
|
||||
D(bug("CDPlay_bincue: play beyond last track !\n"));
|
||||
|
||||
#ifdef USE_SDL_AUDIO
|
||||
SDL_UnlockAudio();
|
||||
#endif
|
||||
|
||||
if (audio_enabled) {
|
||||
player.audiostatus = CDROM_AUDIO_PLAY;
|
||||
#ifdef OSX_CORE_AUDIO
|
||||
D(bug("starting os x sound"));
|
||||
player.soundoutput.setCallback(bincue_core_audio_callback);
|
||||
// should be from current track !
|
||||
player.soundoutput.start(16, 2, 44100);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint8 *fill_buffer(int stream_len)
|
||||
{
|
||||
static uint8 *buf = 0;
|
||||
static int bufsize = 0;
|
||||
int offset = 0;
|
||||
|
||||
if (bufsize < stream_len) {
|
||||
free(buf);
|
||||
buf = (uint8 *) malloc(stream_len);
|
||||
if (buf) {
|
||||
bufsize = stream_len;
|
||||
}
|
||||
else {
|
||||
D(bug("malloc failed \n"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(buf, silence_byte, stream_len);
|
||||
|
||||
if (player.audiostatus == CDROM_AUDIO_PLAY) {
|
||||
int remaining_silence = player.silence - player.audioposition;
|
||||
|
||||
if (player.audiostart + player.audioposition/RAW_SECTOR_SIZE
|
||||
>= player.audioend) {
|
||||
player.audiostatus = CDROM_AUDIO_COMPLETED;
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (remaining_silence >= stream_len) {
|
||||
player.audioposition += stream_len;
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (remaining_silence > 0) {
|
||||
offset += remaining_silence;
|
||||
player.audioposition += remaining_silence;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
int available = ((player.audioend - player.audiostart) *
|
||||
RAW_SECTOR_SIZE) - player.audioposition;
|
||||
if (available > (stream_len - offset))
|
||||
available = stream_len - offset;
|
||||
|
||||
if (lseek(player.audiofh,
|
||||
player.fileoffset + player.audioposition - player.silence,
|
||||
SEEK_SET) < 0)
|
||||
return NULL;
|
||||
|
||||
if (available < 0) {
|
||||
player.audioposition += available; // correct end !;
|
||||
available = 0;
|
||||
}
|
||||
|
||||
if ((ret = read(player.audiofh, &buf[offset], available)) >= 0) {
|
||||
player.audioposition += ret;
|
||||
offset += ret;
|
||||
available -= ret;
|
||||
}
|
||||
|
||||
while (offset < stream_len) {
|
||||
buf[offset++] = silence_byte;
|
||||
if (available-- > 0){
|
||||
player.audioposition++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_SDL_AUDIO
|
||||
void MixAudio_bincue(uint8 *stream, int stream_len)
|
||||
{
|
||||
uint8 *buf;
|
||||
if (audio_enabled && (player.audiostatus == CDROM_AUDIO_PLAY)) {
|
||||
if (buf = fill_buffer(stream_len))
|
||||
SDL_MixAudio(stream, buf, stream_len, SDL_MIX_MAXVOLUME);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenAudio_bincue(int freq, int format, int channels, uint8 silence)
|
||||
{
|
||||
if (freq == 44100 && format == AUDIO_S16MSB && channels == 2) {
|
||||
audio_enabled = true;
|
||||
silence_byte = silence;
|
||||
}
|
||||
else {
|
||||
D(bug("unexpected frequency %d , format %d, or channels %d\n",
|
||||
freq, format, channels));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OSX_CORE_AUDIO
|
||||
static int bincue_core_audio_callback(void)
|
||||
{
|
||||
int frames = player.soundoutput.bufferSizeFrames();
|
||||
uint8 *buf = fill_buffer(frames*4);
|
||||
|
||||
// D(bug("Audio request %d\n", stream_len));
|
||||
|
||||
player.soundoutput.sendAudioBuffer((void *) buf, (buf ? frames : 0));
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
43
BasiliskII/src/Unix/bincue_unix.h
Normal file
43
BasiliskII/src/Unix/bincue_unix.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* bincue_unix.h -- support for cdrom image files in bin/cue format
|
||||
*
|
||||
* (C) 2010 Geoffrey Brown
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef BINCUE_H
|
||||
#define BINCUE_H
|
||||
|
||||
extern void *open_bincue(const char *name);
|
||||
extern bool readtoc_bincue(void *, uint8 *);
|
||||
extern size_t read_bincue(void *, void *, loff_t, size_t);
|
||||
extern loff_t size_bincue(void *);
|
||||
extern void close_bincue(void *);
|
||||
|
||||
extern bool GetPosition_bincue(void *, uint8 *);
|
||||
|
||||
extern bool CDPlay_bincue(void *, uint8, uint8,
|
||||
uint8, uint8, uint8, uint8);
|
||||
extern bool CDPause_bincue(void *);
|
||||
extern bool CDResume_bincue(void *);
|
||||
extern bool CDStop_bincue(void *);
|
||||
|
||||
#ifdef USE_SDL_AUDIO
|
||||
extern void OpenAudio_bincue(int, int, int, uint8);
|
||||
extern void MixAudio_bincue(uint8 *, int);
|
||||
#endif
|
||||
|
||||
#endif
|
@ -74,6 +74,9 @@ AC_ARG_WITH(gtk, [ --with-gtk use GTK user interface [
|
||||
[WANT_GTK="gtk2 gtk"])
|
||||
AC_ARG_WITH(mon, [ --with-mon use mon as debugger [default=yes]], [WANT_MON=$withval], [WANT_MON=yes])
|
||||
|
||||
AC_ARG_WITH(bincue,
|
||||
AS_HELP_STRING([--with-bincue], [Allow cdrom image files in bin/cue mode]))
|
||||
|
||||
dnl Canonical system information.
|
||||
AC_CANONICAL_HOST
|
||||
AC_CANONICAL_TARGET
|
||||
@ -248,6 +251,17 @@ if [[ "x$WANT_SDL" = "xno" ]]; then
|
||||
LIBS="$LIBS $X_PRE_LIBS $X_LIBS -lX11 -lXext $X_EXTRA_LIBS"
|
||||
fi
|
||||
|
||||
dnl BINCUE
|
||||
AS_IF([test "x$with_bincue" = "xyes" ], [have_bincue=yes], [have_bincue=no])
|
||||
AS_IF([test "x$have_bincue" = "xyes" ], [
|
||||
if [[ "x$WANT_SDL_AUDIO" = "xyes" ]]; then
|
||||
DEFINES="$DEFINES -DBINCUE"
|
||||
else
|
||||
AC_MSG_ERROR([You need SDL Audio to use BINCUE support.])
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
dnl We want pthreads. Try libpthread first, then libc_r (FreeBSD), then PTL.
|
||||
HAVE_PTHREADS=yes
|
||||
AC_CHECK_LIB(pthread, pthread_create, , [
|
||||
@ -697,6 +711,12 @@ if [[ "x$WANT_SDL_AUDIO" = "xyes" ]]; then
|
||||
AUDIOSRC="../SDL/audio_sdl.cpp"
|
||||
fi
|
||||
|
||||
dnl BINCUE overrides
|
||||
|
||||
if [[ "x$have_bincue" = "xyes" ]]; then
|
||||
EXTRASYSSRCS="$EXTRASYSSRCS bincue_unix.cpp"
|
||||
fi
|
||||
|
||||
dnl Use 68k CPU natively?
|
||||
WANT_NATIVE_M68K=no
|
||||
if [[ "x$HAVE_M68K" = "xyes" -a "x$CAN_NATIVE_M68K" = "xyes" ]]; then
|
||||
@ -1633,6 +1653,7 @@ echo
|
||||
echo Basilisk II configuration summary:
|
||||
echo
|
||||
echo SDL support ............................ : $SDL_SUPPORT
|
||||
echo BINCUE support ......................... : $have_bincue
|
||||
echo XFree86 DGA support .................... : $WANT_XF86_DGA
|
||||
echo XFree86 VidMode support ................ : $WANT_XF86_VIDMODE
|
||||
echo fbdev DGA support ...................... : $WANT_FBDEV_DGA
|
||||
|
@ -57,19 +57,25 @@
|
||||
#include "user_strings.h"
|
||||
#include "sys.h"
|
||||
|
||||
#if defined(BINCUE)
|
||||
#include "bincue_unix.h"
|
||||
#endif
|
||||
|
||||
#define DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
// File handles are pointers to these structures
|
||||
struct file_handle {
|
||||
char *name; // Copy of device/file name
|
||||
char *name; // Copy of device/file name
|
||||
int fd;
|
||||
bool is_file; // Flag: plain file or /dev/something?
|
||||
bool is_floppy; // Flag: floppy device
|
||||
bool is_cdrom; // Flag: CD-ROM device
|
||||
#if defined(BINCUE)
|
||||
bool is_bincue; // Flag: BIN CUE file
|
||||
#endif
|
||||
bool read_only; // Copy of Sys_open() flag
|
||||
loff_t start_byte; // Size of file header (if any)
|
||||
loff_t start_byte; // Size of file header (if any)
|
||||
loff_t file_size; // Size of file data (only valid if is_file is true)
|
||||
bool is_media_present; // Flag: media is inserted and available
|
||||
|
||||
@ -78,8 +84,12 @@ struct file_handle {
|
||||
#elif defined(__FreeBSD__)
|
||||
struct ioc_capability cdrom_cap;
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
char *ioctl_name; // For CDs on OS X - a device for special ioctls
|
||||
int ioctl_fd;
|
||||
char *ioctl_name; // For CDs on OS X - a device for special ioctls
|
||||
int ioctl_fd;
|
||||
#endif
|
||||
|
||||
#if defined(BINCUE)
|
||||
void *bincue_fd;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -438,6 +448,7 @@ bool cdrom_open(file_handle *fh, const char *path)
|
||||
|
||||
void cdrom_close(file_handle *fh)
|
||||
{
|
||||
|
||||
if (fh->fd >= 0) {
|
||||
close(fh->fd);
|
||||
fh->fd = -1;
|
||||
@ -549,6 +560,31 @@ void *Sys_open(const char *name, bool read_only)
|
||||
}
|
||||
|
||||
// Open file/device
|
||||
|
||||
#if defined(BINCUE)
|
||||
|
||||
void *binfd = open_bincue(name);
|
||||
if (binfd) {
|
||||
file_handle *fh = new file_handle;
|
||||
fh->fd = 0;
|
||||
fh->is_file = false;
|
||||
fh->name = strdup(name);
|
||||
fh->bincue_fd = binfd;
|
||||
fh->is_bincue = true;
|
||||
fh->read_only = true;
|
||||
fh->start_byte = 0;
|
||||
fh->is_floppy = false;
|
||||
fh->is_cdrom = false;
|
||||
fh->is_media_present = true;
|
||||
#if defined __MACOSX__
|
||||
fh->ioctl_fd = -1;
|
||||
fh->ioctl_name = NULL;
|
||||
#endif
|
||||
sys_add_file_handle(fh);
|
||||
return fh;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACOSX__)
|
||||
int fd = open(name, (read_only ? O_RDONLY : O_RDWR) | (is_cdrom ? O_NONBLOCK : 0));
|
||||
#else
|
||||
@ -569,9 +605,16 @@ void *Sys_open(const char *name, bool read_only)
|
||||
fh->is_floppy = is_floppy;
|
||||
fh->is_cdrom = is_cdrom;
|
||||
fh->is_media_present = false;
|
||||
#if defined __linux__
|
||||
fh->cdrom_cap = 0;
|
||||
#endif
|
||||
#if defined __MACOSX__
|
||||
fh->ioctl_fd = -1;
|
||||
fh->ioctl_name = NULL;
|
||||
#endif
|
||||
#if defined(BINCUE)
|
||||
fh->is_bincue = false;
|
||||
fh->bincue_fd = NULL;
|
||||
#endif
|
||||
if (fh->is_file) {
|
||||
fh->is_media_present = true;
|
||||
@ -646,6 +689,13 @@ void Sys_close(void *arg)
|
||||
|
||||
sys_remove_file_handle(fh);
|
||||
|
||||
#if defined(BINCUE)
|
||||
if (fh->is_bincue) {
|
||||
close_bincue(fh->bincue_fd);
|
||||
fh->bincue_fd = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fh->is_cdrom)
|
||||
cdrom_close(fh);
|
||||
if (fh->fd >= 0)
|
||||
@ -667,6 +717,12 @@ size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length)
|
||||
if (!fh)
|
||||
return 0;
|
||||
|
||||
#if defined(BINCUE)
|
||||
if (fh->is_bincue) {
|
||||
return read_bincue(fh->bincue_fd, buffer, offset, length);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Seek to position
|
||||
if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
|
||||
return 0;
|
||||
@ -709,8 +765,14 @@ loff_t SysGetFileSize(void *arg)
|
||||
if (fh->is_file)
|
||||
return fh->file_size;
|
||||
else {
|
||||
#if defined(__linux__)
|
||||
long blocks;
|
||||
#if defined(BINCUE)
|
||||
if (fh->is_bincue) {
|
||||
return size_bincue(fh->bincue_fd);
|
||||
}
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
|
||||
if (ioctl(fh->fd, BLKGETSIZE, &blocks) < 0)
|
||||
return 0;
|
||||
D(bug(" BLKGETSIZE returns %d blocks\n", blocks));
|
||||
@ -951,7 +1013,14 @@ bool SysCDReadTOC(void *arg, uint8 *toc)
|
||||
if (!fh)
|
||||
return false;
|
||||
|
||||
#if defined(BINCUE)
|
||||
if (fh->is_bincue){
|
||||
return readtoc_bincue(fh->bincue_fd, toc);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fh->is_cdrom) {
|
||||
|
||||
#if defined(__linux__)
|
||||
uint8 *p = toc + 2;
|
||||
|
||||
@ -1092,6 +1161,13 @@ bool SysCDGetPosition(void *arg, uint8 *pos)
|
||||
if (!fh)
|
||||
return false;
|
||||
|
||||
#if defined(BINCUE)
|
||||
if (fh->is_bincue) {
|
||||
|
||||
return GetPosition_bincue(fh->bincue_fd, pos);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fh->is_cdrom) {
|
||||
#if defined(__linux__)
|
||||
cdrom_subchnl chan;
|
||||
@ -1157,6 +1233,13 @@ bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end
|
||||
if (!fh)
|
||||
return false;
|
||||
|
||||
#if defined(BINCUE)
|
||||
if (fh->is_bincue) {
|
||||
return CDPlay_bincue(fh->bincue_fd, start_m, start_s,
|
||||
start_f, end_m, end_s, end_f);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fh->is_cdrom) {
|
||||
#if defined(__linux__)
|
||||
cdrom_msf play;
|
||||
@ -1194,6 +1277,12 @@ bool SysCDPause(void *arg)
|
||||
if (!fh)
|
||||
return false;
|
||||
|
||||
#if defined(BINCUE)
|
||||
if (fh->is_bincue){
|
||||
return CDPause_bincue(fh->bincue_fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fh->is_cdrom) {
|
||||
#if defined(__linux__)
|
||||
return ioctl(fh->fd, CDROMPAUSE) == 0;
|
||||
@ -1217,6 +1306,13 @@ bool SysCDResume(void *arg)
|
||||
if (!fh)
|
||||
return false;
|
||||
|
||||
#if defined(BINCUE)
|
||||
if (fh->is_bincue) {
|
||||
return CDResume_bincue(fh->bincue_fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (fh->is_cdrom) {
|
||||
#if defined(__linux__)
|
||||
return ioctl(fh->fd, CDROMRESUME) == 0;
|
||||
@ -1240,6 +1336,13 @@ bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f)
|
||||
if (!fh)
|
||||
return false;
|
||||
|
||||
#if defined(BINCUE)
|
||||
if (fh->is_bincue) {
|
||||
return CDStop_bincue(fh->bincue_fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (fh->is_cdrom) {
|
||||
#if defined(__linux__)
|
||||
return ioctl(fh->fd, CDROMSTOP) == 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user