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.
Relevant replacement documents include:
AudioConvert.c
////////// |
// |
// File: AudioConvert.c |
// |
// Contains: Sample code showing how to use the Sound Manager's SoundConverter routines. |
// |
// Written by: Jim Reekes |
// Revised by: Tim Monroe |
// |
// Copyright: © 1999 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <5> 03/29/00 rtm made changes to get things running under CarbonLib |
// <4> 07/13/99 rtm final adjustments; fixed cross-platform problems resulting from using |
// kSoundNotCompressed ('NONE') format (thanks to EJ Campbell!); see NOTE below |
// <3> 07/07/99 rtm further work; revised coding style; added some comments |
// <2> 03/12/99 rtm added (theDialog == NULL) check to AudConv_SFGetDialogHook to prevent |
// crashing on Windows; simplified UPP handling |
// <1> 03/09/99 rtm first file from jr; added some comments; added Windows-specific code |
// and endian macros |
// |
// This code shows how to use the SoundConverter routines (introduced in Sound Manager version 3.2). It |
// provides routines that convert an AIFF or AIFF-C file into either a QuickTime movie or into an AIFF or |
// AIFF-C file. The user can select the desired target format and settings in a dialog box displayed by |
// a call to SCRequestImageSettings. |
// |
// You should note in particular that this sample code shows how to handle the compression parameters chunk |
// that might be present in an AIFF or AIFF-C file. The compression parameters chunk contains parameters |
// that are required to decompress the audio data in the file. |
// |
// NOTE: Do not use kSoundNotCompressed as an output format for multi-byte audio data if you want to create |
// movies that are to be shared across platforms. QuickTime needs to know the endianness of the sound data, |
// and kSoundNotCompressed does not indicate whether the data is big- or little-endian. Since in this sample |
// code we are reading our data from an AIFF file, we specify that any non-compressed multi-byte data is of |
// the format k16BitBigEndianFormat. |
// |
////////// |
#include "AudioConvert.h" |
////////// |
// |
// main/WinMain |
// The main function for this application. |
// |
////////// |
#if TARGET_OS_MAC |
void main (void) |
#elif TARGET_OS_WIN32 |
int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR theCmdLine, int nCmdShow) |
#endif |
{ |
SoundComponentData myDestInfo; |
#if TARGET_API_MAC_CARBON |
NavReplyRecord myReply; |
NavDialogOptions myDialogOptions; |
AEKeyword myKeyword; |
DescType myActualType; |
Size myActualSize = 0; |
NavEventUPP myEventUPP = NewNavEventUPP(AudConv_HandleNavEvent); |
NavObjectFilterUPP myFilterProc = NewNavObjectFilterUPP(AudConv_NavObjectFilterProc); |
#else |
OSType myTypeList[2] = {kQTFileTypeAIFF, kQTFileTypeAIFC}; |
StandardFileReply myReply; |
#endif |
FSSpec myInputFSSpec; |
FSSpec myOutputFSSpec; |
Boolean myOutputAIFF; |
Str255 myAppName; |
OSErr myErr = noErr; |
#if TARGET_OS_WIN32 |
char myFileName[MAX_PATH]; |
DWORD myLength; |
short myAppResFile = -1; // file reference number for this application's resource file |
InitializeQTML(0L); // initialize QuickTime Media Layer |
#endif |
#if TARGET_OS_MAC |
#if !TARGET_API_MAC_CARBON |
MaxApplZone(); // init everything |
InitGraf(&qd.thePort); |
InitFonts(); |
FlushEvents(everyEvent,0); |
InitWindows(); |
InitMenus(); |
InitDialogs(NULL); |
TEInit(); |
#endif |
InitCursor(); |
#endif |
myErr = EnterMovies(); |
FailIf(myErr != noErr, Exit); |
#if TARGET_OS_WIN32 |
// open the application's resource file, if it exists |
myLength = GetModuleFileName(NULL, myFileName, MAX_PATH); // NULL means: the current process |
if (myLength != 0) { |
FSSpec myFSSpec; |
NativePathNameToFSSpec(myFileName, &myFSSpec, 0L); |
myAppResFile = FSpOpenResFile(&myFSSpec, fsRdWrPerm); |
if (myAppResFile != -1) |
UseResFile(myAppResFile); |
} |
#endif |
GetIndString(myAppName, rStringsResID, rAppNameStringIndex); |
myOutputAIFF = false; // set default file format to 'MooV' file |
#if TARGET_API_MAC_CARBON |
// we are running under Carbon; use Navigation Services |
////////// |
// |
// elicit an AIFF or an AIFC file from the user |
// |
////////// |
// specify the options for the dialog box |
NavGetDefaultDialogOptions(&myDialogOptions); |
myDialogOptions.dialogOptionFlags -= kNavNoTypePopup; |
myDialogOptions.dialogOptionFlags -= kNavAllowMultipleFiles; |
BlockMoveData(myAppName, myDialogOptions.clientName, myAppName[0] + 1); |
myErr = NavGetFile(NULL, &myReply, &myDialogOptions, myEventUPP, NULL, (NavObjectFilterUPP)myFilterProc, NULL, NULL); |
if ((myErr == noErr) && myReply.validRecord) { |
StringPtr myPrompt = AudConv_ConvertCToPascalString(kACSavePrompt); |
StringPtr myFileName = AudConv_ConvertCToPascalString(kMovieFileSaveName); |
NavMenuItemSpecHandle myMenuItemHandle = NULL; |
// get the FSSpec for the selected file |
myErr = AEGetNthPtr(&(myReply.selection), 1, typeFSS, &myKeyword, &myActualType, &myInputFSSpec, sizeof(FSSpec), &myActualSize); |
NavDisposeReply(&myReply); |
// elicit a file to save the converted sound data into; |
// we want to allow the user to save as a movie or as an AIFF file |
myMenuItemHandle = (NavMenuItemSpecHandle)NewHandleClear(sizeof(NavMenuItemSpec)); |
if (myMenuItemHandle != NULL) { |
(**myMenuItemHandle).version = kNavMenuItemSpecVersion; |
(**myMenuItemHandle).menuCreator = FOUR_CHAR_CODE('AuCo'); |
(**myMenuItemHandle).menuType = kQTFileTypeAIFF; |
GetIndString((**myMenuItemHandle).menuItemName, rStringsResID, rAIFFMenuLabelIndex); |
} |
NavGetDefaultDialogOptions(&myDialogOptions); |
myDialogOptions.dialogOptionFlags -= kNavAllowStationery; |
myDialogOptions.dialogOptionFlags += kNavDontAutoTranslate; |
myDialogOptions.popupExtension = myMenuItemHandle; |
BlockMoveData(myFileName, myDialogOptions.savedFileName, myFileName[0] + 1); |
BlockMoveData(myAppName, myDialogOptions.clientName, myAppName[0] + 1); |
BlockMoveData(myPrompt, myDialogOptions.message, myPrompt[0] + 1); |
myErr = NavPutFile(NULL, &myReply, &myDialogOptions, myEventUPP, MovieFileType, sigMoviePlayer, &myOutputAIFF); |
if ((myErr == noErr) && myReply.validRecord) { |
// get the FSSpec for the selected file |
myErr = AEGetNthPtr(&(myReply.selection), 1, typeFSS, &myKeyword, &myActualType, &myOutputFSSpec, sizeof(FSSpec), &myActualSize); |
if (myReply.replacing) { |
myErr = FSpDelete(&myOutputFSSpec); |
FailIf(myErr != noErr, Failure); |
} |
myErr = AudConv_GetOutputFormat(&myDestInfo); |
if (myErr == noErr) { |
if (myOutputAIFF) |
myErr = AudConv_ConvertToAIFF(&myInputFSSpec, &myOutputFSSpec, &myDestInfo); |
else |
myErr = AudConv_ConvertToMovie(&myInputFSSpec, &myOutputFSSpec, &myDestInfo); |
FailIf(myErr != noErr, Failure); |
} |
if (myErr == userCanceledErr) |
myErr = noErr; |
FailIf(myErr != noErr, Failure); |
NavDisposeReply(&myReply); |
} |
free(myPrompt); |
free(myFileName); |
} |
DisposeNavEventUPP(myEventUPP); |
DisposeNavObjectFilterUPP(myFilterProc); |
#else |
// we are NOT running under Carbon; use Standard File Package |
////////// |
// |
// elicit an AIFF or an AIFC file from the user |
// |
////////// |
StandardGetFile(NULL, 2, myTypeList, &myReply); |
if (myReply.sfGood) { |
DlgHookYDUPP myDlgHookUPP = NULL; |
Point myTopLeft; |
myDlgHookUPP = NewDlgHookYDProc(AudConv_SFGetDialogHook); |
myInputFSSpec = myReply.sfFile; // save input file spec |
// elicit a file to save the converted sound data into |
myTopLeft.v = -1; // -1,-1 means to center the dialog |
myTopLeft.h = -1; |
CustomPutFile(NULL, c2pstr(kAIFFFileSaveName), &myReply, rCustomGetFileDialog, myTopLeft, myDlgHookUPP, NULL, NULL, NULL, &myOutputAIFF); |
if (myReply.sfGood) { |
myOutputFSSpec = myReply.sfFile; // save output file spec |
if (myReply.sfReplacing) { |
myErr = FSpDelete(&myOutputFSSpec); |
FailIf(myErr != noErr, Failure); |
} |
myErr = AudConv_GetOutputFormat(&myDestInfo); |
if (myErr == noErr) { |
if (myOutputAIFF) |
myErr = AudConv_ConvertToAIFF(&myInputFSSpec, &myOutputFSSpec, &myDestInfo); |
else |
myErr = AudConv_ConvertToMovie(&myInputFSSpec, &myOutputFSSpec, &myDestInfo); |
FailIf(myErr != noErr, Failure); |
} |
if (myErr == userCanceledErr) |
myErr = noErr; |
FailIf(myErr != noErr, Failure); |
} |
} |
#endif |
Failure: |
ExitMovies(); |
Exit: |
#if TARGET_OS_WIN32 |
// terminate the QuickTime Media Layer |
TerminateQTML(); |
return(1); |
#endif |
#if TARGET_OS_MAC |
return; |
#endif |
} |
////////// |
// |
// AudConv_ConvertToAIFF |
// Read the source AIFF file and convert to the destination format; output a new AIFF file. |
// The new file must include the compression parameters chunk, if used. |
// |
////////// |
OSErr AudConv_ConvertToAIFF (ConstFSSpecPtr theInputFSSpec, ConstFSSpecPtr theOutputFSSpec, SoundComponentDataPtr theDestInfo) |
{ |
SoundComponentData mySourceInfo; |
Handle mySourceCompParamsHandle = NULL; |
Handle myDestHandle = NULL; |
Handle myDestCompParamsHandle = NULL; |
long mySourceOffset; |
long mySourceSize; |
short myInputFile; |
short myOutputFile; |
long myCount; |
OSErr myErr = noErr; |
// open the source file and parse it |
myErr = FSpOpenDF(theInputFSSpec, fsRdPerm, &myInputFile); |
FailIf(myErr != noErr, NoInputFile); |
myErr = AudConv_GetDataFromAIFF(myInputFile, &mySourceInfo, &mySourceOffset, &mySourceSize, &mySourceCompParamsHandle); |
FailIf(myErr != noErr, NoData); |
// create the buffer into which we read the source data |
mySourceInfo.buffer = (Byte *)NewPtrClear(mySourceSize); |
FailWithAction(mySourceInfo.buffer == NULL, myErr = MemError(), NoSource); |
// create the buffer to hold the converted data |
myDestHandle = NewHandle(0); |
FailWithAction(myDestHandle == NULL, myErr = MemError(), NoDest); |
// read the source data from the source file into the source data buffer |
myErr = SetFPos(myInputFile, fsFromStart, mySourceOffset); |
FailIf(myErr != noErr, InputFileErr); |
myErr = FSRead(myInputFile, &mySourceSize, mySourceInfo.buffer); |
FailIf(myErr != noErr, InputFileErr); |
// create the output AIFF file, to hold the converted data |
myErr = FSpCreate(theOutputFSSpec, sigMoviePlayer, AIFFID, smSystemScript); |
FailIf(myErr != noErr, CreateOutputErr); |
myErr = FSpOpenDF(theOutputFSSpec, fsRdWrPerm, &myOutputFile); |
FailIf(myErr != noErr, OutputFileErr); |
// convert the source data into the output data |
myErr = AudConv_ConvertAudioIntoHandle(&mySourceInfo, mySourceCompParamsHandle, myDestHandle, theDestInfo, &myDestCompParamsHandle, NULL); |
FailIf(myErr != noErr, ConvertErr); |
// put the output data into the output AIFF file |
myErr = AudConv_PrepFileAsAIFF(myOutputFile, theDestInfo, myDestCompParamsHandle); |
FailIf(myErr != noErr, ConvertErr); |
HLock(myDestHandle); |
myCount = GetHandleSize(myDestHandle); |
myErr = FSWrite(myOutputFile, &myCount, *myDestHandle); |
HUnlock(myDestHandle); |
FailIf(myErr != noErr, ConvertErr); |
myErr = AudConv_FinishFileAsAIFF(myOutputFile, theDestInfo->sampleCount, myCount); |
FailIf(myErr != noErr, ConvertErr); |
// close the input and output files |
myErr = FSClose(myInputFile); |
FailIf(myErr != noErr, ConvertErr); |
myErr = FSClose(myOutputFile); |
FailIf(myErr != noErr, ConvertErr); |
// dispose of any storage we allocated and no longer need |
if (mySourceInfo.buffer != NULL) |
DisposePtr((Ptr)mySourceInfo.buffer); |
if (myDestHandle != NULL) |
DisposeHandle(myDestHandle); |
if (mySourceCompParamsHandle != NULL) |
DisposeHandle(mySourceCompParamsHandle); |
if (myDestCompParamsHandle != NULL) |
DisposeHandle(myDestCompParamsHandle); |
return(noErr); |
ConvertErr: |
FSClose(myOutputFile); |
OutputFileErr: |
FSpDelete(theOutputFSSpec); |
CreateOutputErr: |
InputFileErr: |
if (myDestHandle != NULL) |
DisposeHandle(myDestHandle); |
NoDest: |
if (mySourceInfo.buffer != NULL) |
DisposePtr((Ptr)mySourceInfo.buffer); |
NoSource: |
if (mySourceCompParamsHandle != NULL) |
DisposeHandle(mySourceCompParamsHandle); |
NoData: |
FSClose(myInputFile); |
NoInputFile: |
return(myErr); |
} |
////////// |
// |
// AudConv_ConvertToMovie |
// Read the source AIFF file and convert to the destination format; output a new movie file. |
// The new file must include the compression parameters chunk, if used. |
// |
////////// |
OSErr AudConv_ConvertToMovie (ConstFSSpecPtr theInputFSSpec, ConstFSSpecPtr theOutputFSSpec, SoundComponentDataPtr theDestInfo) |
{ |
SoundComponentData mySourceInfo; |
Handle mySourceCompParamsHandle = NULL; |
Handle myDestHandle = NULL; |
Handle myDestCompParamsHandle = NULL; |
long mySourceOffset; |
long mySourceSize; |
short myInputFile; |
CompressionInfo myDestCompInfo; |
Movie myMovie; |
Track myTrack; |
short myResRefNum = -1; |
short myResID = movieInDataForkResID; |
long myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile; |
OSErr myErr = noErr; |
// open the source file and parse it |
myErr = FSpOpenDF(theInputFSSpec, fsRdPerm, &myInputFile); |
FailIf(myErr != noErr, NoInputFile); |
myErr = AudConv_GetDataFromAIFF(myInputFile, &mySourceInfo, &mySourceOffset, &mySourceSize, &mySourceCompParamsHandle); |
FailIf(myErr != noErr, NoData); |
// create the buffer into which we read the source data |
mySourceInfo.buffer = (Byte *)NewPtrClear(mySourceSize); |
FailWithAction(mySourceInfo.buffer == NULL, myErr = MemError(), NoSource); |
// create the buffer to hold the converted data |
myDestHandle = NewHandle(0); |
FailWithAction(myDestHandle == NULL, myErr = MemError(), NoDest); |
// read the source data from the source file into the source data buffer |
myErr = SetFPos(myInputFile, fsFromStart, mySourceOffset); |
FailIf(myErr != noErr, InputFileErr); |
myErr = FSRead(myInputFile, &mySourceSize, mySourceInfo.buffer); |
FailIf(myErr != noErr, InputFileErr); |
// create the output movie file, to hold the converted data |
myErr = CreateMovieFile(theOutputFSSpec, sigMoviePlayer, smCurrentScript, myFlags, &myResRefNum, &myMovie); |
FailIf(myErr != noErr, CreateOutputErr); |
// convert the source data into the output data |
myErr = AudConv_ConvertAudioIntoHandle(&mySourceInfo, mySourceCompParamsHandle, myDestHandle, theDestInfo, &myDestCompParamsHandle, &myDestCompInfo); |
FailIf(myErr != noErr, CompressErr); |
// put the output data into a track in the output movie file |
myTrack = NewMovieTrack(myMovie, 0, 0, kFullVolume); |
myErr = GetMoviesError(); |
FailIf(myErr != noErr, CompressErr); |
myErr = AudConv_PutAudioIntoTrack(myTrack, myDestHandle, theDestInfo, myDestCompParamsHandle, &myDestCompInfo); |
FailIf(myErr != noErr, CompressErr); |
myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL); |
FailIf(myErr != noErr, CompressErr); |
// close the movie and movie file |
if (myResRefNum != -1) |
CloseMovieFile(myResRefNum); |
if (myMovie != NULL) |
DisposeMovie(myMovie); |
// dispose of any storage we allocated and no longer need |
if (mySourceInfo.buffer != NULL) |
DisposePtr((Ptr)mySourceInfo.buffer); |
if (myDestHandle != NULL) |
DisposeHandle(myDestHandle); |
if (mySourceCompParamsHandle != NULL) |
DisposeHandle(mySourceCompParamsHandle); |
if (myDestCompParamsHandle != NULL) |
DisposeHandle(myDestCompParamsHandle); |
return(noErr); |
CompressErr: |
if (myMovie != NULL) |
DisposeMovie(myMovie); |
if (myResRefNum != -1) |
CloseMovieFile(myResRefNum); |
CreateOutputErr: |
InputFileErr: |
if (mySourceCompParamsHandle != NULL) |
DisposeHandle(mySourceCompParamsHandle); |
NoDest: |
if (mySourceInfo.buffer != NULL) |
DisposePtr((Ptr)mySourceInfo.buffer); |
NoSource: |
if (mySourceCompParamsHandle != NULL) |
DisposeHandle(mySourceCompParamsHandle); |
NoData: |
FSClose(myInputFile); |
NoInputFile: |
return(myErr); |
} |
////////// |
// |
// AudConv_GetDataFromAIFF |
// Parse the specified file and read the data in the required chunks in it. |
// |
// This is a very simple AIFF parser which will read three basic chunks: the |
// main ContainerChunk, the Common or ExtCommonChunk, and the SoundDataChunk. |
// That's all that's required. One last chunk is read if found, which contains the |
// decompression parameters required to decompress this audio. It's a simple |
// chunk with unknown data contained within. Copy this data into a handle, and |
// it becomes the audio atoms used with the siDecompressionParams selector. |
// |
// Keep in mind that the data in an AIFF file is always big-endian. |
// |
////////// |
OSErr AudConv_GetDataFromAIFF (short theRefNum, SoundComponentDataPtr theSourceInfo, long *theSourceOffset, long *theSourceSize, Handle *theDestCompParams) |
{ |
union { |
ContainerChunk cnt; |
CommonChunk com; |
ExtCommonChunk ext; |
SoundDataChunk snd; |
} myChunk; |
ChunkHeader myCHeader; |
Handle myDestCompParamsHandle; |
long double mySampleRate; |
long myOffset; |
long myDataSize; |
long myByteCount; |
OSErr myErr = noErr; |
myErr = SetFPos(theRefNum, fsFromStart, 0); // we have to start at the beginning |
FailIf(myErr != noErr, Failure); |
// everything is contained in a container chunk, which therefore is the first chunk in the file; |
// read the beginning of the container chunk into memory |
myByteCount = sizeof(ContainerChunk); |
myErr = FSRead(theRefNum, &myByteCount, &myChunk); |
FailIf(myErr != noErr, Failure); |
// make sure it's a FORM chunk |
FailWithAction(EndianU32_BtoN(myChunk.cnt.ckID) != FORMID, myErr = badFileFormat, Failure); |
// find the CommonChunk or ExtCommonChunk |
myErr = AudConv_SetFPosToChunk(theRefNum, CommonID); |
FailIf(myErr != noErr, Failure); |
switch (EndianU32_BtoN(myChunk.cnt.formType)) { |
case AIFFID: |
myByteCount = sizeof(CommonChunk); |
myErr = FSRead(theRefNum, &myByteCount, &myChunk); |
FailIf(myErr != noErr, Failure); |
theSourceInfo->format = k16BitBigEndianFormat; |
break; |
case AIFCID: |
myByteCount = offsetof(ExtCommonChunk, compressionName); |
myErr = FSRead(theRefNum, &myByteCount, &myChunk); |
FailIf(myErr != noErr, Failure); |
theSourceInfo->format = EndianU32_BtoN(myChunk.ext.compressionType); |
break; |
default: |
FailWithAction(badFileFormat, myErr = badFileFormat, Failure); |
break; |
} |
theSourceInfo->flags = 0L; |
theSourceInfo->numChannels = EndianS16_BtoN(myChunk.com.numChannels); |
theSourceInfo->sampleSize = EndianS16_BtoN(myChunk.com.sampleSize); |
#if TARGET_CPU_68K |
mySampleRate = myChunk.com.sampleRate; |
#else |
x80told(&myChunk.com.sampleRate, &mySampleRate); |
#endif |
theSourceInfo->sampleRate = (unsigned long)(mySampleRate * 65536.0); |
theSourceInfo->sampleCount = EndianU32_BtoN(myChunk.com.numSampleFrames); |
theSourceInfo->buffer = NULL; |
theSourceInfo->reserved = 0; |
myErr = AudConv_SetFPosToChunk(theRefNum, SoundDataID); |
FailIf(myErr != noErr, Failure); |
myByteCount = sizeof(SoundDataChunk); |
myErr = FSRead(theRefNum, &myByteCount, &myChunk); |
FailIf(myErr != noErr, Failure); |
myDataSize = EndianS32_BtoN(myChunk.snd.ckSize) - sizeof(SoundDataChunk); // size of source data |
myErr = GetFPos(theRefNum, &myOffset); |
FailIf(myErr != noErr, Failure); |
// return NULL if no siDecompressionParams |
myDestCompParamsHandle = NULL; |
myErr = AudConv_SetFPosToChunk(theRefNum, siDecompressionParams); |
if (myErr == noErr) { |
myByteCount = sizeof(myCHeader); |
myErr = FSRead(theRefNum, &myByteCount, &myCHeader); |
FailIf(myErr != noErr, Failure); |
myByteCount = EndianS32_BtoN(myCHeader.ckSize); |
myDestCompParamsHandle = NewHandle(myByteCount); |
FailIf(myDestCompParamsHandle == NULL, Failure); |
HLock(myDestCompParamsHandle); |
myErr = FSRead(theRefNum, &myByteCount, *myDestCompParamsHandle); |
HUnlock(myDestCompParamsHandle); |
FailIf(myErr != noErr, ReadParamsErr); |
} |
*theSourceOffset = myOffset; // offset from start of file to audio samples |
*theSourceSize = myDataSize; // size in bytes of the audio samples |
*theDestCompParams = myDestCompParamsHandle; // possible handle to the decompression parameters |
return(noErr); |
ReadParamsErr: |
if (myDestCompParamsHandle != NULL) |
DisposeHandle(myDestCompParamsHandle); |
Failure: |
return(myErr); |
} |
////////// |
// |
// AudConv_ConvertAudioIntoHandle |
// Convert the specified sound data. |
// |
////////// |
OSErr AudConv_ConvertAudioIntoHandle ( SoundComponentDataPtr theSourceInfo, |
Handle theSourceCompParamsHandle, |
Handle theDestHandle, |
SoundComponentDataPtr theDestInfo, |
Handle *theDestCompParamsHandle, |
CompressionInfoPtr theDestCompInfo) |
{ |
SoundConverter myConverter; |
Handle myDestCompParamsHandle = NULL; |
CompressionInfo myCompressionInfo; |
unsigned short mySourceBytesPerFrame; |
UInt16 hasOptionsDialog = 0; |
OSErr myErr = noErr; |
// perform in non-real time for best quality |
theDestInfo->flags |= kNoRealtimeProcessing; |
////////// |
// |
// open a sound converter |
// |
////////// |
myErr = SoundConverterOpen(theSourceInfo, theDestInfo, &myConverter); |
FailIf(myErr != noErr, Exit); |
////////// |
// |
// display the options dialog for the sound converter |
// |
// the GetInfo call isn't required; you can just call SetInfo and it will show the dialog |
// or return the same error that GetInfo would have; you might call GetInfo to determine |
// whether the options dialog is supported, perhaps because you want to show a button that |
// would bring up this dialog |
// |
////////// |
myErr = SoundConverterGetInfo(myConverter, siOptionsDialog, &hasOptionsDialog); |
if ((myErr == noErr) && (hasOptionsDialog != 0)) { |
myErr = SoundConverterSetInfo(myConverter, siOptionsDialog, NULL); |
FailIf(myErr != noErr, DialogErr); |
} |
// set the number of output channels |
myErr = SoundConverterSetInfo(myConverter, siCompressionChannels, &theDestInfo->numChannels); |
// ignore this error, some codecs don't use this selector (makes QDesign work) |
////////// |
// |
// get any custom decompression settings |
// |
// not all sound compressors have custom settings, in which case myDestCompParamsHandle |
// will be returned unchanged (NULL); we return these custom settings to the caller, who |
// will put them in the proper place; if this audio is stored in a QuickTime movie, these |
// settings will be stored in a SoundDescriptionExtension of type siDecompressionParams |
// |
////////// |
SoundConverterGetInfo(myConverter, siCompressionParams, &myDestCompParamsHandle); |
// send the source and destination compression settings to the sound converter; this is |
// necessary to make some sound converters (for instance, QDesign) work properly |
if (myDestCompParamsHandle != NULL) { |
HLockHi(myDestCompParamsHandle); |
myErr = SoundConverterSetInfo(myConverter, siCompressionParams, *myDestCompParamsHandle); |
HUnlock(myDestCompParamsHandle); |
FailIf(myErr != noErr, ConverterErr); |
} |
if (theSourceCompParamsHandle != NULL) { |
HLockHi(theSourceCompParamsHandle); |
myErr = SoundConverterSetInfo(myConverter, siDecompressionParams, *theSourceCompParamsHandle); |
HUnlock(theSourceCompParamsHandle); |
FailIf(myErr != noErr, ConverterErr); |
} |
*theDestCompParamsHandle = myDestCompParamsHandle; |
////////// |
// |
// get the sizes of the source and destination sample frames |
// |
// we return the destination sample frame size to the caller, who will put it in the |
// proper place; if this audio data is stored in a QuickTime movie, this size will be |
// stored the Version 1 sound description handle |
// |
////////// |
// get the source number of bytes in each sample frame |
myErr = SoundConverterGetInfo(myConverter, siCompressionFactor, &myCompressionInfo); |
if ((myErr != noErr) || (myCompressionInfo.format != theSourceInfo->format)) { |
myErr = GetCompressionInfo(fixedCompression, theSourceInfo->format, theSourceInfo->numChannels, theSourceInfo->sampleSize, &myCompressionInfo); |
FailIf(myErr != noErr, ConverterErr); |
} |
// bytesPerFrame is not filled in by GetCompressionInfo, so we set it here |
mySourceBytesPerFrame = myCompressionInfo.bytesPerPacket * theSourceInfo->numChannels; |
// get the destination number of bytes in each sample frame |
if (theDestCompInfo != NULL) { |
// get info about destination compression. |
myErr = SoundConverterGetInfo(myConverter, siCompressionFactor, theDestCompInfo); |
if ((myErr != noErr) || (theDestCompInfo->format != theDestInfo->format)) { |
myErr = GetCompressionInfo(fixedCompression, theDestInfo->format, theDestInfo->numChannels, theDestInfo->sampleSize, theDestCompInfo); |
FailIf(myErr != noErr, ConverterErr); |
} |
// bytesPerFrame is not filled in by GetCompressionInfo, so we set it here |
theDestCompInfo->bytesPerFrame = theDestCompInfo->bytesPerPacket * theDestInfo->numChannels; |
} |
////////// |
// |
// do the actual data conversion |
// |
////////// |
myErr = AudConv_WriteAudioToHandle(myConverter, theSourceInfo, mySourceBytesPerFrame, theDestInfo, theDestHandle); |
FailIf(myErr != noErr, ConverterErr); |
SoundConverterClose(myConverter); |
return(noErr); |
ConverterErr: |
if (myDestCompParamsHandle != NULL) |
DisposeHandle(myDestCompParamsHandle); |
DialogErr: |
if (myConverter != NULL) |
SoundConverterClose(myConverter); |
Exit: |
return(myErr); |
} |
////////// |
// |
// AudConv_WriteAudioToHandle |
// Convert the source data into an ever-expanding handle of the given format. |
// Of course this means you've only got one shot at converting the data, and it |
// all has to fit into memory. Converting larger amounts is left to the reader. |
// |
////////// |
OSErr AudConv_WriteAudioToHandle (SoundConverter theConverter, SoundComponentDataPtr theSourceInfo, short theSourceBytesPerFrame, SoundComponentDataPtr theDestInfo, Handle theDestHandle) |
{ |
unsigned long myNumFramesLeft; |
unsigned long mySourceFrames; |
unsigned long myDestFrames; |
unsigned long mySourceBytes; |
unsigned long myDestBytes; |
unsigned long myDataOffset = 0; |
Ptr myDestPtr = NULL; |
OSErr myErr = noErr; |
// get sound converter buffer size info |
myErr = SoundConverterGetBufferSizes(theConverter, kMaxSndConvtBufferSize, &mySourceFrames, &mySourceBytes, &myDestBytes); |
FailIf(myErr != noErr, Exit); |
// create destination data buffer |
myDestPtr = NewPtrClear(myDestBytes); |
FailWithAction(myErr != noErr, myErr = MemError(), Exit); |
////////// |
// |
// convert the sound to the desired output format |
// |
////////// |
myErr = SoundConverterBeginConversion(theConverter); |
FailIf(myErr != noErr, ConverterErr); |
// initialize destination total frame count to zero |
theDestInfo->sampleCount = 0; |
myNumFramesLeft = (unsigned long)theSourceInfo->sampleCount; |
while (myNumFramesLeft > 0) { |
// if there are fewer frames remaining than our source buffer size, |
// we're near the end of our data, so get just what's remaining |
if (mySourceFrames > myNumFramesLeft) |
mySourceFrames = myNumFramesLeft; |
myErr = SoundConverterConvertBuffer(theConverter, theSourceInfo->buffer + myDataOffset, mySourceFrames, myDestPtr, &myDestFrames, &myDestBytes); |
FailIf(myErr != noErr, ConverterErr); |
// write this data to the output handle |
if (myDestBytes > 0) { |
myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes); |
FailIf(myErr != noErr, ConverterErr); |
} |
// move offset to appropriate place in source data |
myDataOffset += mySourceFrames * theSourceBytesPerFrame; |
// keep track of total frames returned by converter |
theDestInfo->sampleCount += myDestFrames; |
myNumFramesLeft -= mySourceFrames; |
} |
// end the conversion, and see if we get back a few more bytes of data |
myErr = SoundConverterEndConversion(theConverter, myDestPtr, &myDestFrames, &myDestBytes); |
FailIf(myErr != noErr, ConverterErr); |
if (myDestBytes > 0) { |
// write this data to the output handle |
myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes); |
FailIf(myErr != noErr, ConverterErr); |
} |
theDestInfo->sampleCount += myDestFrames; // update final amount of samples |
ConverterErr: |
if (myDestPtr != NULL) |
DisposePtr(myDestPtr); |
Exit: |
return(myErr); |
} |
////////// |
// |
// AudConv_GetOutputFormat |
// Display the QuickTime standard sound settings dialog to obtain the output format. |
// |
////////// |
OSErr AudConv_GetOutputFormat (SoundComponentDataPtr theDestInfo) |
{ |
ComponentInstance myComponent = NULL; |
ComponentResult myErr = noErr; |
myComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubTypeSound); |
FailWithAction(myComponent == NULL, myErr = cantFindHandler, NoComponent); |
myErr = SCRequestImageSettings(myComponent); // show the dialog to user |
if (myErr == noErr) { // then get the settings |
myErr = SCGetInfo(myComponent, scSoundCompressionType, &theDestInfo->format); |
FailIf(myErr != noErr, SCGetInfoFailed); |
myErr = SCGetInfo(myComponent, scSoundChannelCountType, &theDestInfo->numChannels); |
FailIf(myErr != noErr, SCGetInfoFailed); |
myErr = SCGetInfo(myComponent, scSoundSampleSizeType, &theDestInfo->sampleSize); |
FailIf(myErr != noErr, SCGetInfoFailed); |
myErr = SCGetInfo(myComponent, scSoundSampleRateType, &theDestInfo->sampleRate); |
FailIf(myErr != noErr, SCGetInfoFailed); |
// any non-compressed format appears as 'raw ', which is not what the Sound Manager expects |
if (theDestInfo->format == k8BitOffsetBinaryFormat) { |
if (theDestInfo->sampleSize > 8) |
theDestInfo->format = k16BitBigEndianFormat; |
else |
theDestInfo->format = kSoundNotCompressed; |
} |
// clear the rest of the unused parameters |
theDestInfo->flags = 0; |
theDestInfo->sampleCount = 0; |
theDestInfo->buffer = NULL; |
theDestInfo->reserved = 0L; |
} |
return((OSErr)myErr); |
SCGetInfoFailed: |
CloseComponent(myComponent); |
NoComponent: |
return((OSErr)myErr); |
} |
////////// |
// |
// AudConv_SetFPosToChunk |
// Set the file's position to the start of the given chunk. |
// |
////////// |
OSErr AudConv_SetFPosToChunk (short theRefNum, ID theChunkID) |
{ |
long myCount; |
ChunkHeader myCHeader; |
OSErr myErr = noErr; |
// position the file mark past the ContainerChunk, which is always the first thing in the file |
myErr = SetFPos(theRefNum, fsFromStart, sizeof(ContainerChunk)); |
FailIf(myErr != noErr, Exit); |
while (true) { |
myCount = sizeof(myCHeader); |
myErr = FSRead(theRefNum, &myCount, &(myCHeader.ckID)); |
if (myErr == eofErr) // reached the end of file, exit gracefully |
break; |
FailIf(myErr != noErr, Exit); |
if (theChunkID == EndianU32_BtoN(myCHeader.ckID)) { |
myCount = -myCount; // rewind ptr to top of chunk if found |
myErr = SetFPos(theRefNum, fsFromMark, myCount); |
FailIf(myErr != noErr, Exit); |
break; |
} else { // else skip over the entire chunk |
myErr = SetFPos(theRefNum, fsFromMark, EndianS32_BtoN(myCHeader.ckSize)); |
if (myErr == eofErr) // reached the end of file, exit gracefully |
break; |
FailIf(myErr != noErr, Exit); |
} |
} |
Exit: |
return(myErr); |
} |
////////// |
// |
// AudConv_PrepFileAsAIFF |
// Write out the necessary chunks, without audio samples, to prepare for writing; |
// leave file position at the proper place for audio samples. |
// |
////////// |
OSErr AudConv_PrepFileAsAIFF (short theOutputFile, SoundComponentDataPtr theDestInfo, Handle theDestParams) |
{ |
Str255 myCompressionName; |
ContainerChunk myContainer; |
FormatVersionChunk myVersion; |
ExtCommonChunk myCommon; |
SoundDataChunk mySoundData; |
ID myFormType; |
long myCount; |
OSErr myErr = noErr; |
switch (theDestInfo->format) { |
case kSoundNotCompressed: |
case k8BitOffsetBinaryFormat: |
case k16BitBigEndianFormat: |
case k24BitFormat: |
case k32BitFormat: |
myFormType = AIFFID; |
break; |
default : |
myFormType = AIFCID; |
break; |
} |
myErr = SetFPos(theOutputFile, fsFromStart, 0); |
FailIf(myErr != noErr, Exit); |
// write out the first chunk |
myContainer.ckID = EndianU32_NtoB(FORMID); |
myContainer.ckSize = EndianU32_NtoB(0); // nothing in the file just yet |
myContainer.formType = EndianU32_NtoB(myFormType); |
myCount = sizeof(myContainer); |
myErr = FSWrite(theOutputFile, &myCount, &myContainer); |
FailIf(myErr != noErr, Exit); |
// write out the version chunk for AIFC files |
if (myFormType == AIFCID) { |
myVersion.ckID = EndianU32_NtoB(FormatVersionID); |
myVersion.ckSize = EndianS32_NtoB(sizeof(FormatVersionChunk) - sizeof(ChunkHeader)); |
myVersion.timestamp = EndianU32_NtoB(AIFCVersion1); // timestamp for this version |
myCount = sizeof(myVersion); |
myErr = FSWrite(theOutputFile, &myCount, &myVersion); |
FailIf(myErr != noErr, Exit); |
} |
// write out the CommonChunk or ExtCommonChunk if the data is compressed |
myCommon.ckID = EndianU32_NtoB(CommonID); |
myCommon.ckSize = EndianS32_NtoB(sizeof(CommonChunk) - sizeof(ChunkHeader)); // size of common chunk variables without compression info |
myCommon.numChannels = EndianS16_NtoB(theDestInfo->numChannels); |
myCommon.numSampleFrames = EndianU32_NtoB(0); // nothing in the file just yet |
myCommon.sampleSize = EndianS16_NtoB(theDestInfo->sampleSize); |
#if TARGET_CPU_68K |
myCommon.sampleRate = theDestInfo->sampleRate / 65536.0; |
#else |
{ |
long double ldRate; |
ldRate = theDestInfo->sampleRate / 65536.0; |
ldtox80(&ldRate, &myCommon.sampleRate); |
} |
#endif |
myCommon.compressionType = EndianU32_NtoB(theDestInfo->format); |
myCommon.compressionName[0] = 0; |
if (myFormType == AIFCID) { |
myErr = GetCompressionName(theDestInfo->format, myCompressionName); |
if (myErr == noErr) { |
// write out the ExtCommonChunk for compressed data, with the name |
myCount = offsetof(ExtCommonChunk, compressionName); |
myCommon.ckSize = myCount - sizeof(ChunkHeader) + myCompressionName[0] + 1; |
myCommon.ckSize = ++myCommon.ckSize & ~1; // must be word aligned |
myCommon.ckSize = EndianS32_NtoB(myCommon.ckSize); |
myErr = FSWrite(theOutputFile, &myCount, &myCommon); |
FailIf(myErr != noErr, Exit); |
myCount = myCompressionName[0] + 1; |
myCount = ++myCount & ~1; // must be word aligned |
myErr = FSWrite(theOutputFile, &myCount, &myCompressionName); |
FailIf(myErr != noErr, Exit); |
} else { |
// write out the ExtCommonChunk for compressed data, without the name |
myCommon.ckSize = EndianS32_NtoB(sizeof(ExtCommonChunk) - sizeof(ChunkHeader)); // size of common chunk variables without compression info |
myCount = offsetof(ExtCommonChunk, compressionName); |
myErr = FSWrite(theOutputFile, &myCount, &myCommon); |
FailIf(myErr != noErr, Exit); |
} |
} else { // write out the CommonChunk for non-compressed data |
myCount = sizeof(CommonChunk); |
myErr = FSWrite(theOutputFile, &myCount, &myCommon); |
FailIf(myErr != noErr, Exit); |
} |
if (theDestParams != NULL) { |
ChunkHeader myCHeader; |
myCHeader.ckID = EndianU32_NtoB(siDecompressionParams); |
myCHeader.ckSize = EndianS32_NtoB(GetHandleSize(theDestParams)); |
myCount = sizeof(myCHeader); |
myErr = FSWrite(theOutputFile, &myCount, &myCHeader); |
FailIf(myErr != noErr, Exit); |
myCount = EndianS32_BtoN(myCHeader.ckSize); |
HLock(theDestParams); |
myErr = FSWrite(theOutputFile, &myCount, *theDestParams); |
FailIf(myErr != noErr, Exit); |
} |
mySoundData.ckID = EndianU32_NtoB(SoundDataID); |
mySoundData.ckSize = EndianS32_NtoB(0); // nothing in the file just yet |
mySoundData.offset = EndianU32_NtoB(0); |
mySoundData.blockSize = EndianU32_NtoB(0); |
myCount = sizeof(mySoundData); |
myErr = FSWrite(theOutputFile, &myCount, &mySoundData); |
FailIf(myErr != noErr, Exit); |
// now go back and update Container to current file size |
myErr = SetFPos(theOutputFile, fsFromStart, 0); |
FailIf(myErr != noErr, Exit); |
myCount = sizeof(myContainer); |
myErr = FSRead(theOutputFile, &myCount, &myContainer); |
FailIf(myErr != noErr, Exit); |
myErr = GetEOF(theOutputFile, &myContainer.ckSize); |
FailIf(myErr != noErr, Exit); |
myErr = SetFPos(theOutputFile, fsFromStart, 0); |
FailIf(myErr != noErr, Exit); |
myContainer.ckSize -= sizeof(ChunkHeader); |
myContainer.ckSize = EndianS32_NtoB(myContainer.ckSize); |
myCount = sizeof(myContainer); |
myErr = FSWrite(theOutputFile, &myCount, &myContainer); |
FailIf(myErr != noErr, Exit); |
// set current file position to the end of end of the file |
// the last chunk is the SoundDataChunk, and that's where to begin writing data |
myErr = SetFPos(theOutputFile, fsFromStart, EndianS32_BtoN(myContainer.ckSize) + sizeof(ChunkHeader)); |
FailIf(myErr != noErr, Exit); |
Exit: |
return(myErr); |
} |
////////// |
// |
// AudConv_FinishFileAsAIFF |
// This assumes that all the necessary chunks have been written to the file and |
// then audio samples have been added. Finally, update the chunks to reflect the |
// samples that were added. |
// |
////////// |
OSErr AudConv_FinishFileAsAIFF (short theOutputFile, unsigned long destFramesMoved, unsigned long destBytesMoved) |
{ |
CommonChunk myCommon; |
SoundDataChunk mySoundData; |
long myCount; |
OSErr myErr = noErr; |
// find the CommonChunk and update the numSampleFrames |
myErr = AudConv_SetFPosToChunk(theOutputFile, CommonID); |
FailIf(myErr != noErr, Exit); |
myCount = sizeof(myCommon); |
myErr = FSRead(theOutputFile, &myCount, &myCommon); |
FailIf(myErr != noErr, Exit); |
myErr = SetFPos(theOutputFile, fsFromMark, -myCount); // reset to start of chunk |
FailIf(myErr != noErr, Exit); |
myCommon.numSampleFrames = EndianU32_NtoB(destFramesMoved); |
myCount = sizeof(myCommon); |
myErr = FSWrite(theOutputFile, &myCount, &myCommon); |
FailIf(myErr != noErr, Exit); |
// find the SoundDataChunk and update the ckSize |
myErr = AudConv_SetFPosToChunk(theOutputFile, SoundDataID); |
FailIf(myErr != noErr, Exit); |
myCount = sizeof(mySoundData); |
myErr = FSRead(theOutputFile, &myCount, &mySoundData); |
FailIf(myErr != noErr, Exit); |
myErr = SetFPos(theOutputFile, fsFromMark, -myCount); // reset to start of chunk |
FailIf(myErr != noErr, Exit); |
mySoundData.ckSize = EndianS32_NtoB(sizeof(SoundDataChunk) - sizeof(ChunkHeader) + destBytesMoved); |
myCount = sizeof(mySoundData); |
myErr = FSWrite(theOutputFile, &myCount, &mySoundData); |
FailIf(myErr != noErr, Exit); |
Exit: |
return(myErr); |
} |
////////// |
// |
// AudConv_PutAudioIntoTrack |
// Add the audio as a track with the necessary decompression extension. |
// |
////////// |
OSErr AudConv_PutAudioIntoTrack ( Track theTrack, |
Handle theDestAudioData, |
SoundComponentDataPtr theDestInfo, |
Handle theDestCompParamsHandle, |
CompressionInfoPtr theDestCompInfo) |
{ |
SoundDescriptionV1Handle mySampleDesc; |
Media myMedia; |
OSErr myErr = noErr; |
////////// |
// |
// create a media for the track passed in |
// |
////////// |
// set new track to be a sound track |
myMedia = NewTrackMedia(theTrack, SoundMediaType, theDestInfo->sampleRate >> 16, NULL, 0); |
myErr = GetMoviesError(); |
FailIf(myErr != noErr, Exit); |
// start a media editing session |
myErr = BeginMediaEdits(myMedia); |
FailIf(myErr != noErr, Exit); |
////////// |
// |
// create a sound sample description |
// |
////////// |
// use the SoundDescription format 1 because it adds fields for data size information |
// and is required by AddSoundDescriptionExtension if an extension is required for the compression format |
mySampleDesc = (SoundDescriptionV1Handle)NewHandleClear(sizeof(SoundDescriptionV1)); |
FailWithAction(myErr != noErr, myErr = MemError(), Exit); |
(**mySampleDesc).desc.descSize = sizeof(SoundDescriptionV1); |
(**mySampleDesc).desc.dataFormat = theDestInfo->format; |
(**mySampleDesc).desc.resvd1 = 0; |
(**mySampleDesc).desc.resvd2 = 0; |
(**mySampleDesc).desc.dataRefIndex = 1; |
(**mySampleDesc).desc.version = 1; |
(**mySampleDesc).desc.revlevel = 0; |
(**mySampleDesc).desc.vendor = 0; |
(**mySampleDesc).desc.numChannels = theDestInfo->numChannels; |
(**mySampleDesc).desc.sampleSize = theDestInfo->sampleSize; |
(**mySampleDesc).desc.compressionID = 0; |
(**mySampleDesc).desc.packetSize = 0; |
(**mySampleDesc).desc.sampleRate = theDestInfo->sampleRate; |
(**mySampleDesc).samplesPerPacket = theDestCompInfo->samplesPerPacket; |
(**mySampleDesc).bytesPerPacket = theDestCompInfo->bytesPerPacket; |
(**mySampleDesc).bytesPerFrame = theDestCompInfo->bytesPerFrame; |
(**mySampleDesc).bytesPerSample = theDestCompInfo->bytesPerSample; |
// not all compression formats have compression params, so we need to add a |
// sound description extension only for those that do |
if (theDestCompParamsHandle != NULL) { |
myErr = AddSoundDescriptionExtension((SoundDescriptionHandle)mySampleDesc, theDestCompParamsHandle, siDecompressionParams); |
FailIf(myErr != noErr, MediaErr); |
} |
////////// |
// |
// add samples to the media |
// |
////////// |
myErr = AddMediaSample( myMedia, |
theDestAudioData, |
0, |
theDestInfo->sampleCount * theDestCompInfo->bytesPerFrame, |
1, |
(SampleDescriptionHandle)mySampleDesc, |
theDestInfo->sampleCount * theDestCompInfo->samplesPerPacket, |
0, |
NULL); |
FailIf(myErr != noErr, MediaErr); |
myErr = EndMediaEdits(myMedia); |
FailIf(myErr != noErr, MediaErr); |
////////// |
// |
// insert the media into the track |
// |
////////// |
myErr = InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(myMedia), fixed1); |
FailIf(myErr != noErr, MediaErr); |
MediaErr: |
if (mySampleDesc != NULL) |
DisposeHandle((Handle)mySampleDesc); |
Exit: |
return(myErr); |
} |
////////// |
// |
// AudConv_SFGetDialogHook |
// Hook function for the get file dialog box. |
// |
////////// |
PASCAL_RTN short AudConv_SFGetDialogHook (short theItem, DialogPtr theDialog, void *theOutputAIFF) |
{ |
ControlHandle myAIFFControl; |
ControlHandle myMovieControl; |
Rect myRect; |
short myKind; |
// make sure we've got a real dialog |
if (theDialog == NULL) |
return(theItem); |
GetDialogItem(theDialog, kOutputAIFFButton, &myKind, (Handle *)&myAIFFControl, &myRect); |
GetDialogItem(theDialog, kOutputMovieButton, &myKind, (Handle *)&myMovieControl, &myRect); |
switch (theItem) { |
case sfHookFirstCall: |
if (*(Boolean *)theOutputAIFF) |
SetControlValue(myAIFFControl, kControlRadioButtonCheckedValue); |
else |
SetControlValue(myMovieControl, kControlRadioButtonUncheckedValue); |
break; |
case kOutputAIFFButton: |
SetControlValue(myAIFFControl, kControlRadioButtonCheckedValue); |
SetControlValue(myMovieControl, kControlRadioButtonUncheckedValue); |
*(Boolean *)theOutputAIFF = true; |
break; |
case kOutputMovieButton: |
SetControlValue(myAIFFControl, kControlRadioButtonUncheckedValue); |
SetControlValue(myMovieControl, kControlRadioButtonCheckedValue); |
*(Boolean *)theOutputAIFF = false; |
break; |
} |
return(theItem); |
} |
////////// |
// |
// AudConv_NavObjectFilterProc |
// Object filter function for the get file dialog box. |
// |
////////// |
PASCAL_RTN Boolean AudConv_NavObjectFilterProc (AEDesc *theItem, void *theInfo, void *theCallBackUD, NavFilterModes theFilterMode) |
{ |
#pragma unused(theCallBackUD, theFilterMode) |
NavFileOrFolderInfo *myInfo = (NavFileOrFolderInfo *)theInfo; |
if (theItem->descriptorType == typeFSS) { |
if (!myInfo->isFolder) { |
OSType myType = myInfo->fileAndFolder.fileInfo.finderInfo.fdType; |
// see whether the file type is in the list of file types that our application can open |
if ((myType == kQTFileTypeAIFF) || (myType == kQTFileTypeAIFC)) |
return(true); |
// if we got to here, it's a file we cannot open |
return(false); |
} |
} |
// if we got to here, it's a folder or non-HFS object |
return(true); |
} |
////////// |
// |
// AudConv_ConvertCToPascalString |
// Convert a C string into a Pascal string. |
// |
// The caller is responsible for disposing of the pointer returned by this function (by calling free). |
// |
////////// |
StringPtr AudConv_ConvertCToPascalString (char *theString) |
{ |
StringPtr myString = malloc(strlen(theString) + 1); |
short myIndex = 0; |
while (theString[myIndex] != '\0') { |
myString[myIndex + 1] = theString[myIndex]; |
myIndex++; |
} |
myString[0] = (unsigned char)myIndex; |
return(myString); |
} |
////////// |
// |
// AudConv_HandleNavEvent |
// A callback procedure that handles events while a Navigation Service dialog box is displayed. |
// |
////////// |
PASCAL_RTN void AudConv_HandleNavEvent (NavEventCallbackMessage theCallBackSelector, NavCBRecPtr theCallBackParms, void *theCallBackUD) |
{ |
Boolean myOutputAIFF = *(Boolean *)theCallBackUD; |
NavMenuItemSpec myMenuItemSpec; |
Str255 myFileName; |
static Str15 myMooVExt; |
static Str15 myAIFFExt; |
if (theCallBackSelector == kNavCBStart) { |
// get the filename extensions |
GetIndString(myMooVExt, rStringsResID, rMooVExtensionIndex); |
GetIndString(myAIFFExt, rStringsResID, rAIFFExtensionIndex); |
} |
if (theCallBackSelector == kNavCBPopupMenuSelect) { |
myMenuItemSpec = *(NavMenuItemSpec *)theCallBackParms->eventData.eventDataParms.param; |
myOutputAIFF = (myMenuItemSpec.menuType == kQTFileTypeAIFF); |
} |
if (*(Boolean *)theCallBackUD != myOutputAIFF) { |
// the output file type has changed, so adjust the filename extension accordingly |
short myIndex; |
short myCount; |
// get the current filename |
NavCustomControl(theCallBackParms->context, kNavCtlGetEditFileName, &myFileName); |
// find the position of the last filename separator in the current filename |
myIndex = myFileName[0]; |
while ((myFileName[myIndex] != kFileExtSeparator) && (myIndex > 0)) |
myIndex--; |
if (myIndex != 0) { |
if (myOutputAIFF) { |
for (myCount = 1; myCount <= myAIFFExt[0]; myCount++) |
myFileName[myIndex + myCount - 1] = myAIFFExt[myCount]; |
} else { |
for (myCount = 1; myCount <= myMooVExt[0]; myCount++) |
myFileName[myIndex + myCount - 1] = myMooVExt[myCount]; |
} |
myFileName[0] = myIndex + myCount - 2; |
NavCustomControl(theCallBackParms->context, kNavCtlSetEditFileName, &myFileName); |
} |
*(Boolean *)theCallBackUD = myOutputAIFF; |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14