Copyright ©1995 by NeXT Computer, Inc.  All Rights Reserved.

Sound



Inherits From: Object
Declared In: soundkit/Sound.h



Class Description

Sound objects represent and manage sounds.  A Sound object's sound can be recorded from a microphone, read from a soundfile or NXBundle resource, retrieved from the pasteboard or from the sound segment in the application's executable file, or created algorithmically.  The Sound class also provides an application-wide name table that lets you identify and locate sounds by name.

Playback and recording are performed by background threads, allowing your application to proceed in parallel.  The latency between sending a play: or record: message and the start of the playback or recording, while within the tolerance demanded by most applications, can be further decreased by first reserving the sound facilities that you wish to use.  This is done by calling the SNDReserve() C function.  You should only use a Sound object to play and record sounds in applications that have a running NXApp object present.

You can also edit a Sound object by adding and removing samples.  To minimize data movement (and thus save time), an edited Sound may become fragmented; in other words, its sound data might become discontiguous in memory.  While playback of a fragmented Sound object is transparent, it does incur some overhead.  If you perform a number of edits you may want to return the Sound to a contiguous state by sending it a compactSamples message before you play it.  However, a large Sound may take a long time to compact, so a judicious and well-timed use of compactSamples is advised.  Fragmented Sounds are automatically compacted before they're copied to a pasteboard (through the writeToPasteboard: method).  Also, when you write a Sound to a soundfile, the data in the file will be compact regardless of the state of the object.

A Sound object contains a SNDSoundStruct, the structure that describes and contains sound data and that's used as the soundfile format and the pasteboard sound type.  Most of the methods defined in the Sound class are implemented so that you needn't be aware of this structure.  However, if you wish to directly manipulate the sound data in a Sound object, you need to be familiar with the SNDSoundStruct architecture, as described in the SNDAlloc() function



Instance Variables

SNDSoundStruct *soundStruct;

int soundStructSize;

int priority;

id delegate;

int status;

char *name;


soundStruct The object's sound data structure.
soundStructSize The length of soundStruct in bytes.
priority The object's recording and playback priority.
delegate The target of notification messages.
status What the object is currently doing.
name The object's name.



Method Types

Creating and freeing a Sound object
+ addName:fromBundle:
+ addName:fromSection:
+ addName:fromSoundfile:
initFromSection:
initFromPasteboard:
initFromSoundfile:
free
Accessing the Sound name table + addName:sound:
+ findSoundFor:
+ removeSoundForName:
Accessing the Sound's name setName:
name
Reading and writing sound data readSoundfile:
readSoundFromStream:
writeSoundfile:
writeSoundToStream:
writeToPasteboard:
Modifying sound data convertToFormat:samplingRate:channelCount:
convertToFormat:
setDataSize:dataFormat:samplingRate:
channelCount:infoSize:
setSoundStruct:soundStructSize:
setName:
name
Querying the object soundStruct
soundStructSize
data
dataFormat
dataSize
channelCount
samplingRate
sampleCount
duration
info
infoSize
isEmpty
compatibleWith:
processingError
Recording and playing pause
pause:
isPlayable
play
play:
record
record:
resume
resume:
stop
stop:
samplesProcessed
status
soundBeingProcessed
soundStructBeingProcessed
Editing sound data isEditable
copySamples:at:count:
copySound:
deleteSamples
deleteSamplesAt:count:
insertSamples:at:
needsCompacting
compactSamples
Archiving the object finishUnarchiving
read:
write:
Accessing the delegate setDelegate:
delegate
tellDelegate:
Accessing the sound hardware + getVolume::
+ setVolume::
+ isMuted
+ setMute:



Class Methods

addName:fromBundle:
+ addName:(const char *)name fromBundle:(NXBundle *)aBundle

Creates a Sound object from the sound resource named name in the NXBundle aBundle, assigns the name name to the object, and places the name on the sound name table.  If name is already in use, or if the resource isn't found or can't be read, the Sound isn't created and nil is returned.  Otherwise, the new Sound is returned.



addName:fromSection:
+ addName:(const char *)name fromSection:(const char *)sectionName

