src/cdrom/macosx/SDL_syscdrom.c
author Ryan C. Gordon
Mon, 22 Aug 2005 14:38:31 +0000
changeset 1126 d581fe3f36db
parent 769 b8d311d90021
child 1312 c9b51268668f
permissions -rw-r--r--
Fix for bug reported by Michael Benfield on the SDL mailing list:

"I'm on Mac OS 10.3.9 with a CVS SDL 1.2.9.

My understanding is that SDL_CDResume is supposed to resume play after
calling SDL_CDPlay. It doesn't on my system. It returns 0 but nothing happens.

Any ideas?

Thanks.

Mike Benfield"

--ryan.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 #include "SDL_syscdrom_c.h"
    29 
    30 #pragma mark -- Globals --
    31 
    32 static FSRef**         tracks;
    33 static FSVolumeRefNum* volumes;
    34 static CDstatus        status;
    35 static int             nextTrackFrame;
    36 static int             nextTrackFramesRemaining;
    37 static int             fakeCD;
    38 static int             currentTrack;
    39 static int             didReadTOC;
    40 static int             cacheTOCNumTracks;
    41 static int             currentDrive; /* Only allow 1 drive in use at a time */
    42 
    43 #pragma mark -- Prototypes --
    44 
    45 static const char *SDL_SYS_CDName   (int drive);
    46 static int         SDL_SYS_CDOpen   (int drive);
    47 static int         SDL_SYS_CDGetTOC (SDL_CD *cdrom);
    48 static CDstatus    SDL_SYS_CDStatus (SDL_CD *cdrom, int *position);
    49 static int         SDL_SYS_CDPlay   (SDL_CD *cdrom, int start, int length);
    50 static int         SDL_SYS_CDPause  (SDL_CD *cdrom);
    51 static int         SDL_SYS_CDResume (SDL_CD *cdrom);
    52 static int         SDL_SYS_CDStop   (SDL_CD *cdrom);
    53 static int         SDL_SYS_CDEject  (SDL_CD *cdrom);
    54 static void        SDL_SYS_CDClose  (SDL_CD *cdrom);
    55 
    56 #pragma mark -- Helper Functions --
    57 
    58 /* Read a list of tracks from the volume */
    59 static int LoadTracks (SDL_CD *cdrom)
    60 {
    61     /* Check if tracks are already loaded */
    62     if  ( tracks[cdrom->id] != NULL )
    63         return 0;
    64         
    65     /* Allocate memory for tracks */
    66     tracks[cdrom->id] = (FSRef*) calloc (1, sizeof(**tracks) * cdrom->numtracks);
    67     if (tracks[cdrom->id] == NULL) {
    68         SDL_OutOfMemory ();
    69         return -1;
    70     }
    71     
    72     /* Load tracks */
    73     if (ListTrackFiles (volumes[cdrom->id], tracks[cdrom->id], cdrom->numtracks) < 0)
    74         return -1;
    75 
    76     return 0;
    77 }
    78 
    79 /* Find a file for a given start frame and length */
    80 static FSRef* GetFileForOffset (SDL_CD *cdrom, int start, int length,  int *outStartFrame, int *outStopFrame)
    81 {
    82     int i;
    83     
    84     for (i = 0; i < cdrom->numtracks; i++) {
    85     
    86         if (cdrom->track[i].offset <= start &&
    87             start < (cdrom->track[i].offset + cdrom->track[i].length))
    88             break;
    89     }
    90     
    91     if (i == cdrom->numtracks)
    92         return NULL;
    93         
    94     currentTrack = i;
    95 
    96     *outStartFrame = start - cdrom->track[i].offset;
    97     
    98     if ((*outStartFrame + length) < cdrom->track[i].length) {
    99         *outStopFrame = *outStartFrame + length;
   100         length = 0;
   101         nextTrackFrame = -1;
   102         nextTrackFramesRemaining = -1;
   103     }
   104     else {
   105         *outStopFrame = -1;
   106         length -= cdrom->track[i].length - *outStartFrame;
   107         nextTrackFrame = cdrom->track[i+1].offset;
   108         nextTrackFramesRemaining = length;
   109     }
   110     
   111     return &tracks[cdrom->id][i];
   112 }
   113 
   114 /* Setup another file for playback, or stop playback (called from another thread) */
   115 static void CompletionProc (SDL_CD *cdrom)
   116 {
   117     
   118     Lock ();
   119     
   120     if (nextTrackFrame > 0 && nextTrackFramesRemaining > 0) {
   121     
   122         /* Load the next file to play */
   123         int startFrame, stopFrame;
   124         FSRef *file;
   125         
   126         PauseFile ();
   127         ReleaseFile ();
   128                 
   129         file = GetFileForOffset (cdrom, nextTrackFrame, 
   130             nextTrackFramesRemaining, &startFrame, &stopFrame);
   131         
   132         if (file == NULL) {
   133             status = CD_STOPPED;
   134             Unlock ();
   135             return;
   136         }
   137         
   138         LoadFile (file, startFrame, stopFrame);
   139         
   140         SetCompletionProc (CompletionProc, cdrom);
   141         
   142         PlayFile ();
   143     }
   144     else {
   145     
   146         /* Release the current file */
   147         PauseFile ();
   148         ReleaseFile ();
   149         status = CD_STOPPED;
   150     }
   151     
   152     Unlock ();
   153 }
   154 
   155 
   156 #pragma mark -- Driver Functions --
   157 
   158 /* Initialize */
   159 int SDL_SYS_CDInit (void) 
   160 {
   161     /* Initialize globals */
   162     volumes = NULL;
   163     tracks  = NULL;
   164     status  = CD_STOPPED;
   165     nextTrackFrame = -1;
   166     nextTrackFramesRemaining = -1;
   167     fakeCD  = SDL_FALSE;
   168     currentTrack = -1;
   169     didReadTOC = SDL_FALSE;
   170     cacheTOCNumTracks = -1;
   171     currentDrive = -1;
   172     
   173     /* Fill in function pointers */
   174     SDL_CDcaps.Name   = SDL_SYS_CDName;
   175     SDL_CDcaps.Open   = SDL_SYS_CDOpen;
   176     SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
   177     SDL_CDcaps.Status = SDL_SYS_CDStatus;
   178     SDL_CDcaps.Play   = SDL_SYS_CDPlay;
   179     SDL_CDcaps.Pause  = SDL_SYS_CDPause;
   180     SDL_CDcaps.Resume = SDL_SYS_CDResume;
   181     SDL_CDcaps.Stop   = SDL_SYS_CDStop;
   182     SDL_CDcaps.Eject  = SDL_SYS_CDEject;
   183     SDL_CDcaps.Close  = SDL_SYS_CDClose;
   184 
   185     /* 
   186         Read the list of "drives"
   187         
   188         This is currently a hack that infers drives from
   189         mounted audio CD volumes, rather than
   190         actual CD-ROM devices - which means it may not
   191         act as expected sometimes.
   192     */
   193     
   194     /* Find out how many cd volumes are mounted */
   195     SDL_numcds = DetectAudioCDVolumes (NULL, 0);
   196 
   197     /*
   198         If there are no volumes, fake a cd device
   199         so tray empty can be reported.
   200     */
   201     if (SDL_numcds == 0) {
   202     
   203         fakeCD = SDL_TRUE;
   204         SDL_numcds = 1;
   205         status = CD_TRAYEMPTY;
   206         
   207         return 0;
   208     }
   209     
   210     /* Allocate space for volumes */
   211     volumes = (FSVolumeRefNum*) calloc (1, sizeof(*volumes) * SDL_numcds);
   212     if (volumes == NULL) {
   213         SDL_OutOfMemory ();
   214         return -1;
   215     }
   216     
   217     /* Allocate space for tracks */
   218     tracks = (FSRef**) calloc (1, sizeof(*tracks) * (SDL_numcds + 1));
   219     if (tracks == NULL) {
   220         SDL_OutOfMemory ();
   221         return -1;
   222     }
   223     
   224     /* Mark the end of the tracks array */
   225     tracks[ SDL_numcds ] = (FSRef*)-1;
   226     
   227     /* 
   228         Redetect, now save all volumes for later
   229         Update SDL_numcds just in case it changed
   230     */
   231     {
   232         int numVolumes = SDL_numcds;
   233         
   234         SDL_numcds = DetectAudioCDVolumes (volumes, numVolumes);
   235         
   236         /* If more cds suddenly show up, ignore them */
   237         if (SDL_numcds > numVolumes) {
   238             SDL_SetError ("Some CD's were added but they will be ignored");
   239             SDL_numcds = numVolumes;
   240         }
   241     }
   242     
   243     return 0;
   244 }
   245 
   246 /* Shutdown and cleanup */
   247 void SDL_SYS_CDQuit(void)
   248 {
   249     ReleaseFile();
   250     
   251     if (volumes != NULL)
   252         free (volumes);
   253         
   254     if (tracks != NULL) {
   255     
   256         FSRef **ptr;
   257         for (ptr = tracks; *ptr != (FSRef*)-1; ptr++)
   258             if (*ptr != NULL)
   259                 free (*ptr);
   260             
   261         free (tracks);
   262     }
   263 }
   264 
   265 /* Get the Unix disk name of the volume */
   266 static const char *SDL_SYS_CDName (int drive)
   267 {
   268     OSStatus     err = noErr;
   269     HParamBlockRec  pb;
   270     GetVolParmsInfoBuffer   volParmsInfo;
   271    
   272     if (fakeCD)
   273         return "Fake CD-ROM Device";
   274 
   275     pb.ioParam.ioNamePtr = NULL;
   276     pb.ioParam.ioVRefNum = volumes[drive];
   277     pb.ioParam.ioBuffer = (Ptr)&volParmsInfo;
   278     pb.ioParam.ioReqCount = (SInt32)sizeof(volParmsInfo);
   279     err = PBHGetVolParmsSync(&pb);
   280 
   281     if (err != noErr) {
   282         SDL_SetError ("PBHGetVolParmsSync returned %d", err);
   283         return NULL;
   284     }
   285 
   286     return volParmsInfo.vMDeviceID;
   287 }
   288 
   289 /* Open the "device" */
   290 static int SDL_SYS_CDOpen (int drive)
   291 {
   292     /* Only allow 1 device to be open */
   293     if (currentDrive >= 0) {
   294         SDL_SetError ("Only one cdrom is supported");
   295         return -1;
   296     }
   297     else
   298         currentDrive = drive;
   299 
   300     return drive;
   301 }
   302 
   303 /* Get the table of contents */
   304 static int SDL_SYS_CDGetTOC (SDL_CD *cdrom)
   305 {
   306     if (fakeCD) {
   307         SDL_SetError (kErrorFakeDevice);
   308         return -1;
   309     }
   310     
   311     if (didReadTOC) {
   312         cdrom->numtracks = cacheTOCNumTracks;
   313         return 0;
   314     }
   315     
   316     
   317     ReadTOCData (volumes[cdrom->id], cdrom);
   318     didReadTOC = SDL_TRUE;
   319     cacheTOCNumTracks = cdrom->numtracks;
   320     
   321     return 0;
   322 }
   323 
   324 /* Get CD-ROM status */
   325 static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position)
   326 {
   327     if (position) {
   328         int trackFrame;
   329         
   330         Lock ();
   331         trackFrame = GetCurrentFrame ();
   332         Unlock ();
   333     
   334         *position = cdrom->track[currentTrack].offset + trackFrame;
   335     }
   336     
   337     return status;
   338 }
   339 
   340 /* Start playback */
   341 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
   342 {
   343     int startFrame, stopFrame;
   344     FSRef *ref;
   345     
   346     if (fakeCD) {
   347         SDL_SetError (kErrorFakeDevice);
   348         return -1;
   349     }
   350     
   351     Lock();
   352     
   353     if (LoadTracks (cdrom) < 0)
   354         return -2;
   355     
   356     if (PauseFile () < 0)
   357         return -3;
   358         
   359     if (ReleaseFile () < 0)
   360         return -4;
   361     
   362     ref = GetFileForOffset (cdrom, start, length, &startFrame, &stopFrame);
   363     if (ref == NULL) {
   364         SDL_SetError ("SDL_SYS_CDPlay: No file for start=%d, length=%d", start, length);
   365         return -5;
   366     }
   367     
   368     if (LoadFile (ref, startFrame, stopFrame) < 0)
   369         return -6;
   370     
   371     SetCompletionProc (CompletionProc, cdrom);
   372     
   373     if (PlayFile () < 0)
   374         return -7;
   375     
   376     status = CD_PLAYING;
   377     
   378     Unlock();
   379     
   380     return 0;
   381 }
   382 
   383 /* Pause playback */
   384 static int SDL_SYS_CDPause(SDL_CD *cdrom)
   385 {
   386     if (fakeCD) {
   387         SDL_SetError (kErrorFakeDevice);
   388         return -1;
   389     }
   390     
   391     Lock ();
   392     
   393     if (PauseFile () < 0) {
   394         Unlock ();
   395         return -2;
   396     }
   397     
   398     status = CD_PAUSED;
   399     
   400     Unlock ();
   401     
   402     return 0;
   403 }
   404 
   405 /* Resume playback */
   406 static int SDL_SYS_CDResume(SDL_CD *cdrom)
   407 {
   408     if (fakeCD) {
   409         SDL_SetError (kErrorFakeDevice);
   410         return -1;
   411     }
   412     
   413     Lock ();
   414     
   415     if (PlayFile () < 0) {
   416         Unlock ();
   417         return -2;
   418     }
   419         
   420     status = CD_PLAYING;
   421     
   422     Unlock ();
   423     
   424     return 0;
   425 }
   426 
   427 /* Stop playback */
   428 static int SDL_SYS_CDStop(SDL_CD *cdrom)
   429 {
   430     if (fakeCD) {
   431         SDL_SetError (kErrorFakeDevice);
   432         return -1;
   433     }
   434     
   435     Lock ();
   436     
   437     if (PauseFile () < 0) {
   438         Unlock ();
   439         return -2;
   440     }
   441         
   442     if (ReleaseFile () < 0) {
   443         Unlock ();
   444         return -3;
   445     }
   446         
   447     status = CD_STOPPED;
   448     
   449     Unlock ();
   450     
   451     return 0;
   452 }
   453 
   454 /* Eject the CD-ROM (Unmount the volume) */
   455 static int SDL_SYS_CDEject(SDL_CD *cdrom)
   456 {
   457     OSStatus err;
   458 	HParamBlockRec  pb;
   459     
   460     if (fakeCD) {
   461         SDL_SetError (kErrorFakeDevice);
   462         return -1;
   463     }
   464     
   465     Lock ();
   466     
   467     if (PauseFile () < 0) {
   468         Unlock ();
   469         return -2;
   470     }
   471         
   472     if (ReleaseFile () < 0) {
   473         Unlock ();
   474         return -3;
   475     }
   476     
   477     status = CD_STOPPED;
   478     
   479 	// Eject the volume
   480 	pb.ioParam.ioNamePtr = NULL;
   481 	pb.ioParam.ioVRefNum = volumes[cdrom->id];
   482 	err = PBUnmountVol((ParamBlockRec *) &pb);
   483 
   484 	if (err != noErr) {
   485         Unlock ();
   486 		SDL_SetError ("PBUnmountVol returned %d", err);
   487 		return -4;
   488 	}
   489     
   490     status = CD_TRAYEMPTY;
   491 
   492     /* Invalidate volume and track info */
   493     volumes[cdrom->id] = 0;
   494     free (tracks[cdrom->id]);
   495     tracks[cdrom->id] = NULL;
   496     
   497     Unlock ();
   498     
   499     return 0;
   500 }
   501 
   502 /* Close the CD-ROM */
   503 static void SDL_SYS_CDClose(SDL_CD *cdrom)
   504 {
   505     currentDrive = -1;
   506     return;
   507 }
   508