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.
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