ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/bincue_unix.cpp
Revision: 1.1
Committed: 2010-10-06T00:30:23Z (14 years, 1 month ago) by asvitkine
Branch: MAIN
CVS Tags: HEAD
Log Message:
[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

File Contents

# User Rev Content
1 asvitkine 1.1 /*
2     * Copyright (C) 2002-2010 The DOSBox Team
3     *
4     * This program is free software; you can redistribute it and/or modify
5     * it under the terms of the GNU General Public License as published by
6     * the Free Software Foundation; either version 2 of the License, or
7     * (at your option) any later version.
8     *
9     * This program is distributed in the hope that it will be useful,
10     * but WITHOUT ANY WARRANTY; without even the implied warranty of
11     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12     * GNU General Public License for more details.
13     *
14     * You should have received a copy of the GNU General Public License
15     * along with this program; if not, write to the Free Software
16     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17     */
18    
19     /* Geoffrey Brown 2010
20     * Includes ideas from dosbox src/dos/cdrom_image.cpp
21     *
22     * Limitations: 1) cue files must reference single bin file
23     * 2) only supports raw mode1 data and audio
24     * 3) no support for audio flags
25     * 4) requires SDL audio or OS X core audio
26     * 5) limited cue file keyword support
27     *
28     * Creating cue/bin files:
29     * cdrdao read-cd --read-raw --paranoia 3 foo.toc
30     * toc2cue foo.toc
31     */
32    
33     #include "sysdeps.h"
34     #include <stdio.h>
35     #include <stdlib.h>
36     #include <ctype.h>
37     #include <libgen.h>
38     #include <string.h>
39     #include <fcntl.h>
40     #include <unistd.h>
41     #include <sys/stat.h>
42     #include <errno.h>
43    
44     #ifdef OSX_CORE_AUDIO
45     #include "../MacOSX/MacOSX_sound_if.h"
46     static int bincue_core_audio_callback(void);
47     #endif
48    
49     #ifdef USE_SDL_AUDIO
50     #include <SDL.h>
51     #include <SDL_audio.h>
52     #endif
53    
54     #include "bincue_unix.h"
55     #define DEBUG 0
56     #include "debug.h"
57    
58     #define MAXTRACK 100
59     #define MAXLINE 512
60     #define CD_FRAMES 75
61     #define RAW_SECTOR_SIZE 2352
62     #define COOKED_SECTOR_SIZE 2048
63    
64     // Bits of Track Control Field -- These are standard for scsi cd players
65    
66     #define PREMPHASIS 0x1
67     #define COPY 0x2
68     #define DATA 0x4
69     #define AUDIO 0
70     #define FOURTRACK 0x8
71    
72     // Audio status -- These are standard for scsi cd players
73    
74     #define CDROM_AUDIO_INVALID 0x00
75     #define CDROM_AUDIO_PLAY 0x11
76     #define CDROM_AUDIO_PAUSED 0x12
77     #define CDROM_AUDIO_COMPLETED 0x13
78     #define CDROM_AUDIO_ERROR 0x14
79     #define CDROM_AUDIO_NO_STATUS 0x15
80    
81     typedef unsigned char uint8;
82    
83     // cuefiles can be challenging as some information is
84     // implied. For example, there may a pregap (also postgap)
85     // of silence that must be generated. Here we implement
86     // only the pregap.
87    
88     typedef struct {
89     int number;
90     unsigned int start; // Track start in frames
91     unsigned int length; // Track length in frames
92     loff_t fileoffset; // Track frame start within file
93     unsigned int pregap; // Silence in frames to generate
94     unsigned char tcf; // Track control field
95     } Track;
96    
97     typedef struct {
98     char *binfile; // Binary file name
99     unsigned int length; // file length in frames
100     int binfh; // binary file handle
101     int tcnt; // number of tracks
102     Track tracks[MAXTRACK];
103     } CueSheet;
104    
105     typedef struct {
106     CueSheet *cs; // cue sheet to play from
107     int audiofh; // file handle for audio data
108     unsigned int audioposition; // current position from audiostart (bytes)
109     unsigned int audiostart; // start position if playing (frame)
110     unsigned int audioend; // end position if playing (frames)
111     unsigned int silence; // pregap (silence) bytes
112     unsigned char audiostatus; // See defines above for status
113     loff_t fileoffset; // offset from file beginning to audiostart
114     #ifdef OSX_CORE_AUDIO
115     OSXsoundOutput soundoutput;
116     #endif
117     } CDPlayer;
118    
119     // Minute,Second,Frame data type
120    
121     typedef struct {
122     int m, s, f; // note size matters since we scan for %d !
123     } MSF;
124    
125     // Parser State
126    
127     static unsigned int totalPregap;
128     static unsigned int prestart;
129    
130     // Audio System State
131    
132     static bool audio_enabled = false;
133     static uint8 silence_byte;
134    
135    
136     // CD Player state. Note only one player is supported !
137    
138     static CDPlayer player;
139    
140     static void FramesToMSF(unsigned int frames, MSF *msf)
141     {
142     msf->m = frames/(60 * CD_FRAMES);
143     frames = frames%(60 * CD_FRAMES);
144     msf->s = frames/CD_FRAMES;
145     msf->f = frames%CD_FRAMES;
146     }
147    
148     static int MSFToFrames(MSF msf)
149     {
150     return (msf.m * 60 * CD_FRAMES) + (msf.s * CD_FRAMES) + msf.f;
151     }
152    
153    
154     static int PositionToTrack(CueSheet *cs, unsigned int position)
155     {
156     int i;
157     MSF msf;
158    
159     FramesToMSF(position, &msf);
160    
161     for (i = 0; i < cs->tcnt; i++) {
162     if ((position >= cs->tracks[i].start) &&
163     (position <= (cs->tracks[i].start + cs->tracks[i].length)))
164     break;
165     }
166     return i;
167     }
168    
169     static bool AddTrack(CueSheet *cs)
170     {
171     int skip = prestart;
172     Track *prev;
173     Track *curr = &(cs->tracks[cs->tcnt]);
174    
175     prestart = 0;
176    
177     if (skip > 0) {
178     if (skip > curr->start) {
179     D(bug("AddTrack: prestart > start\n"));
180     return false;
181     }
182     }
183    
184     curr->fileoffset = curr->start * RAW_SECTOR_SIZE;
185    
186     // now we patch up the indicated time
187    
188     curr->start += totalPregap;
189    
190     // curr->pregap is supposed to be part of this track, but it
191     // must be generated as silence
192    
193     totalPregap += curr->pregap;
194    
195     if (cs->tcnt == 0) {
196     if (curr->number != 1) {
197     D(bug("AddTrack: number != 1\n"));
198     return false;
199     }
200     cs->tcnt++;
201     return true;
202     }
203    
204     prev = &(cs->tracks[cs->tcnt - 1]);
205    
206     if (prev->start < skip)
207     prev->length = skip - prev->start - curr->pregap;
208     else
209     prev->length = curr->start - prev->start - curr->pregap;
210    
211     // error checks
212    
213     if (curr->number <= 1) {
214     D(bug("Bad track number %d\n", curr->number));
215     return false;
216     }
217     if ((prev->number + 1 != curr->number) && (curr->number != 0xAA)) {
218     D(bug("Bad track number %d\n", curr->number));
219     return false;
220     }
221     if (curr->start < prev->start + prev->length) {
222     D(bug("unexpected start %d\n", curr->start));
223     return false;
224     }
225    
226     cs->tcnt++;
227     return true;
228     }
229    
230     static bool ParseCueSheet(FILE *fh, CueSheet *cs, const char *cuefile)
231     {
232     bool seen1st = false;
233     char line[MAXLINE];
234     unsigned int i_line=0;
235     char *keyword;
236    
237     totalPregap = 0;
238     prestart = 0;
239    
240     while (fgets(line, MAXLINE, fh) != NULL) {
241     Track *curr = &cs->tracks[cs->tcnt];
242    
243     // check for CUE file
244    
245     if (!i_line && (strncmp("FILE", line, 4) != 0)) {
246     return false;
247     }
248     i_line++;
249    
250     // extract keyword
251    
252     if (NULL != (keyword = strtok(line, " \t\n\t"))) {
253     if (!strcmp("FILE", keyword)) {
254     char *filename;
255     char *filetype;
256    
257     if (i_line > 1) {
258     D(bug("More than one FILE token\n"));
259     goto fail;
260     }
261     filename = strtok(NULL, "\"\t\n\r");
262     filetype = strtok(NULL, " \"\t\n\r");
263     if (strcmp("BINARY", filetype)) {
264     D(bug("Not binary file %s", filetype));
265     goto fail;
266     }
267     else {
268     char *tmp = strdup(cuefile);
269     char *b = dirname(tmp);
270     cs->binfile = (char *) malloc(strlen(b) + strlen(filename) + 2);
271     sprintf(cs->binfile, "%s/%s", b, filename);
272     free(tmp);
273     }
274     } else if (!strcmp("TRACK", keyword)) {
275     char *field;
276     int i_track;
277    
278     if (seen1st) {
279     if (!AddTrack(cs)){
280     D(bug("AddTrack failed \n"));
281     goto fail;
282     }
283     curr = &cs->tracks[cs->tcnt];
284     }
285    
286     seen1st = true;
287    
288     // parse track number
289    
290     field = strtok(NULL, " \t\n\r");
291     if (1 != sscanf(field, "%d", &i_track)) {
292     D(bug("Expected track number\n"));
293     goto fail;
294     }
295     curr->number = i_track;
296    
297     // parse track type
298    
299     field = strtok(NULL, " \t\n\r");
300     if (!strcmp("MODE1/2352", field)) {
301     curr->tcf = DATA;
302     } else if (!strcmp("AUDIO", field)) {
303     curr->tcf = AUDIO;
304     } else {
305     D(bug("Unexpected track type %s", field));
306     goto fail;
307     }
308    
309     } else if (!strcmp("INDEX", keyword)) {
310     char *field;
311     int i_index;
312     MSF msf;
313    
314     // parse INDEX number
315    
316     field = strtok(NULL, " \t\n\r");
317     if (1 != sscanf(field, "%d", &i_index)) {
318     D(bug("Expected index number"));
319     goto fail;
320     }
321    
322     // parse INDEX start
323    
324     field = strtok(NULL, " \t\n\r");
325     if (3 != sscanf(field, "%d:%d:%d",
326     &msf.m, &msf.s, &msf.f)) {
327     D(bug("Expected index start frame\n"));
328     goto fail;
329     }
330    
331     if (i_index == 1)
332     curr->start = MSFToFrames(msf);
333     else if (i_index == 0)
334     prestart = MSFToFrames(msf);
335     } else if (!strcmp("PREGAP", keyword)) {
336     MSF msf;
337     char *field = strtok(NULL, " \t\n\r");
338     if (3 != sscanf(field, "%d:%d:%d",
339     &msf.m, &msf.s, &msf.f)) {
340     D(bug("Expected pregap frame\n"));
341     goto fail;
342     }
343     curr->pregap = MSFToFrames(msf);
344    
345     // Ignored directives
346    
347     } else if (!strcmp("TITLE", keyword)) {
348     } else if (!strcmp("PERFORMER", keyword)) {
349     } else if (!strcmp("REM", keyword)) {
350     } else if (!strcmp("ISRC", keyword)) {
351     } else if (!strcmp("SONGWRITER", keyword)) {
352     } else {
353     D(bug("Unexpected keyword %s\n", keyword));
354     goto fail;
355     }
356     }
357     }
358    
359     AddTrack(cs); // add final track
360     return true;
361     fail:
362     return false;
363     }
364    
365     static bool LoadCueSheet(const char *cuefile, CueSheet *cs)
366     {
367     FILE *fh = NULL;
368     int binfh = -1;
369     struct stat buf;
370     Track *tlast = NULL;
371    
372     if (cs) {
373     bzero(cs, sizeof(*cs));
374     if (!(fh = fopen(cuefile, "r")))
375     return false;
376    
377     if (!ParseCueSheet(fh, cs, cuefile)) goto fail;
378    
379     // Open bin file and find length
380    
381     if ((binfh = open(cs->binfile,O_RDONLY)) < 0) {
382     D(bug("Can't read bin file %s\n", cs->binfile));
383     goto fail;
384     }
385    
386     if (fstat(binfh, &buf)) {
387     D(bug("fstat returned error\n"));
388     goto fail;
389     }
390    
391     // compute length of final track
392    
393    
394     tlast = &cs->tracks[cs->tcnt - 1];
395     tlast->length = buf.st_size/RAW_SECTOR_SIZE
396     - tlast->start + totalPregap;
397    
398     if (tlast->length < 0) {
399     D(bug("Binary file too short \n"));
400     goto fail;
401     }
402    
403     // save bin file length and pointer
404    
405     cs->length = buf.st_size/RAW_SECTOR_SIZE;
406     cs->binfh = binfh;
407    
408     fclose(fh);
409     return true;
410    
411     fail:
412     if (binfh >= 0)
413     close(binfh);
414     fclose(fh);
415     free(cs->binfile);
416     return false;
417    
418     }
419     return false;
420     }
421    
422    
423    
424     void *open_bincue(const char *name)
425     {
426     CueSheet *cs;
427    
428     if (player.cs == NULL) {
429     cs = (CueSheet *) malloc(sizeof(CueSheet));
430     if (!cs) {
431     D(bug("malloc failed\n"));
432     return NULL;
433     }
434    
435     if (LoadCueSheet(name, cs)) {
436     player.cs = cs;
437     #ifdef OSX_CORE_AUDIO
438     audio_enabled = true;
439     #endif
440     if (audio_enabled)
441     player.audiostatus = CDROM_AUDIO_NO_STATUS;
442     else
443     player.audiostatus = CDROM_AUDIO_INVALID;
444     player.audiofh = dup(cs->binfh);
445     return cs;
446     }
447     else
448     free(cs);
449     }
450     return NULL;
451     }
452    
453     void close_bincue(void *fh)
454     {
455    
456    
457     }
458    
459     /*
460     * File read (cooked)
461     * Data are stored in raw sectors of which only COOKED_SECTOR_SIZE
462     * bytes are valid -- the remaining include 16 bytes at the beginning
463     * of each raw sector and RAW_SECTOR_SIZE - COOKED_SECTOR_SIZE - bytes
464     * at the end
465     *
466     * We assume that a read request can land in the middle of
467     * sector. We compute the byte address of that sector (sec)
468     * and the offset of the first byte we want within that sector (secoff)
469     *
470     * Reading is performed one raw sector at a time, extracting as many
471     * valid bytes as possible from that raw sector (available)
472     */
473    
474     size_t read_bincue(void *fh, void *b, loff_t offset, size_t len)
475     {
476     size_t bytes_read = 0; // bytes read so far
477     unsigned char *buf = (unsigned char *) b; // target buffer
478     unsigned char secbuf[RAW_SECTOR_SIZE]; // temporary buffer
479    
480     off_t sec = ((offset/COOKED_SECTOR_SIZE) * RAW_SECTOR_SIZE);
481     off_t secoff = offset % COOKED_SECTOR_SIZE;
482    
483     // sec contains location (in bytes) of next raw sector to read
484     // secoff contains offset within that sector at which to start
485     // reading since we can request a read that starts in the middle
486     // of a sector
487    
488     CueSheet *cs = (CueSheet *) fh;
489    
490     if (cs == NULL || lseek(cs->binfh, sec, SEEK_SET) < 0) {
491     return -1;
492     }
493     while (len) {
494    
495     // bytes available in next raw sector or len (bytes)
496     // we want whichever is less
497    
498     size_t available = COOKED_SECTOR_SIZE - secoff;
499     available = (available > len) ? len : available;
500    
501     // read the next raw sector
502    
503     if (read(cs->binfh, secbuf, RAW_SECTOR_SIZE) != RAW_SECTOR_SIZE) {
504     return bytes_read;
505     }
506    
507     // copy cooked sector bytes (skip first 16)
508     // we want out of those available
509    
510     bcopy(&secbuf[16+secoff], &buf[bytes_read], available);
511    
512     // next sector we start at the beginning
513    
514     secoff = 0;
515    
516     // increment running count decrement request
517    
518     bytes_read += available;
519     len -= available;
520     }
521     return bytes_read;
522     }
523    
524     loff_t size_bincue(void *fh)
525     {
526     if (fh) {
527     return ((CueSheet *)fh)->length * COOKED_SECTOR_SIZE;
528     }
529     }
530    
531     bool readtoc_bincue(void *fh, unsigned char *toc)
532     {
533     CueSheet *cs = (CueSheet *) fh;
534     if (cs) {
535    
536     MSF msf;
537     unsigned char *p = toc + 2;
538     *p++ = cs->tracks[0].number;
539     *p++ = cs->tracks[cs->tcnt - 1].number;
540     for (int i = 0; i < cs->tcnt; i++) {
541    
542     FramesToMSF(cs->tracks[i].start, &msf);
543     *p++ = 0;
544     *p++ = 0x10 | cs->tracks[i].tcf;
545     *p++ = cs->tracks[i].number;
546     *p++ = 0;
547     *p++ = 0;
548     *p++ = msf.m;
549     *p++ = msf.s;
550     *p++ = msf.f;
551     }
552     FramesToMSF(cs->length, &msf);
553     *p++ = 0;
554     *p++ = 0x14;
555     *p++ = 0xAA;
556     *p++ = 0;
557     *p++ = 0;
558     *p++ = msf.m;
559     *p++ = msf.s;
560     *p++ = msf.f;
561    
562     int toc_size = p - toc;
563     *toc++ = toc_size >> 8;
564     *toc++ = toc_size & 0xff;
565     return true;
566     }
567     }
568    
569     bool GetPosition_bincue(void *fh, uint8 *pos)
570     {
571     CueSheet *cs = (CueSheet *) fh;
572     if (cs && player.cs == cs) {
573     MSF abs, rel;
574     int fpos = player.audioposition / RAW_SECTOR_SIZE + player.audiostart;
575     int trackno = PositionToTrack(cs, fpos);
576    
577     if (!audio_enabled)
578     return false;
579    
580     FramesToMSF(fpos, &abs);
581     if (trackno < cs->tcnt) {
582     // compute position relative to start of frame
583    
584     unsigned int position = player.audioposition/RAW_SECTOR_SIZE +
585     player.audiostart - player.cs->tracks[trackno].start;
586    
587     FramesToMSF(position, &rel);
588     }
589     else
590     FramesToMSF(0, &rel);
591    
592     *pos++ = 0;
593     *pos++ = player.audiostatus;
594     *pos++ = 0;
595     *pos++ = 12; // Sub-Q data length
596     *pos++ = 0;
597     if (trackno < cs->tcnt)
598     *pos++ = 0x10 | cs->tracks[trackno].tcf;
599     *pos++ = (trackno < cs->tcnt) ? cs->tracks[trackno].number : 0xAA;
600     *pos++ = 1; // track index
601     *pos++ = 0;
602     *pos++ = abs.m;
603     *pos++ = abs.s;
604     *pos++ = abs.f;
605     *pos++ = 0;
606     *pos++ = rel.m;
607     *pos++ = rel.s;
608     *pos++ = rel.f;
609     *pos++ = 0;
610     // D(bug("CDROM position %02d:%02d:%02d track %02d\n", abs.m, abs.s, abs.f, trackno));
611     return true;
612     }
613     else
614     return false;
615     }
616    
617     bool CDPause_bincue(void *fh)
618     {
619     CueSheet *cs = (CueSheet *) fh;
620     if (cs && cs == player.cs) {
621     if (player.audiostatus == CDROM_AUDIO_PLAY) {
622     player.audiostatus = CDROM_AUDIO_PAUSED;
623     return true;
624     }
625     }
626     return false;
627     }
628    
629     bool CDStop_bincue(void *fh)
630     {
631     CueSheet *cs = (CueSheet *) fh;
632    
633     if (cs && cs == player.cs) {
634     #ifdef OSX_CORE_AUDIO
635     player.soundoutput.stop();
636     #endif
637     if (player.audiostatus != CDROM_AUDIO_INVALID)
638     player.audiostatus = CDROM_AUDIO_NO_STATUS;
639     return true;
640     }
641     return false;
642     }
643    
644     bool CDResume_bincue(void *fh)
645     {
646     CueSheet *cs = (CueSheet *) fh;
647     if (cs && cs == player.cs) {
648     if (player.audiostatus == CDROM_AUDIO_PAUSED) {
649     player.audiostatus = CDROM_AUDIO_PLAY;
650     return true;
651     }
652     }
653     return false;
654     }
655    
656     bool CDPlay_bincue(void *fh, uint8 start_m, uint8 start_s, uint8 start_f,
657     uint8 end_m, uint8 end_s, uint8 end_f)
658     {
659     CueSheet *cs = (CueSheet *)fh;
660     if (cs && cs == player.cs) {
661     int track;
662     MSF msf;
663    
664     #ifdef USE_SDL_AUDIO
665     SDL_LockAudio();
666     #endif
667    
668     player.audiostatus = CDROM_AUDIO_NO_STATUS;
669    
670     player.audiostart = (start_m * 60 * CD_FRAMES) +
671     (start_s * CD_FRAMES) + start_f;
672     player.audioend = (end_m * 60 * CD_FRAMES) + (end_s * CD_FRAMES) + end_f;
673    
674     track = PositionToTrack(player.cs, player.audiostart);
675    
676     if (track < player.cs->tcnt) {
677     player.audioposition = 0;
678    
679     // here we need to compute silence
680    
681     if (player.audiostart - player.cs->tracks[track].start >
682     player.cs->tracks[track].pregap)
683     player.silence = 0;
684     else
685     player.silence = (player.cs->tracks[track].pregap -
686     player.audiostart +
687     player.cs->tracks[track].start) * RAW_SECTOR_SIZE;
688    
689     player.fileoffset = player.cs->tracks[track].fileoffset;
690    
691     D(bug("file offset %d\n", (unsigned int) player.fileoffset));
692    
693     // fix up file offset if beyond the silence bytes
694    
695     if (!player.silence) // not at the beginning
696     player.fileoffset += (player.audiostart -
697     player.cs->tracks[track].start -
698     player.cs->tracks[track].pregap) * RAW_SECTOR_SIZE;
699    
700     FramesToMSF(player.cs->tracks[track].start, &msf);
701     D(bug("CDPlay_bincue track %02d start %02d:%02d:%02d silence %d",
702     player.cs->tracks[track].number, msf.m, msf.s, msf.f,
703     player.silence/RAW_SECTOR_SIZE));
704     D(bug(" Stop %02u:%02u:%02u\n", end_m, end_s, end_f));
705     }
706     else
707     D(bug("CDPlay_bincue: play beyond last track !\n"));
708    
709     #ifdef USE_SDL_AUDIO
710     SDL_UnlockAudio();
711     #endif
712    
713     if (audio_enabled) {
714     player.audiostatus = CDROM_AUDIO_PLAY;
715     #ifdef OSX_CORE_AUDIO
716     D(bug("starting os x sound"));
717     player.soundoutput.setCallback(bincue_core_audio_callback);
718     // should be from current track !
719     player.soundoutput.start(16, 2, 44100);
720     #endif
721     return true;
722     }
723     }
724     return false;
725     }
726    
727     static uint8 *fill_buffer(int stream_len)
728     {
729     static uint8 *buf = 0;
730     static int bufsize = 0;
731     int offset = 0;
732    
733     if (bufsize < stream_len) {
734     free(buf);
735     buf = (uint8 *) malloc(stream_len);
736     if (buf) {
737     bufsize = stream_len;
738     }
739     else {
740     D(bug("malloc failed \n"));
741     return NULL;
742     }
743     }
744    
745     memset(buf, silence_byte, stream_len);
746    
747     if (player.audiostatus == CDROM_AUDIO_PLAY) {
748     int remaining_silence = player.silence - player.audioposition;
749    
750     if (player.audiostart + player.audioposition/RAW_SECTOR_SIZE
751     >= player.audioend) {
752     player.audiostatus = CDROM_AUDIO_COMPLETED;
753     return buf;
754     }
755    
756     if (remaining_silence >= stream_len) {
757     player.audioposition += stream_len;
758     return buf;
759     }
760    
761     if (remaining_silence > 0) {
762     offset += remaining_silence;
763     player.audioposition += remaining_silence;
764     }
765    
766     int ret = 0;
767     int available = ((player.audioend - player.audiostart) *
768     RAW_SECTOR_SIZE) - player.audioposition;
769     if (available > (stream_len - offset))
770     available = stream_len - offset;
771    
772     if (lseek(player.audiofh,
773     player.fileoffset + player.audioposition - player.silence,
774     SEEK_SET) < 0)
775     return NULL;
776    
777     if (available < 0) {
778     player.audioposition += available; // correct end !;
779     available = 0;
780     }
781    
782     if ((ret = read(player.audiofh, &buf[offset], available)) >= 0) {
783     player.audioposition += ret;
784     offset += ret;
785     available -= ret;
786     }
787    
788     while (offset < stream_len) {
789     buf[offset++] = silence_byte;
790     if (available-- > 0){
791     player.audioposition++;
792     }
793     }
794     }
795     return buf;
796     }
797    
798    
799     #ifdef USE_SDL_AUDIO
800     void MixAudio_bincue(uint8 *stream, int stream_len)
801     {
802     uint8 *buf;
803     if (audio_enabled && (player.audiostatus == CDROM_AUDIO_PLAY)) {
804     if (buf = fill_buffer(stream_len))
805     SDL_MixAudio(stream, buf, stream_len, SDL_MIX_MAXVOLUME);
806     }
807     }
808    
809     void OpenAudio_bincue(int freq, int format, int channels, uint8 silence)
810     {
811     if (freq == 44100 && format == AUDIO_S16MSB && channels == 2) {
812     audio_enabled = true;
813     silence_byte = silence;
814     }
815     else {
816     D(bug("unexpected frequency %d , format %d, or channels %d\n",
817     freq, format, channels));
818     }
819     }
820     #endif
821    
822     #ifdef OSX_CORE_AUDIO
823     static int bincue_core_audio_callback(void)
824     {
825     int frames = player.soundoutput.bufferSizeFrames();
826     uint8 *buf = fill_buffer(frames*4);
827    
828     // D(bug("Audio request %d\n", stream_len));
829    
830     player.soundoutput.sendAudioBuffer((void *) buf, (buf ? frames : 0));
831    
832     return 1;
833     }
834     #endif