Creates a Sound object from section sectionName in the sound segment of the application's executable file, assigns the name name to the object, and places the name on the sound name table.  If name is already in use, or if the section isn't found or its data can't be copied, the Sound isn't created and nil is returned.  Otherwise, the new Sound is returned.



addName:fromSoundfile:
+ addName:(const char *)name fromSoundfile:(const char *)filename

Creates a Sound object from the soundfile filename, assigns the name name to the object, and adds it to the named Sound table. If name is already in use, or if filename isn't found or can't be read, the Sound isn't created and nil is returned.  Otherwise, the new Sound is returned.



addName:sound:
+ addName:(const char *)name sound:aSound

Assigns the name name to the Sound aSound and adds it to the named Sound table.  Returns aSound, or nil if name is already in use.



findSoundFor:
+ findSoundFor:(const char *)aName

Finds and returns the named Sound object.  First the named Sound table is searched; if the sound isn't found, then the method looks for "aName.snd" in the sound segment of the application's executable file.  Finally, the file is searched for in the following directories (in order):

~/Library/Sounds
/LocalLibrary/Sounds
/NextLibrary/Sounds

where ~ represents the user's home directory.  If the Sound eludes the search, nil is returned.



getVolume::
+ getVolume:(float *)left :(float *)right

Returns, by reference, the stereo output levels as floating-point numbers between 0.0 and 1.0.



isMuted
+ (BOOL)isMuted

Returns YES if the sound output level is currently muted.



removeSoundForName:
+ removeSoundForName:(const char *)name

Removes the named Sound from the named Sound table.  If the Sound isn't found, returns nil; otherwise returns the Sound.



setMute:
+ setMute:(BOOL)aFlag

Mutes and unmutes the sound output level as aFlag is YES or NO, respectively.  If successful, returns self; otherwise returns nil.



setVolume::
+ setVolume:(float)left :(float)right

Sets the stereo output levels.  These affect the volume of the stereo signals sent to the built-in speaker and headphone jacks. left and right must be floating-point numbers between 0.0 (minimum) and 1.0 (maximum).  If successful, returns self; otherwise returns nil.



Instance Methods

channelCount
(int)channelCount

Returns the number of channels in the Sound.



compactSamples
(int)compactSamples

The Sound's sampled data is compacted into a contiguous block, undoing the fragmentation that can occur during editing.  If the Sound's data isn't fragmented (its format isn't SND_FORMAT_INDIRECT), then this method does nothing.  Compacting a large sound can take a long time; keep in mind that when you copy a Sound to a pasteboard, the object is automatically compacted before it's copied.  Also, the soundfile representation of a Sound contains contiguous data so there's no need to compact a Sound before writing it to a soundfile simply to ensure that the file representation will be compact.  An error code is returned.



compatibleWith:
(BOOL)compatibleWith:aSound

Returns YES if the format, sampling rate, and channel count of aSound's sound data is the same as that of the Sound receiving this message.  If one (or both) of the Sounds doesn't contain a sound (its soundStruct is nil) then the objects are declared compatible and YES is returned.



convertToFormat:
(int)convertToFormat:(int)newFormat

This is the same as convertToFormat:samplingRate:channelCount:, except that only the format is changed.  An error code is returned.



convertToFormat:samplingRate:channelCount:
(int)convertToFormat:(int)newFormat
samplingRate:(double)newRate
channelCount:(int)newChannelCount

Convert the Sound's data to the given format, sampling rate, and number of channels.  The following conversions are possible:

Arbitrary sampling rate conversion.
Compression and decompression.
Floating-point formats (including double-precision) to and from linear formats.
Mono to stereo.
CODEC mu-law to and from linear formats.

An error code is returned.



copySamples:at:count:
(int)copySamples:aSound
at:(int)startSample
count:(int)sampleCount

Replaces the Sound's sampled data with a copy of a portion of aSound's data.  The copied portion starts at aSound's startSample'th sample (zero-based) and extends over sampleCount samples.  The Sound receiving this message must be editable and the two Sounds must be compatible.  If the specified portion of aSound is fragmented, the Sound receiving this message will also be fragmented.  An error code is returned.



copySound:
(int)copySound:aSound

Replaces the Sound's data with a copy of aSound's data.  The Sound receiving this message needn't be editable, nor must the two Sounds be compatible.  An error code is returned.



