Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
SoundSnippets.c
////////// |
// |
// File: SoundSnippets.c |
// |
// Contains: Code snippets showing how to perform a few typical sound-related operations. |
// |
// Written by: Tim Monroe |
// Some routines based on existing code by Jim Reekes and Kip Olson. |
// |
// Copyright: © 1998-2003 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// <8> 08/06/03 era fixed SndSnip_ConvertWAVEFormats as it did not previously set |
// the value for numChannels in the Sound Description |
// <7> 02/03/99 rtm minor tweaks based on AudioConvert.c sample code |
// <6> 02/03/99 rtm reworked prompt and filename handling to remove "\p" sequences |
// <5> 06/25/98 rtm added SndSnip_HasSoundManager3_1; modified the function |
// SndSnip_PromptUserForDiskFileAndSaveCompressed to compress a |
// file in 40K chunks |
// <4> 06/22/98 rtm added SndSnip_GetAudioSettings and SndSnip_ConvertWAVEFormats; |
// added SndSnip_PromptUserForAudioFileAndCompress and |
// SndSnip_PromptUserForDiskFileAndSaveCompressed, which illustrate |
// both the standard sound compression dialog component and the |
// sound converter routines |
// <3> 05/29/98 rtm added SndSnip_GetHardwareSettings, SndSnip_CheckVersionNumber, |
// SndSnip_GetVolume, and SndSnip_SetVolume |
// <2> 04/24/98 rtm got bufferCmd double-buffering working |
// <1> 04/17/98 rtm first file |
// |
// This sample code illustrates how to perform some common sound-related operations, |
// such as saving a movie's sound track into a separate file or playing a continuous sound using |
// the bufferCmd sound command. This is not really a library of utilities, but more a grab-bag |
// of "how-to" recipes. Here's a partial list of what's illustrated here: |
// |
// *Save a sound-only QuickTime movie as a WAVE file. |
// *Save a sound-only QuickTime movie as a file whose type the user selects. |
// *Save a sound track in a QuickTime movie as a file whose type the user selects. |
// *Create a handle that contains the first sound track in a QuickTime movie. |
// *Open and play a WAVE file using the QuickTime API. |
// *Set the volume level of a sound track in a QuickTime movie. |
// *Use the bufferCmd sound command to continuously play a large buffer of audio data. |
// *Get the current hardware settings. |
// *Get the settings for a sound track in a QuickTime movie. |
// *Get and set the current left and right volumes of a sound channel. |
// *Let the user select an audio file and select its compression settings; then compress it. |
// |
// NOTES: |
// |
// *** (1) *** |
// The function SndSnip_PromptUserForAudioFileAndCompress uses the standard sound compression dialog routines |
// to elicit compression/conversion settings from the user. These routines were based on the standard image |
// compression dialog routines. You can use modal-dialog filter functions and hook functions with the sound |
// routines in the same way that you use them with the image routines; this, however, is not illustrated here. |
// See the code snippet QTStdCompr for sample code that does illustrate these features of the standard |
// compression dialog routines. |
// |
// *** (2) *** |
// The function SndSnip_PromptUserForDiskFileAndSaveCompressed uses the sound converter routines (such as |
// SoundConverterConvertBuffer) to compress audio data. These routines were introduced in Sound Manager 3.2. |
// If you want to run in earlier Sound Manager versions, you'll need to replace this conversion method with |
// some other method. The method illustrated here is based on code in the file SoundConvert.c by Andrew Wulf, |
// Apple Developer Technical Support. |
// |
////////// |
#include "SoundSnippets.h" |
#include "QTUtilities.h" |
// global variables |
SoundHeader gSndHeader1; |
SoundHeader gSndHeader2; |
Boolean gCurrChunkDirty = false; |
short gCurrChunkIndex; |
SoundHeaderPtr gCurrChunkHeader; |
SndChannelPtr gCurrChannel; |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Sound movie conversion routines. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// SndSnip_SaveSoundMovieAsWAVEFile |
// Save a sound-only QuickTime movie as a WAVE file. |
// |
////////// |
void SndSnip_SaveSoundMovieAsWAVEFile (Movie theMovie) |
{ |
StandardFileReply myReply; |
StringPtr myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt); |
StringPtr myFileName = QTUtils_ConvertCToPascalString(kSaveSoundWaveName); |
// have the user select the name and location of the new WAVE file |
StandardPutFile(myPrompt, myFileName, &myReply); |
if (!myReply.sfGood) |
return; |
// use the default progress procedure, if any |
SetMovieProgressProc(theMovie, (MovieProgressUPP)-1L, 0); |
// export the movie into a file |
ConvertMovieToFile( theMovie, // the movie to convert |
NULL, // all tracks in the movie |
&myReply.sfFile, // the output file |
kQTFileTypeWave, // the output file type |
FOUR_CHAR_CODE('TVOD'), // the output file creator |
smSystemScript, // the script |
NULL, // no resource ID to be returned |
0L, // no flags |
NULL); // no specific component |
free(myPrompt); |
free(myFileName); |
} |
////////// |
// |
// SndSnip_SaveSoundMovieAsAnyTypeFile |
// Save a sound-only QuickTime movie as a file whose type the user selects. |
// |
////////// |
void SndSnip_SaveSoundMovieAsAnyTypeFile (Movie theMovie) |
{ |
StandardFileReply myReply; |
StringPtr myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt); |
StringPtr myFileName = QTUtils_ConvertCToPascalString(kSaveSoundFileName); |
// have the user select the name and location of the new file |
StandardPutFile(myPrompt, myFileName, &myReply); |
if (!myReply.sfGood) |
return; |
// use the default progress procedure, if any |
SetMovieProgressProc(theMovie, (MovieProgressUPP)-1L, 0); |
// export the movie into a file; since the flag showUserSettingsDialog is set, |
// the user gets to select the audio format of the saved audio data |
ConvertMovieToFile( theMovie, // the movie to convert |
NULL, // all tracks in the movie |
&myReply.sfFile, // the output file |
0L, // the output file type |
FOUR_CHAR_CODE('TVOD'), // the output file creator |
smSystemScript, // the script |
NULL, // no resource ID to be returned |
createMovieFileDeleteCurFile | movieToFileOnlyExport | showUserSettingsDialog, |
NULL); // no specific component |
free(myPrompt); |
free(myFileName); |
} |
////////// |
// |
// SndSnip_SaveSoundTrackAsAnyTypeFile |
// Save the first sound track in a QuickTime movie as a file whose type the user selects. |
// |
////////// |
void SndSnip_SaveSoundTrackAsAnyTypeFile (Movie theMovie) |
{ |
StandardFileReply myReply; |
Track myTrack = NULL; |
StringPtr myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt); |
StringPtr myFileName = QTUtils_ConvertCToPascalString(kSaveSoundFileName); |
// find the first sound track in the specified movie |
myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly); |
if (myTrack == NULL) |
return; |
// have the user select the name and location of the new file |
StandardPutFile(myPrompt, myFileName, &myReply); |
if (!myReply.sfGood) |
return; |
// use the default progress procedure, if any |
SetMovieProgressProc(theMovie, (MovieProgressUPP)-1L, 0); |
// export the specified track into a file; since the flag showUserSettingsDialog is set, |
// the user gets to select the audio format of the saved audio data |
ConvertMovieToFile( theMovie, // the movie to convert |
myTrack, // save only the specified track |
&myReply.sfFile, // the output file |
0L, // the output file type |
FOUR_CHAR_CODE('TVOD'), // the output file creator |
smSystemScript, // the script |
NULL, // no resource ID to be returned |
createMovieFileDeleteCurFile | movieToFileOnlyExport | showUserSettingsDialog, |
NULL); // no specific component |
free(myPrompt); |
free(myFileName); |
} |
////////// |
// |
// SndSnip_ExtractSoundTrackIntoHandle |
// Create a handle that contains the first sound track in a QuickTime movie. |
// |
// NOTE: To create a handle that contains ALL the sound tracks in the movie |
// mixed together, pass NULL instead of myTrack to PutMovieIntoTypedHandle. |
// |
////////// |
void SndSnip_ExtractSoundTrackIntoHandle (Movie theMovie) |
{ |
Track myTrack = NULL; |
Handle myHandle = NULL; |
OSErr myErr = noErr; |
// find the first sound track in the specified movie |
myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly); |
if (myTrack == NULL) |
goto bail; |
// create an empty handle; this will be resized by PutMovieIntoTypedHandle |
myHandle = NewHandle(0); |
if (myHandle == NULL) |
goto bail; |
// use the default progress procedure, if any |
SetMovieProgressProc(theMovie, (MovieProgressUPP)-1L, 0); |
// extract the sound track and put it into the handle |
myErr = PutMovieIntoTypedHandle( |
theMovie, // the movie to convert |
myTrack, // save only the specified track |
soundListRsrc, // make it a sound resource handle |
myHandle, // the handle into which to put the audio data |
0, // start time |
GetTrackDuration(myTrack), // ending time |
0L, // flags |
NULL); // no specific component |
if (myErr != noErr) |
goto bail; |
// to prove we really did create the handle, play it |
SndPlay(NULL, (SndListHandle)myHandle, false); |
bail: |
if (myHandle != NULL) |
DisposeHandle(myHandle); |
} |
////////// |
// |
// SndSnip_PlayWAVEFileWithQuickTime |
// Open and play a WAVE file using the QuickTime API. |
// |
////////// |
void SndSnip_PlayWAVEFileWithQuickTime (void) |
{ |
StandardFileReply myReply; |
SFTypeList myTypeList = {kQTFileTypeWave, 0, 0, 0}; |
short myRefNum; |
// elicit a file from the user |
StandardGetFile(NULL, 1, myTypeList, &myReply); |
if (myReply.sfGood) { |
Movie myMovie; |
OpenMovieFile(&myReply.sfFile, &myRefNum, fsRdPerm); |
NewMovieFromFile(&myMovie, myRefNum, NULL, (StringPtr)NULL, newMovieActive, NULL); |
if (myRefNum != 0) |
CloseMovieFile(myRefNum); |
// play the movie once thru |
StartMovie(myMovie); |
while (!IsMovieDone(myMovie)) |
MoviesTask(myMovie, 0); |
DisposeMovie(myMovie); |
} |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Volume routines. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// SndSnip_SetVolumeOfSoundTrack |
// Set the volume level of the first sound track in a QuickTime movie. |
// |
// The range for the theVolume parameter is 0 (no volume) to 256 (full volume). |
// You can also pass negative values (e.g., -128). No sound is produced for negative |
// values, but they are useful for maintaining the absolute value of a volume. |
// |
////////// |
void SndSnip_SetVolumeOfSoundTrack (Movie theMovie, short theVolume) |
{ |
Track myTrack = NULL; |
// find the first sound track in the specified movie |
myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly); |
if (myTrack == NULL) |
return; |
// set the volume of the track |
SetTrackVolume(myTrack, theVolume); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Double-buffering routines. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// SndSnip_PlaySoundResourceUsingBufferCmds |
// Use the bufferCmd sound command to continuously play a large buffer of audio data. |
// |
// NOTE: This routine exists solely to illustrate one way to use bufferCmd sound commands |
// to play a buffer of audio data continuously; here, we read the audio data from a resource |
// and then hand chunks of it to the Sound Manager using bufferCmds. OF COURSE we could have |
// played the entire buffer in one fell swoop by calling SndPlay. The idea is that a real |
// application would read the audio data from disk in bite-sized chunks and use the techniques |
// illustrated here to play that data. |
// |
////////// |
OSErr SndSnip_PlaySoundResourceUsingBufferCmds (void) |
{ |
Handle myHandle = NULL; |
SoundComponentData myResourceInfo; |
unsigned long myNumFrames; |
unsigned long myOffset; |
Ptr myPtr = NULL; |
Size mySize; |
long myFreq; |
SndChannelPtr mySndChannel = NULL; |
SndCallBackUPP mySndCallBackProc; |
unsigned long myBufferSize; // the size of the buffer we pass to each bufferCmd command |
OSErr myErr = memFullErr; |
// get the resource data; we assume this resource contains sampled sound data |
myHandle = GetResource(soundListRsrc, kSampleResourceID); |
if (myHandle == NULL) |
goto bail; |
// get info from the sound resource header |
myErr = ParseSndHeader((SndListHandle)myHandle, &myResourceInfo, &myNumFrames, &myOffset); |
if (myErr != noErr) |
goto bail; |
// get the base frequency from the sampled sound header |
myFreq = SndSnip_GetSndBaseFrequency(myHandle); |
// lock the handle, so we can use a pointer to cruise thru the sound data |
HLockHi(myHandle); |
myPtr = *myHandle + myOffset; |
mySize = GetHandleSize(myHandle) - myOffset; |
myBufferSize = mySize / kNumberOfBufferChunks; |
// allocate a routine descriptor for our sound callback routine |
mySndCallBackProc = NewSndCallBackProc(SndSnip_CallbackProc); |
// create storage for a new sound channel |
mySndChannel = (SndChannelPtr)NewPtrClear(sizeof(SndChannel)); |
if (mySndChannel == NULL) |
goto bail; |
// set the number of commands in the sound channel queue; |
// pass the application's A5 value in the userInfo field |
mySndChannel->qLength = kNumberOfCmdsInQueue; |
#if TARGET_OS_MAC |
mySndChannel->userInfo = SetCurrentA5(); |
#endif |
// create the sound channel |
myErr = SndNewChannel(&mySndChannel, sampledSynth, initMono, mySndCallBackProc); |
if (myErr != noErr) { |
DisposePtr((Ptr)mySndChannel); |
goto bail; |
} |
// the basic idea is this: fill two buffers with data and pass them to the Sound Manager |
// using the bufferCmd sound command; each bufferCmd is immediately followed in the queue |
// by a callBackCmd sound command; a callBackCmd triggers the _CheckBuffers routine, which |
// installs another buffer of data and an associated callBackCmd |
// configure the first sound header |
gSndHeader1.samplePtr = myPtr; |
gSndHeader1.length = myBufferSize; |
gSndHeader1.sampleRate = myResourceInfo.sampleRate; |
gSndHeader1.loopStart = 0; |
gSndHeader1.loopEnd = 0; |
gSndHeader1.encode = stdSH; |
gSndHeader1.baseFrequency = myFreq; |
// install the first bufferCmd, to play the first chunk of the buffer |
myErr = SndSnip_InstallBufferCmd(mySndChannel, &gSndHeader1); |
// install the first callBackCmd |
myErr = SndSnip_InstallCallbackCmd(mySndChannel, 0, (long)&gSndHeader1); |
// configure the second sound header |
gSndHeader2.samplePtr = myPtr + myBufferSize; |
gSndHeader2.length = myBufferSize; |
gSndHeader2.sampleRate = myResourceInfo.sampleRate; |
gSndHeader2.loopStart = 0; |
gSndHeader2.loopEnd = 0; |
gSndHeader2.encode = stdSH; |
gSndHeader2.baseFrequency = myFreq; |
// install the second bufferCmd, to play the second chunk of the buffer |
myErr = SndSnip_InstallBufferCmd(mySndChannel, &gSndHeader2); |
// install the second callBackCmd |
myErr = SndSnip_InstallCallbackCmd(mySndChannel, 1, (long)&gSndHeader2); |
bail: |
return(myErr); |
} |
////////// |
// |
// SndSnip_CallbackProc |
// Handle callback messages. Keep it quick, okay? |
// |
////////// |
PASCAL_RTN void SndSnip_CallbackProc (SndChannelPtr theChannel, SndCommand *theCommand) |
{ |
#if TARGET_OS_MAC |
long myA5; |
myA5 = SetA5(theChannel->userInfo); |
#endif |
// in this sample code, param1 of theCommand is the 0-based index of the chunk of the buffer |
// that just finished playing, and param2 is a pointer to the sound header for that chunk |
gCurrChunkDirty = true; |
gCurrChunkIndex = theCommand->param1; |
gCurrChunkHeader = (SoundHeaderPtr)(theCommand->param2); |
gCurrChannel = theChannel; |
#if TARGET_OS_MAC |
myA5 = SetA5(myA5); |
#endif |
} |
////////// |
// |
// SndSnip_CheckBuffers |
// Handle callback messages. |
// |
// This function needs to be called periodically. On MacOS, you could call this function |
// every time thru your event loop. On Windows, you'll need to use a different strategy; |
// for instance, you could install a timer that is called often enough that the bufferCmd |
// gets installed before the currently-playing buffer is exhausted. |
// |
////////// |
void SndSnip_CheckBuffers (void) |
{ |
if (gCurrChunkDirty) { |
// install next buffer of data, if there is one |
if (gCurrChunkIndex < (kNumberOfBufferChunks - 2)) { |
gCurrChunkHeader->samplePtr = gCurrChunkHeader->samplePtr + (2 * (gCurrChunkHeader->length)); |
// install a bufferCmd command, to play the next buffer of data |
SndSnip_InstallBufferCmd(gCurrChannel, gCurrChunkHeader); |
// install a callBackCmd command, to be called when this buffer is finished playing |
SndSnip_InstallCallbackCmd(gCurrChannel, gCurrChunkIndex + 2, (long)gCurrChunkHeader); |
} |
} |
gCurrChunkDirty = false; |
} |
////////// |
// |
// SndSnip_InstallBufferCmd |
// Install a bufferCmd sound command to play the specified buffer of data. |
// |
////////// |
static OSErr SndSnip_InstallBufferCmd (SndChannelPtr theChannel, SoundHeaderPtr theHeaderPtr) |
{ |
SndCommand mySndCommand; |
mySndCommand.cmd = bufferCmd; |
mySndCommand.param1 = 0; |
mySndCommand.param2 = (long)theHeaderPtr; |
return(SndDoCommand(theChannel, &mySndCommand, kWait)); |
} |
////////// |
// |
// SndSnip_InstallCallbackCmd |
// Install a callBackCmd sound command. |
// |
////////// |
static OSErr SndSnip_InstallCallbackCmd (SndChannelPtr theChannel, short theParam1, long theParam2) |
{ |
SndCommand mySndCommand; |
mySndCommand.cmd = callBackCmd; |
mySndCommand.param1 = theParam1; |
mySndCommand.param2 = theParam2; |
return(SndDoCommand(theChannel, &mySndCommand, kWait)); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Sample sound information routines. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// SndSnip_GetSoundHeader |
// Returns a pointer to the sound header in a sampled sound resource. |
// |
////////// |
SoundHeaderPtr SndSnip_GetSoundHeader (Handle theSndHandle) |
{ |
SoundHeaderPtr mySndHeader = NULL; |
long myOffset = 0; |
OSErr myErr = noErr; |
myErr = GetSoundHeaderOffset((SndListHandle)theSndHandle, &myOffset); |
if (myErr == noErr) |
mySndHeader = (SoundHeaderPtr)((long)*theSndHandle + myOffset); |
return(mySndHeader); |
} |
////////// |
// |
// SndSnip_GetSndBaseFrequency |
// Returns the base frequency of a sampled sound. |
// |
////////// |
long SndSnip_GetSndBaseFrequency (Handle theSndHandle) |
{ |
SoundHeaderPtr mySndHeader; |
long myBaseFreq = kMiddleC; // a reasonable default |
mySndHeader = SndSnip_GetSoundHeader(theSndHandle); |
if (mySndHeader != NULL) |
myBaseFreq = mySndHeader->baseFrequency; |
return(myBaseFreq); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Settings routines. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// SndSnip_GetHardwareSettings |
// Get the current hardware settings. |
// |
// Based on the function GetHardwareSettings by Kip Olson (develop, issue 24). |
// |
////////// |
OSErr SndSnip_GetHardwareSettings (SndChannelPtr theChannel, SoundComponentData *theInfo) |
{ |
NumVersion myVersion; |
OSErr myErr = noErr; |
// SndGetInfo is available only in Sound Manager 3.1 or later, |
// so check for an appropriate Sound Manager version |
myVersion = SndSoundManagerVersion(); |
if (!SndSnip_CheckVersionNumber(&myVersion, 3, 1, 0)) |
goto bail; |
myErr = SndGetInfo(theChannel, siNumberChannels, &theInfo->numChannels); |
if (myErr != noErr) |
goto bail; |
myErr = SndGetInfo(theChannel, siSampleRate, &theInfo->sampleRate); |
if (myErr != noErr) |
goto bail; |
myErr = SndGetInfo(theChannel, siSampleSize, &theInfo->sampleSize); |
if (myErr != noErr) |
goto bail; |
if (theInfo->sampleSize == 8) |
theInfo->format = kOffsetBinary; |
else |
theInfo->format = kTwosComplement; |
bail: |
return(myErr); |
} |
////////// |
// |
// SndSnip_GetAudioSettings |
// Get the settings for the first audio track in the specified movie. |
// |
////////// |
OSErr SndSnip_GetAudioSettings (Movie theMovie, SoundComponentData *theInfo) |
{ |
Track myTrack = NULL; |
Media myMedia = NULL; |
SoundDescriptionHandle myDesc = NULL; |
OSErr myErr = paramErr; |
// get the first sound track in the specified movie |
myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly); |
if (myTrack == NULL) |
goto bail; |
myMedia = GetTrackMedia(myTrack); |
if (myMedia == NULL) |
goto bail; |
myDesc = (SoundDescriptionHandle)NewHandle(0); |
if (myDesc == NULL) |
goto bail; |
GetMediaSampleDescription(myMedia, 1, (SampleDescriptionHandle)myDesc); |
if (GetHandleSize((Handle)myDesc) == 0) |
goto bail; |
theInfo->numChannels = (**myDesc).numChannels; |
theInfo->sampleRate = (**myDesc).sampleRate; |
theInfo->sampleSize = (**myDesc).sampleSize; |
theInfo->format = (**myDesc).dataFormat; |
myErr = noErr; |
bail: |
if (myDesc != NULL) |
DisposeHandle((Handle)myDesc); |
return(myErr); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Sound information routines. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// SndSnip_HasSoundManager3_1 |
// Returns true if the installed version of the Sound Manager is at least 3.1. |
// |
////////// |
Boolean SndSnip_HasSoundManager3_1 (void) |
{ |
NumVersionVariant myVersion; |
myVersion.parts = SndSoundManagerVersion(); |
return(myVersion.whole >= 0x03100000); |
} |
////////// |
// |
// SndSnip_CheckVersionNumber |
// Returns true if the given version number is compatible with |
// (that is, not older than) version theMajor.theMinor.theBug. |
// |
////////// |
Boolean SndSnip_CheckVersionNumber ( |
const NumVersion *theVersion, |
UInt8 theMajor, |
UInt8 theMinor, |
UInt8 theBug) |
{ |
if (theVersion->majorRev != theMajor) |
return(theVersion->majorRev > theMajor); |
else |
return(theVersion->minorAndBugRev >= theMinor << 4 | theBug); |
} |
////////// |
// |
// SndSnip_GetVolume |
// Get the current left and right volumes of a sound channel. |
// |
// Based on the function GetVolume by Kip Olson (develop, issue 24). |
// |
////////// |
OSErr SndSnip_GetVolume (SndChannelPtr theChannel, unsigned short *theLeftVol, unsigned short *theRightVol) |
{ |
SndCommand mySndCommand; |
unsigned long myVolume; |
OSErr myErr = noErr; |
// getVolumeCmd is available only in Sound Manager version 3.0 and later; |
// you might want to check for a correct version here |
mySndCommand.cmd = getVolumeCmd; |
mySndCommand.param1 = 0; |
mySndCommand.param2 = (long)&myVolume; |
myErr = SndDoImmediate(theChannel, &mySndCommand); |
if (myErr == noErr) { |
*theLeftVol = myVolume & 0x0000ffff; |
*theRightVol = myVolume >> 16; |
} |
return(myErr); |
} |
////////// |
// |
// SndSnip_SetVolume |
// Set the left and right volumes of a sound channel. |
// |
// Based on the function SetVolume by Kip Olson (develop, issue 24). |
// |
////////// |
OSErr SndSnip_SetVolume (SndChannelPtr theChannel, unsigned short theLeftVol, unsigned short theRightVol) |
{ |
SndCommand mySndCommand; |
OSErr myErr = noErr; |
// volumeCmd is available only in Sound Manager version 3.0 and later; |
// you might want to check for a correct version here |
mySndCommand.cmd = volumeCmd; |
mySndCommand.param1 = 0; |
mySndCommand.param2 = (theRightVol << 16) | theLeftVol; |
myErr = SndDoImmediate(theChannel, &mySndCommand); |
return(myErr); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Movie export component routines. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// SndSnip_ConvertWAVEFormats |
// Convert an 8-bit, 22 kHz WAVE file to a 16-bit, 22 kHz IMAPCM WAVE file. |
// |
////////// |
OSErr SndSnip_ConvertWAVEFormats (Movie theMovie, FSSpec *theFile) |
{ |
ComponentInstance myComponent = NULL; |
SoundDescriptionHandle myDesc = NULL; |
ComponentResult myErr = badComponentType; |
// open a movie export component |
myComponent = OpenDefaultComponent(MovieExportType, kQTFileTypeWave); |
if (myComponent == NULL) |
goto bail; |
// create and fill in a sound description |
myDesc = (SoundDescriptionHandle)NewHandleClear(sizeof(SoundDescription)); |
if (myDesc == NULL) { |
myErr = MemError(); |
goto bail; |
} |
(**myDesc).descSize = sizeof(SoundDescription); |
(**myDesc).numChannels = 2; |
(**myDesc).sampleSize = 16; |
(**myDesc).sampleRate = rate22050hz; |
(**myDesc).dataFormat = k16BitLittleEndianFormat; |
// tell the export component to use the specified audio characteristics |
myErr = MovieExportSetSampleDescription(myComponent, (SampleDescriptionHandle)myDesc, SoundMediaType); |
if (myErr != noErr) |
goto bail; |
// export the movie into a file |
myErr = ConvertMovieToFile( |
theMovie, // the movie to convert |
NULL, // all tracks in the movie |
theFile, // the output file |
kQTFileTypeWave, // the output file type |
FOUR_CHAR_CODE('TVOD'), // the output file creator |
smSystemScript, // the script |
NULL, // no resource ID to be returned |
0L, // no flags |
myComponent); // the export component |
bail: |
// dispose of any storage we allocated |
if (myComponent != NULL) |
CloseComponent(myComponent); |
if (myDesc != NULL) |
DisposeHandle((Handle)myDesc); |
return((OSErr)myErr); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Standard sound compression dialog routines. |
// |
// These routines illustrate how to use QuickTime's standard sound compression dialog routines |
// to get compression settings from the user and to compress a sound using those settings. See |
// Chapter 20 of QuickTime 3 Reference for information on the standard sound compression dialog |
// routines. |
// |
// These routines show how to display the standard sound compression dialog box directly; you can |
// also display the dialog box indirectly by using ConvertMovieToFile with the showUserSettingsDialog |
// flag set; see SndSnip_SaveSoundTrackAsAnyTypeFile above for an example. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// SndSnip_PromptUserForAudioFileAndCompress |
// Let the user select an audio file and select its compression settings; then compress it. |
// |
////////// |
void SndSnip_PromptUserForAudioFileAndCompress (void) |
{ |
SFTypeList myTypeList; |
StandardFileReply myReply; |
short myRefNum = 0; |
SoundComponentData mySrcInfo; |
SoundComponentData myDstInfo; |
unsigned long myNumFrames = 0; |
unsigned long myDataOffset = 0; |
ComponentInstance myComponent = NULL; |
OSErr myErr = noErr; |
////////// |
// |
// have the user select an AIFF or AIFF-C file |
// |
////////// |
myTypeList[0] = kQTFileTypeAIFF; |
myTypeList[1] = kQTFileTypeAIFC; |
StandardGetFilePreview(NULL, 2, myTypeList, &myReply); |
if (!myReply.sfGood) |
goto bail; |
////////// |
// |
// open the selected sound file and get the characteristics of the sound |
// |
////////// |
myErr = FSpOpenDF(&myReply.sfFile, fsCurPerm, &myRefNum); |
if (myErr != noErr) |
goto bail; |
myErr = ParseAIFFHeader(myRefNum, &mySrcInfo, &myNumFrames, &myDataOffset); |
if (myErr != noErr) |
goto bail; |
// zero out fields we're not interested in |
mySrcInfo.flags = 0L; |
mySrcInfo.sampleCount = 0L; |
mySrcInfo.buffer = NULL; |
mySrcInfo.reserved = 0L; |
////////// |
// |
// display the standard sound compression dialog box |
// |
////////// |
// open the standard compression dialog component |
myComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubTypeSound); |
if (myComponent == NULL) |
goto bail; |
// set the initial values for the dialog box, using the current characteristics of the selected sound |
SCSetInfo(myComponent, scSoundSampleRateType, &(mySrcInfo.sampleRate)); |
SCSetInfo(myComponent, scSoundSampleSizeType, &(mySrcInfo.sampleSize)); |
SCSetInfo(myComponent, scSoundChannelCountType, &(mySrcInfo.numChannels)); |
SCSetInfo(myComponent, scSoundCompressionType, &(mySrcInfo.format)); |
// request sound compression settings from the user; in other words, put up the dialog box |
// (don't let "Image" in the name here fool you: it's just a relic from the fact that the |
// sound compression dialog routines were based on the existing image compression dialog routines) |
myErr = SCRequestImageSettings(myComponent); |
if (myErr == userCanceledErr) |
goto bail; |
////////// |
// |
// retrieve the compression/conversion settings selected by the user |
// |
////////// |
SCGetInfo(myComponent, scSoundSampleRateType, &(myDstInfo.sampleRate)); |
SCGetInfo(myComponent, scSoundSampleSizeType, &(myDstInfo.sampleSize)); |
SCGetInfo(myComponent, scSoundChannelCountType, &(myDstInfo.numChannels)); |
SCGetInfo(myComponent, scSoundCompressionType, &(myDstInfo.format)); |
// any non-compressed format appears as 'raw ', which is not what the Sound Manager expects |
if (myDstInfo.format == k8BitOffsetBinaryFormat) |
myDstInfo.format = kSoundNotCompressed; |
// zero out fields we're not interested in |
myDstInfo.flags = 0L; |
myDstInfo.sampleCount = 0L; |
myDstInfo.buffer = NULL; |
myDstInfo.reserved = 0L; |
////////// |
// |
// save the compressed sound in a new file |
// |
////////// |
SndSnip_PromptUserForDiskFileAndSaveCompressed(myRefNum, &mySrcInfo, &myDstInfo, myDataOffset, myNumFrames); |
bail: |
if (myComponent != NULL) |
CloseComponent(myComponent); |
if (myRefNum != 0) |
FSClose(myRefNum); |
} |
////////// |
// |
// SndSnip_PromptUserForDiskFileAndSaveCompressed |
// Let the user select a new disk file, then convert the specified audio data into that file. |
// |
// This routine uses the Sound Converter routines introduced in Sound Manager 3.2. |
// |
////////// |
void SndSnip_PromptUserForDiskFileAndSaveCompressed (short theSrcRefNum, SoundComponentData *theSrcInfo, SoundComponentData *theDstInfo, unsigned long theSrcDataOffset, unsigned long theSrcNumFrames) |
{ |
StandardFileReply myReply; |
SoundConverter myConverter = NULL; |
unsigned long myNumInputFrames, myNumInputBytes, myNumOutputBytes; |
Ptr myInputBuffer = NULL; |
Ptr myOutputBuffer = NULL; |
short myDstRefNum = 0; |
StringPtr myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt); |
StringPtr myFileName = QTUtils_ConvertCToPascalString(kSaveSoundFileName); |
OSErr myErr = noErr; |
////////// |
// |
// prompt the user for a file to put the compressed sound into |
// |
////////// |
StandardPutFile(myPrompt, myFileName, &myReply); |
if (!myReply.sfGood) |
goto bail; |
if (myReply.sfReplacing) { |
myErr = FSpDelete(&myReply.sfFile); |
if (myErr != noErr) |
goto bail; |
} |
////////// |
// |
// open a sound converter |
// |
////////// |
// perform in non-real time for best quality |
theDstInfo->flags |= kNoRealtimeProcessing; |
myErr = SoundConverterOpen(theSrcInfo, theDstInfo, &myConverter); |
if (myErr != noErr) |
goto bail; |
myErr = SoundConverterGetBufferSizes(myConverter, kNumberOfTargetBytes, &myNumInputFrames, &myNumInputBytes, &myNumOutputBytes); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// convert the sound to the desired output format |
// |
////////// |
// create the input and output buffers |
myInputBuffer = NewPtrClear(myNumInputBytes); |
if (myInputBuffer == NULL) |
goto bail; |
myOutputBuffer = NewPtrClear(myNumOutputBytes); |
if (myOutputBuffer == NULL) |
goto bail; |
// create and open the output file |
myErr = FSpCreate(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), kQTFileTypeAIFC, smSystemScript); |
if (myErr != noErr) |
goto bail; |
myErr = FSpOpenDF(&myReply.sfFile, fsRdWrPerm, &myDstRefNum); |
if (myErr != noErr) |
goto bail; |
SetEOF(myDstRefNum, 0L); |
// convert the audio data |
myErr = SoundConverterBeginConversion(myConverter); |
if (myErr == noErr) { |
long myInputFileSize = 0L; |
unsigned long myActOutputFrames = 0L; |
unsigned long myActOutputSize = 0L; |
unsigned long myAddOutputFrames = 0L; |
unsigned long myAddOutputSize = 0L; |
long myCurrentFrames = 0L; |
long myNumFramesLeft = theSrcNumFrames; |
long myBytesRead = 0L; |
long myBytesWritten = 0L; |
long myFramesWritten = 0L; |
// set up the input file for reading |
GetEOF(theSrcRefNum, &myInputFileSize); |
myErr = SetFPos(theSrcRefNum, fsFromStart, theSrcDataOffset); |
if (myErr != noErr) |
goto bail; |
myInputFileSize -= theSrcDataOffset; |
// set up a preliminary AIFF header for the output file; |
// we'll update the actual values later.... |
myErr = SetupAIFFHeader(myDstRefNum, |
theDstInfo->numChannels, |
theDstInfo->sampleRate, |
theDstInfo->sampleSize, |
theDstInfo->format, |
0L, |
0L); |
if (myErr != noErr) |
goto bail; |
myCurrentFrames = myNumInputFrames; |
// loop through buffers of size myNumInputFrames |
while (myNumFramesLeft > 0) { |
// read a buffer of data from the input file |
myBytesRead = myNumInputBytes; |
myErr = FSRead(theSrcRefNum, &myBytesRead, myInputBuffer); |
if ((myErr != noErr) && (myErr != eofErr)) |
goto bail; |
// convert the buffer |
myErr = SoundConverterConvertBuffer(myConverter, |
myInputBuffer, |
myCurrentFrames, |
myOutputBuffer, |
&myActOutputFrames, |
&myActOutputSize); |
if (myErr != noErr) |
goto bail; |
// write the converted data into the output file |
myErr = FSWrite(myDstRefNum, (long *)&myActOutputSize, myOutputBuffer); |
if (myErr != noErr) |
goto bail; |
myBytesWritten += myActOutputSize; |
myFramesWritten += myActOutputFrames; |
// calculate remaining frames and adjust myCurrentFrames for last buffer |
myNumFramesLeft -= myCurrentFrames; |
if (myNumFramesLeft < myNumInputFrames) |
myCurrentFrames = myNumFramesLeft; |
} |
// end the conversion, and see if we get back a few more bytes of data |
myErr = SoundConverterEndConversion(myConverter, myOutputBuffer, &myAddOutputFrames, &myAddOutputSize); |
if (myErr != noErr) |
goto bail; |
// if we got any more data, write it to disk |
if (myAddOutputFrames != 0L) { |
myErr = FSWrite(myDstRefNum, (long *)&myAddOutputSize, myOutputBuffer); |
if (myErr != noErr) |
goto bail; |
} |
myErr = SetFPos(myDstRefNum, fsFromStart, 0); |
if (myErr != noErr) |
goto bail; |
// update AIFF header |
myErr = SetupAIFFHeader( |
myDstRefNum, |
theDstInfo->numChannels, |
theDstInfo->sampleRate, |
theDstInfo->sampleSize, |
theDstInfo->format, |
myBytesWritten + myAddOutputSize, |
myFramesWritten + myAddOutputFrames); |
} |
bail: |
free(myPrompt); |
free(myFileName); |
if (myConverter != NULL) |
SoundConverterClose(myConverter); |
if (myInputBuffer != NULL) |
DisposePtr(myInputBuffer); |
if (myOutputBuffer != NULL) |
DisposePtr(myOutputBuffer); |
if (myDstRefNum != 0) |
FSClose(myDstRefNum); |
#if TARGET_OS_MAC |
// make sure the file data gets written out to disk |
FlushVol("\p", myReply.sfFile.vRefNum); |
#endif |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-27