Home | History | Annotate | Download | only in macosx
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 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 (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 /* This is the Mac OS X / CoreAudio specific header for the SDL CD-ROM API
     25    Contributed by Darrell Walisser and Max Horn
     26  */
     27 
     28 /***********************************************************************************
     29  Implementation Notes
     30  *********************
     31 
     32     This code has several limitations currently (all of which are proabaly fixable):
     33 
     34     1. A CD-ROM device is inferred from a mounted cdfs volume, so device 0 is
     35        not necessarily the first CD-ROM device on the system. (Somewhat easy to fix
     36        by useing the device name from the volume id's to reorder the volumes)
     37 
     38     2. You can only open and control 1 CD-ROM device at a time. (Challenging to fix,
     39        due to extensive code restructuring)
     40 
     41     3. The status reported by SDL_CDStatus only changes to from CD_PLAYING to CD_STOPPED in
     42        1-second intervals (because the audio is buffered in 1-second chunks) If
     43        the audio data is less than 1 second, the remainder is filled with silence.
     44 
     45        If you need to play sequences back-to-back that are less that 1 second long,
     46        use the frame position to determine when to play the next sequence, instead
     47        of SDL_CDStatus.
     48 
     49        This may be possible to fix with a clever usage of the AudioUnit API.
     50 
     51     4. When new volumes are inserted, our volume information is not updated. The only way
     52        to refresh this information is to reinit the CD-ROM subsystem of SDL. To fix this,
     53        one would probably have to fix point 1 above first, then figure out how to register
     54        for a notification when new media is mounted in order to perform an automatic
     55        rescan for cdfs volumes.
     56 
     57 
     58 
     59     So, here comes a description of how this all works.
     60 
     61         < Initializing >
     62 
     63         To get things rolling, we have to locate mounted volumes that contain
     64         audio (since nearly all Macs don't have analog audio-in on the sound card).
     65         That's easy, since these volumes have a flag that indicates this special
     66         filesystem. See DetectAudioCDVolumes() in CDPlayer.cpp for this code.
     67 
     68         Next, we parse the invisible .TOC.plist in the root of the volume, which gets us
     69         the track information (number, offset, length, leadout, etc). See ReadTOCData() in
     70         CDPlayer.cpp for the skinny on this.
     71 
     72 
     73         < The Playback Loop >
     74 
     75         Now come the tricky parts. Let's start with basic audio playback. When a frame
     76         range to play is requested, we must first find the .aiff files on the volume,
     77         hopefully in the right order. Since these files all begin with a number "1 Audio Track",
     78         etc, this is used to determine the correct track order.
     79 
     80         Once all files are determined, we have to find what file corresponds to the start
     81         and length parameter to SDL_SYS_CDPlay(). Again, this is quite simple by walking the
     82         cdrom's track list. At this point, we also save the offset to the next track and frames
     83         remaining, if we're going to have to play another file after the first one. See
     84         GetFileForOffset() for this code.
     85 
     86         At this point we have all info needed to start playback, so we hand off to the LoadFile()
     87         function, which proceeds to do its magic and plays back the file.
     88 
     89         When the file is finished playing, CompletionProc() is invoked, at which time we can
     90         play the next file if the previously saved next track and frames remaining
     91         indicates that we should.
     92 
     93 
     94         < Magic >
     95 
     96         OK, so it's not really magic, but since I don't fully understand all the hidden details it
     97         seems like it to me ;-) The API's involved are the AudioUnit and AudioFile API's. These
     98         appear to be an extension of CoreAudio for creating modular playback and f/x entities.
     99         The important thing is that CPU usage is very low and reliability is very high. You'd
    100         be hard-pressed to find a way to stutter the playback with other CPU-intensive tasks.
    101 
    102         One part of this magic is that it uses multiple threads, which carries the usual potential
    103         for disaster if not handled carefully. Playback currently requires 4 additional threads:
    104             1. The coreaudio runloop thread
    105             2. The coreaudio device i/o thread
    106             3. The file streaming thread
    107             4. The notification/callback thread
    108 
    109         The first 2 threads are necessary evil - CoreAudio creates this no matter what the situation
    110         is (even the SDL sound implementation creates theses suckers). The last two are are created
    111         by us.
    112 
    113         The file is streamed from disk using a threaded double-buffer approach.
    114         This way, the high latency operation of reading from disk can be performed without interrupting
    115         the real-time device thread (which amounts to avoiding dropouts). The device thread grabs the
    116         buffer that isn't being read and sends it to the CoreAudio mixer where it eventually gets
    117         to the sound card.
    118 
    119         The device thread posts a notification when the file streaming thread is out of data. This
    120         notification must be handled in a separate thread to avoid potential deadlock in the
    121         device thread. That's where the notification thread comes in. This thread is signaled
    122         whenever a notification needs to be processed, so another file can be played back if need be.
    123 
    124         The API in CDPlayer.cpp contains synchronization because otherwise both the notification thread
    125         and main thread (or another other thread using the SDL CD api) can potentially call it at the same time.
    126 
    127 ************************************************************************************/
    128 
    129 
    130 #include "SDL_cdrom.h"
    131 #include "../SDL_syscdrom.h"
    132 
    133 #include "CDPlayer.h"
    134 
    135 #define kErrorFakeDevice "Error: Cannot proceed since we're faking a CD-ROM device. Reinit the CD-ROM subsystem to scan for new volumes."
    136 
    137