data
(unsigned char *)data

Returns a pointer to the Sound's sampled data.  You can use the pointer to examine, create, and modify the sound data.  To intelligently manipulate the data, you need to be aware of its size, format, sampling rate, and the number of channels that it contains (a query method for each of these attributes is provided by the Sound class).  The size of the data, in particular, must be respected; it's set when the Sound is created or given a new sound (through readSoundfile:, for example) and can't be changed directly.  To resize the data, you should invoke one of the editing methods such as insertSamples:at: or deleteSamplesAt:count:.  To start with a new, unfragmented sound with a determinate length, invoke the setDataSize:dataFormat:samplingRate:channelCount:infoSize: method.  Keep in mind that the sound data in a fragmented sound is a pointer to a NULL-terminated list of pointers to SNDSoundStructs, one for each fragment.  To examine or manipulate the samples in a fragmented sound, you must understand the SNDSoundStruct structure.



dataFormat
(int)dataFormat

Returns the format of the Sound's data.  If the data is fragmented, the format of the samples is returned (in other words, SND_FORMAT_INDIRECT is never returned by this method).



dataSize
(int)dataSize

Return the size (in bytes) of the Sound's data.  If you modify the data (through the pointer returned by the data method) you must be careful not to exceed its length.  If the sound is fragmented, the value returned by this method is the size of the Sound's soundStruct and doesn't include the actual data itself.



delegate
delegate

Returns the Sound's delegate.



deleteSamples
(int)deleteSamples

Deletes all the samples in the Sound's data.  The Sound must be editable.  An error code is returned.



deleteSamplesAt:count:
(int)deleteSamplesAt:(int)startSample count:(int)sampleCount

Deletes a range of samples from the Sound:  sampleCount samples are deleted starting with the startSample'th sample (zero-based).  The Sound must be editable and may become fragmented.  An error code is returned.



duration
(double)duration

Returns the Sound's length in seconds.



finishUnarchiving
finishUnarchiving

You never invoke this method.  It's invoked automatically by the read: method to tie up loose ends after unarchiving the Sound.



free
free

Frees the Sound and deallocates its sound data.  The Sound is removed from the named Sound table and its name made eligible for reuse.



info
(char *)info

Returns a pointer to the Sound's info string.



infoSize
(int)infoSize

Returns the size (in bytes) of the Sound's info string.



initFromPasteboard:
initFromPasteboard:(Pasteboard *)thePboard

Initializes the Sound instance, which must be newly allocated, by copying the sound data from the Pasteboard object thePboard. (A Pasteboard can have only one sound entry at a time.)  Returns self (an unnamed Sound) if thePboard currently contains a sound entry; otherwise, frees the newly allocated Sound and returns nil.

See also:  + alloc (Object), + allocFromZone: (Object)



initFromSection:
initFromSection:(const char *)sectionName

Initializes the Sound instance, which must be newly allocated, by copying the sound data from section sectionName of the sound segment of the application's executable file.  If the section isn't found, the object looks for a soundfile named sectionName in the same directory as the application's executable.   Returns self (an unnamed Sound) if the sound data was successfully copied; otherwise, frees the newly allocated Sound and returns nil.

See also:  + alloc (Object), + allocFromZone: (Object)



initFromSoundfile:
initFromSoundfile:(const char *)filename

Initializes the Sound instance, which must be newly allocated, from the soundfile filename.   Returns self (an unnamed Sound) if the file was successfully read; otherwise, frees the newly allocated Sound and returns nil.

See also:  + alloc (Object), + allocFromZone: (Object)



insertSamples:at:
(int)insertSamples:aSound at:(int)startSample

Pastes the sound data in aSound into the Sound receiving this message, starting at the receiving Sound's startSample'th sample (zero-based).  The receiving Sound doesn't lose any of its original sound data--the samples greater than or equal to startSample are moved to accommodate the inserted sound data.  The receiving Sound must be editable and the two Sounds must be compatible (as determined by isCompatible:).  If the method is successful, the receiving Sound is fragmented.  An error code is returned.



isEditable
(BOOL)isEditable

Returns YES if the Sound's format indicates that it can be edited, otherwise returns NO.



