home *** CD-ROM | disk | FTP | other *** search
- /*
- * DACPlayer.h
- * Implementation of an object to play sound over the soundout device (DACs).
- *
- * You may freely copy, distribute, and reuse the code in this example.
- * NeXT disclaims any warranty of any kind, expressed or implied, as to its
- * fitness for any particular use.
- *
- * Written by: Robert Poor
- * Created: Sep/92
- */
-
- #import <sound/sounddriver.h>
- #import <sound/soundstruct.h>
-
- /* Tag for DMA messages going to sndout */
- #define WRITE_TAG 2
-
- #define HIGH_WATER ((512+256)*1024)
- #define LOW_WATER (512 * 1024)
- #define READ_BUF_SIZE (vm_page_size / sizeof(short))
-
- /* The states that the recorder can be in. */
- typedef enum {
- PLA_STOPPED, /* stopped, ports & resources freed */
- PLA_PAUSED, /* ports allocated, awaiting DMA size from host */
- PLA_RUNNING, /* running... */
- PLA_STOPPING, /* draining remaining regions before stopping */
- N_PLA_STATES
- } Pla_state_t;
-
- /*
- * values for dacPlayerFlags
- */
- typedef struct {
- unsigned int willPlay:1; /* does delegate respond to willPlay? */
- unsigned int didPlay:1; /* ... */
- unsigned int playData:1;
- unsigned int didChangeState:1;
- } dacplayer_flags_t;
-
-
- @interface DACPlayer:Object
- {
- Pla_state_t playerState; /* current state of the player object */
- int regionsQueued; /* # of regions currently queued */
- int bytesPlayed;
- int bytesQueued;
-
- id delegate; /* the target of notification messages */
- int samplingRate; /* sampling rate 44100 or 22050 */
- int regionSize; /* # of bytes per call to playData */
- int regionCount; /* # of regions to be queued in advance */
- /*
- * It's not necessarily safe to update parameters while the DAC is
- * running, so we squirrel away the user-settable parameters and update
- * from them only when it's safe...
- */
- int newSamplingRate, newRegionSize, newRegionCount;
-
- port_t devicePort;
- port_t ownerPort;
- port_t streamPort;
- port_t replyPort;
- dacplayer_flags_t flags; /* various bits */
- }
-
- - init;
- - free;
-
- /*
- * METHODS THAT CONTROL DACPLAYER
- */
-
- - prepare;
- /*
- * Prepares the DACPlayer object for playing.
- *
- * Upon entry, if the DACPlayer is running, we stop it first with a call
- * to [dacPlayer stop]. The prepare method then acquires all the resources
- * it needs (ports, soundout, etc), and calls [delegate willPlay:self].
- * Even though the stream is left in a "paused" state, the delegate method
- * [delegate playData:::] will be called regionCount times to fill up the
- * region queue. The DACPlayer state is set to PLA_PAUSED.
- *
- * Returns nil if some resource couldn't be acquired.
- */
-
- - run;
- /*
- * Starts (or resumes) the DACPlayer. If the state is PLA_STOPPED, then
- * the run method first calls [self prepare] to set up the stream. Otherwise,
- * if the state is anything but PLA_PAUSED, run simply returns nil.
- *
- * Otherwise, the stream is "unpaused" and the DACPlayer will start sending
- * data to the DACs. The DACPlayer will start calling he delegate method
- * [delegate playData:::] to fetch the data to be played.
- *
- * The DACPlayer state is set to PLA_RUNNING.
- */
-
- - pause;
- /*
- * Upon entry, if the DACPlayer state is PLA_STOPPED, the pause method
- * simply returns [self prepare]. If the state is anything else besides
- * PLA_RUNNING, the pause method returns nil.
- *
- * Suspend output to the DACs. This merely pauses the sound stream, so it
- * stops making requests of [delegate playData:::]. Note that any buffers
- * of data that have been queued won't get played until the stream is run
- * again.
- *
- * The DACPlayer state is set to PLA_PAUSED.
- */
-
- - stop;
- /*
- * Stop playing immediately.
- * If the DACPlayer state is PLA_STOPPED, then this method simply returns.
- * Otherwise, it frees all the resources acquired in -setup, calls
- * [delegate didPlay:] and sets the state to PLA_STOPPED.
- */
-
- - finish;
- /*
- * Do a graceful shutdown of the DACPlayer.
- * If the DACPlayer state is PLA_RUNNING, then the DACPlayer will stop
- * enqueuing new regions (and will stop calling playData:::) and will set
- * the state to PLA_STOPPING. When the last available buffer has been played
- * by the sound driver, then the DACPlayer will call [self stop]. This all
- * means that any sound that has been queued up will get played before the
- * DACPlayer shuts down.
- */
-
- /*
- * METHODS THAT CONFIGURE DACPLAYER
- */
-
- - delegate;
- - setDelegate:anObject;
- /*
- * get/set the delegate for the dacPlayer. Strictly speaking, you don't need
- * a delegate in order to use dacPlayer, but the delegate is responsible
- * for doing something interesting with the data buffers (via the didPlay:::
- * method) before the buffers are sent to the DACs. So you will always
- * supply a delegate with a didPlay::: method unless you want to send streams
- * of zeros to the DACs.
- */
-
- - (int)regionSize;
- - (int)regionCount;
- - setRegionSize:(int)bytes andCount:(int)count;
- /*
- * Sets (or gets) the size of each sound region and the number of regions
- * that we keep queued up for the sound driver.
- */
-
- - (int)samplingRate;
- - setSamplingRate:(int)aRate;
- /*
- * get/set the sampling rate for the DACs. aRate must be either 44100 (the
- * default) or 22050. Sampling rate changes won't take effect until the
- * next call to -prepare
- */
-
- /*
- * METHODS THAT QUERY DACPLAYER
- */
-
- - (Pla_state_t)playerState;
- /*
- * gets the current state of the dac player, one of:
- * PLA_STOPPED stopped and idle, no resources allocated
- * PLA_PAUSED no sound playing, DAC resources allocated
- * PLA_RUNNING sound actively playing, DAC resources allocated
- * PLA_STOPPING sound actively playing, but will stop soon
- */
-
- - (int)bytesPlayed;
- - (int)samplesPlayed;
- - (int)framesPlayed;
- - (double)secondsPlayed;
- /*
- * These methods return how many bytes, samples, sample frames have actually
- * been sent to the DACs since the most recent call to -prepare. (NB: two
- * stereo samples make up one sample frame).
- */
-
- - (int)bytesQueued;
- - (int)samplesQueued;
- - (int)framesQueued;
- - (double)secondsQueued;
- /*
- * These methods return how many bytes, samples, sample frames have been
- * queued up for playing (and possible played).
- */
-
- @end
-
- /***
- *** DELEGATE METHODS
- ***
- *** Description of the Player's delegate methods
- ***/
-
- @interface PlayerDelegate:Object
-
- - willPlay :player;
- /*
- * Called by the player when going from a stopped to a paused state. Called
- * after all the resources have been allocated but before any regions get
- * queued. The various dacPlayer configuration parameters MAY be set from
- * a willPlay: method.
- */
-
- - didPlay :player;
- /*
- * Called when the player goes into a stopped state (either from a stop or
- * abort message) after all the sound resources have been freed.
- */
-
- - playData :player :(char *)data :(int)nbytes;
- /*
- * Called whenever the player wants more sound data. player is the dacPlayer
- * requesting the data, data is a buffer of length nbytes. The buffer is
- * guaranteed to be zero'd out. The dacPlayer requires that the samples you
- * write are stereo 16 bit samples. The samples will be played at whatever
- * sampling rate you established in a call to setSamplingRate:
- */
-
- - didChangeState :player from:(Pla_state_t)oldState to:(Pla_state_t)newState;
- /*
- * Called whenever the player changes state.
- */
-
- @end
-