isEmpty
(BOOL)isEmpty

Returns YES if the Sound doesn't contain any sound data, otherwise returns NO.  This always returns NO if the Sound isn't editable (as determined by sending it the isEditable message).



isPlayable
(BOOL)isPlayable

Returns YES if the Sound can be played, otherwise returns NO.  Some unplayable Sounds just need to be converted to another format, sampling rate, or number of channels; others are inherently unplayable, such as those whose format is SND_FORMAT_DISPLAY.  To play a Sound that's just been recorded from the DSP, you must change its format from SND_FORMAT_DSP_DATA_16 to SND_FORMAT_LINEAR_16.



name
(const char *)name

Returns the Sound's name.



needsCompacting
(BOOL)needsCompacting

Returns YES if the Sound's data is fragmented.  Otherwise returns NO.



pause
(int)pause

Pauses the Sound during recording or playback.  An error code is returned.



pause:
pause:sender

Action method that pauses the Sound.  Other than the argument and the return type, this is the same as the pause method. Returns self.



play
(int)play

Initiates playback of the Sound.  The method returns immediately while the playback continues asynchronously in the background.  The playback ends when the Sound receives the stop message, or when its data is exhausted.

When playback starts, willPlay: is sent to the Sound's delegate; when it stops, didPlay: is sent.  An error code is returned.

Warning: For this method to work properly, the main event loop must not be blocked.
play:
play:sender

Action method that plays the Sound.  Other than the argument and the return type, this is the same as the play method.  Returns self.



processingError
(int)processingError

Returns a constant that represents the last error that was generated.  The sound error codes are listed in "Types and Constants."



read:
read:(NXTypedStream *)stream

Reads archived sound data from stream into the Sound.  Returns self.



readSoundfile:
(int)readSoundfile:(const char *)filename

Replaces the Sound's contents with those of the soundfile filename.  The Sound loses its current name, if any.  An error code is returned.



readSoundFromStream:
readSoundFromStream:(NXStream *)stream

Replaces the Sound's contents with those of the sound in the NXStream stream.  The Sound is given the name of the sound in the NXStream.  If the sound in the NXStream is named, the Sound gets the new name.  An error code is returned.



record
(int)record

Initiate recording into the Sound.  To record from the CODEC microphone, the Sound's format, sampling rate, and channel count must be SND_FORMAT_MULAW_8, SND_RATE_CODEC, and 1, respectively.  If this information isn't set (if the Sound is a newly created object, for example), it defaults to accommodate a CODEC recording.  If the Sound's format is SND_FORMAT_DSP_DATA_16, the recording is from the DSP.

The method returns immediately while the recording continues asynchronously in the background.  The recording stops when the Sound receives the stop message or when the recording has gone on for the duration of the original sound data.  The default CODEC recording lasts precisely ten minutes if not stopped.  To record for a longer time, first increase the size of the sound data with setSoundStruct:soundStructSize: or setDataSize:dataFormat:samplingRate:channelCount:infoSize:.

When the recording begins, willRecord: is sent to the Sound's delegate; when the recording stops, didRecord: is sent.

An error code is returned.

Warning: For this method to work properly, the main event loop must not be blocked.
record:
record:sender

Action method that initiates a recording.  Other than the argument and return type, this is the same as the record method. Returns self.



resume
(int)resume

Resumes the paused Sound's activity.  An error code is returned.



resume:
resume:sender

Action method that resumes the paused Sound.  Returns self.



sampleCount
(int)sampleCount

Returns the number of sample frames, or channel count-independent samples, in the Sound.



samplesProcessed
(int)samplesProcessed

If the Sound is currently playing or recording, this returns the number of sample frames that have been played or recorded so far. Otherwise, the number of sample frames in the Sound is returned.  If the sample frame count can't be determined, 1 is returned.



samplingRate
(double)samplingRate

Returns the Sound's sampling rate.



setDataSize:dataFormat:samplingRate:channelCount:infoSize:
(int)setDataSize:(int)newDataSize
dataFormat:(int)newDataFormat
samplingRate:(double)newSamplingRate
channelCount:(int)newChannelCount
infoSize:(int)newInfoSize

Allocates new, unfragmented sound data for the Sound, as described by the arguments.  The Sound's previous data is freed. This method is useful for setting a determinate data length prior to a recording or for creating a scratch pad for algorithmic sound creation.  An error code is returned.



setDelegate:
setDelegate:anObject

Sets the Sound's delegate to anObject.  The delegate may implement the following methods:

willPlay:
didPlay:
willRecord:
didRecord:
hadError:

Returns self.



setName:
setName:(const char *)aName

Sets the Sound's name to aName.  If aName is already being used, then the Sound's name isn't set and nil is returned; otherwise returns self.



setSoundStruct:soundStructSize:
setSoundStruct:(SNDSoundStruct *)aStruct  soundStructSize:(int)size

Sets the Sound's sound structure to aStruct.  The size in bytes of the new structure, including its sound data storage, must be specified by size.  This method can be used to set up a large buffer before recording into an existing Sound, by passing the existing soundStruct in the first argument while making size larger than the current size.  (The default buffer holds ten minutes of CODEC sound.)  The method is also useful in cases where aStruct already has sound data but isn't encapsulated in a Sound object yet.  The Sound's status must be NX_SoundInitialized or NX_SoundStopped for this method to do anything.  Returns self.



soundBeingProcessed
soundBeingProcessed

Returns the Sound object that's being performed.  The default implementation always returns self.



soundStruct
(SNDSoundStruct *)soundStruct

Returns a pointer to the Sound's SNDSoundStruct structure that holds the object's sound data.



soundStructBeingProcessed
(SNDSoundStruct *)soundStructBeingProcessed

Returns a pointer to the SNDSoundStruct structure that's being performed.  This may not be the same structure as returned by the soundStruct method--Sound object's contain a private sound structure that may be used for recording playing.  If the Sound isn't currently playing or recording, then this will return the public structure.



soundStructSize
(int)soundStructSize

Returns the size, in bytes, of the Sound's sound structure (pointed to by soundStruct).  Use of this value requires a knowledge of the SNDSoundStruct architecture.



status
(int)status

Return the Sound's current status, one of the following integer constants:

NX_SoundStopped
NX_SoundRecording
NX_SoundPlaying
NX_SoundInitialized
NX_SoundRecordingPaused
NX_SoundPlayingPaused
NX_SoundRecordingPending
NX_SoundPlayingPending
NX_SoundFreed


stop
(int)stop

Terminates the Sound's playback or recording.  If the Sound was recording, the didRecord: message is sent to the delegate; if playing, didPlay: is sent.  An error code is returned.



stop:
stop:sender

Action method that stops the Sound's playback or recording.  Other than the argument and the return type, this is the same as the stop method.  Returns self.



tellDelegate:
tellDelegate:(SEL)theMessage

Sends theMessage to the Sound's delegate (only sent if the delegate implements theMessage).  You never invoke this method directly; it's invoked automatically as the result of activities such as recording and playing.  However, you can use it in designing a subclass of Sound.  Returns self.



write:
write:(NXTypedStream *)stream

Archives the Sound by writing its data to stream, which must be open for writing.  Returns self.



writeSoundfile:
(int)writeSoundfile:(const char *)filename

Writes the Sound's contents (its SNDSoundStruct and sound data) to the soundfile filename.  An error code is returned.



writeSoundToStream:
writeSoundToStream:(NXStream *)stream

Writes the Sound's name (if any), priority, SNDSoundStruct, and sound data (if any) to the NXStream stream.  Returns self.



writeToPasteboard:
(int)writeToPasteboard:(Pasteboard *)thePboard

Puts a copy of the Sound's contents (its SNDSoundStruct and sound data) on the pasteboard maintained by the Pasteboard object thePboard.  If the Sound is fragmented, it's compacted before the copy is created.  An error code is returned.



Methods Implemented by the Delegate

didPlay:
didPlay:sender

Sent to the delegate when the Sound stops playing.



didRecord:
didRecord:sender

Sent to the delegate when the Sound stops recording.



hadError:
hadError:sender

Sent to the delegate if an error occurs during recording or playback.



willPlay:
willPlay:sender

Sent to the delegate when the Sound begins to play.



willRecord:
willRecord:sender

Sent to the delegate when the Sound begins to record.