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.
SoundConverter.c
////////// |
// |
// File: SoundConverter.c |
// |
// Contains: Sound format conversion sample code. |
// |
// Written by: Bob Aron |
// Revised by: Tim Monroe |
// |
// Copyright: © 1999 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <4> 03/20/00 rtm made changes to get things running under CarbonLib |
// <3> 07/07/99 rtm final tweaks |
// <2> 07/02/99 rtm fixed problem causing crashes on Windows |
// <1> 07/01/99 rtm first file from Bob Aron; conversion to personal coding style |
// |
// This is a simple application that demonstrates how to convert some uncompressed audio into any compression |
// format supported by QuickTime 4.0 and Sound Manager 3.4. There are two routines of interest in this file: |
// SndConv_ConvertSomeUncompressedAudio and SndConv_CreateSoundMovie. |
// |
// SndConv_ConvertSomeUncompressedAudio creates a 16-bit, mono, 44.1 kHz sine wave to use as source audio data |
// and then converts it to whatever format is desired. The output format is hard-coded at the beginning of the function |
// SndConv_DriveAudioConversion. SndConv_ConvertSomeUncompressedAudio places the converted data into a handle and then |
// calls SndConv_CreateSoundMovie to add the data to a newly-created movie. |
// |
////////// |
#include "SoundConverter.h" |
////////// |
// |
// SndConv_DriveAudioConversion |
// Create some sound data and compress it into a file. |
// |
////////// |
void SndConv_DriveAudioConversion (void) |
{ |
Handle mySourceHandle, |
myDestHandle; |
Handle myDestCompParamsHandle = NULL; |
SoundComponentData mySourceInfo, |
myDestInfo; |
unsigned long mySourceTotalFrames, |
myDestTotalFrames; |
short myResRefNum = -1; |
CompressionInfo myDestCompressionInfo; |
Movie myMovie = NULL; |
FSSpec myFile; |
Boolean myIsSelected = false; |
Boolean myIsReplacing = false; |
long myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile; |
StringPtr myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt); |
StringPtr myFileName = QTUtils_ConvertCToPascalString(kSaveSoundFileName); |
OSErr myErr = noErr; |
////////// |
// |
// allocate handles to hold the uncompressed (source) and compressed (destination) sound data |
// |
////////// |
mySourceHandle = NewHandleClear(0); |
myDestHandle = NewHandleClear(0); |
if ((mySourceHandle == NULL) || (myDestHandle == NULL)) |
goto Bail; |
// create some uncompressed, 16-bit, mono, 44.1 kHz sound data; also, fill in the mySourceInfo structure |
// and return the total number of frames, which we'll use later |
myErr = SndConv_UncompressedSineWaveToHandle(mySourceHandle, &mySourceInfo, &mySourceTotalFrames); |
FailIf(myErr != noErr, Bail); |
////////// |
// |
// convert the source data |
// |
////////// |
// fill in the myDestInfo fields with the data format that you want to convert to |
myDestInfo.flags |= kNoRealtimeProcessing; // perform in non-real time for best quality (this can also be set to zero) |
myDestInfo.format = kQDesign2Compression; // data format k16BitBigEndianFormat |
myDestInfo.numChannels = 1; // number of channels (1 for mono, 2 for stereo) |
myDestInfo.sampleSize = 16; // sample size (may be left at 16 for all formats except k8BitOffsetBinaryFormat) |
myDestInfo.sampleRate = rate44khz; // sample rate |
myDestInfo.sampleCount = 0; // leave set to 0 |
myDestInfo.buffer = NULL; // leave set to NULL |
myDestInfo.reserved = 0; // leave set to 0 |
// convert the source data |
myErr = SndConv_ConvertSomeUncompressedAudio( mySourceHandle, |
mySourceInfo, |
mySourceTotalFrames, |
myDestHandle, |
myDestInfo, |
&myDestTotalFrames, |
&myDestCompressionInfo, |
&myDestCompParamsHandle); |
FailIf(myErr != noErr, Bail); |
////////// |
// |
// create a new movie to hold the converted sound data |
// |
////////// |
// elicit a location from the user |
QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing); |
if (!myIsSelected) |
goto Bail; |
if (myIsReplacing) { |
myErr = FSpDelete(&myFile); |
if (myErr != noErr) |
goto Bail; |
} |
myErr = CreateMovieFile(&myFile, FOUR_CHAR_CODE('TVOD'), smSystemScript, myFlags, &myResRefNum, &myMovie); |
FailIf(myErr != noErr, Bail); |
// put the newly-converted data into the movie |
myErr = SndConv_CreateSoundMovie( myDestHandle, |
myResRefNum, |
myMovie, |
myDestInfo, |
&myDestCompParamsHandle, |
myDestCompressionInfo, |
myDestTotalFrames); |
FailIf(myErr != noErr, Bail); |
Bail: |
free(myPrompt); |
free(myFileName); |
if (myMovie != 0) |
DisposeMovie(myMovie); |
if (myResRefNum != -1) |
CloseMovieFile(myResRefNum); |
if (myDestHandle != NULL) |
DisposeHandle(myDestHandle); |
if (mySourceHandle != NULL) |
DisposeHandle(mySourceHandle); |
if (myDestCompParamsHandle != NULL) |
DisposeHandle(myDestCompParamsHandle); |
return; |
} |
////////// |
// |
// SndConv_ConvertSomeUncompressedAudio |
// Convert the specified sound data. |
// |
// This routine expects uncompressed, 16-bit mono data as source data. |
// |
////////// |
OSErr SndConv_ConvertSomeUncompressedAudio ( Handle theSourceHandle, |
SoundComponentData theSourceInfo, |
unsigned long theSourceTotalFrames, |
Handle theDestHandle, |
SoundComponentData theDestInfo, |
unsigned long *theDestFramesMoved, |
CompressionInfo *theDestCompInfo, |
Handle *theDestCompParams) |
{ |
SoundConverter myConverter; |
Handle myDestCompParamsHandle = NULL; |
unsigned long myNumFramesLeft, |
mySourceFrames, |
myDestFrames, |
mySourceBytes, |
myDestBytes, |
myDataOffset = 0; |
Ptr myDestPtr = NULL; |
UInt16 hasOptionsDialog = 0; |
short mySourceBytesPerFrame = 2; // 16-bit mono uncompressed data is 2 bytes per frame |
OSErr myErr = noErr; |
////////// |
// |
// open a sound converter |
// |
////////// |
myErr = SoundConverterOpen(&theSourceInfo, &theDestInfo, &myConverter); |
if (myErr != noErr) |
goto Bail; |
// see if the destination format has an options dialog and open if it does |
myErr = SoundConverterGetInfo(myConverter, siOptionsDialog, &hasOptionsDialog); |
if ((myErr == noErr) && hasOptionsDialog) { |
myErr = SoundConverterSetInfo(myConverter, siOptionsDialog, NULL); |
FailIf(myErr != noErr, Bail); |
} |
myErr = SoundConverterSetInfo(myConverter, siCompressionChannels, &theDestInfo.numChannels); |
// ignore this error, since some codecs don't use this selector (makes QDesign work) |
////////// |
// |
// get the sound converter-specific settings. |
// |
////////// |
// Not all sound converters have custom settings; in this case myDestCompParamsHandle will be unchanged. |
// we need to return these settings to the caller, as they are required by the decompressor to be able |
// to decompress the compressed audio. If this audio is stored in a QuickTime movie, these parameters |
// will be stored in a SoundDescriptionExtension of type siDecompressionParams. |
myErr = SoundConverterGetInfo(myConverter, siCompressionParams, &myDestCompParamsHandle); |
// if any compression params were passed back, send it to the sound converter now |
if (myErr == noErr) { |
HLockHi(myDestCompParamsHandle); |
myErr = SoundConverterSetInfo(myConverter, siCompressionParams, *myDestCompParamsHandle); |
HUnlock(myDestCompParamsHandle); |
FailIf(myErr != noErr, Bail); |
} else { |
// no audio atom list to deal with, so set to NULL |
myDestCompParamsHandle = NULL; |
} |
// get sound converter buffer size info |
myErr = SoundConverterGetBufferSizes(myConverter, kMaxBufferSize, &mySourceFrames, &mySourceBytes, &myDestBytes); |
FailIf(myErr != noErr, Bail); |
// create destination data buffer |
myDestPtr = NewPtrClear(myDestBytes); |
myErr = MemError(); |
FailIf(myErr != noErr, Bail); |
////////// |
// |
// convert the sound to the desired output format |
// |
////////// |
myErr = SoundConverterBeginConversion(myConverter); |
FailIf(myErr != noErr, Bail); |
// get info about destination compression |
// We need to return this information to the caller as they are required |
// in order to put this compressed audio into a QuickTime movie. This information |
// will go into the Version 1 Sound Description Handle. |
myErr = SoundConverterGetInfo(myConverter, siCompressionFactor, theDestCompInfo); |
if (myErr != noErr) { |
myErr = GetCompressionInfo(fixedCompression, theDestInfo.format, theDestInfo.numChannels, theDestInfo.sampleSize, theDestCompInfo); |
FailIf(myErr != noErr, Bail); |
} |
// myBytesPerFrame is not filled in by GetInfo, so we set it here |
theDestCompInfo->bytesPerFrame = theDestCompInfo->bytesPerPacket * theDestInfo.numChannels; |
// initialize destination total frame count to zero |
*theDestFramesMoved = 0; |
myNumFramesLeft = theSourceTotalFrames; |
HLockHi(theSourceHandle); |
// loop through buffers of size mySourceFrames |
while (myNumFramesLeft > 0) { |
// if there are fewer frames remaining than our source buffer size, |
// we're near the end of our data, so get what's remaining |
if (myNumFramesLeft < mySourceFrames) |
mySourceFrames = myNumFramesLeft; |
myErr = SoundConverterConvertBuffer(myConverter, *theSourceHandle + myDataOffset, mySourceFrames, myDestPtr, &myDestFrames, &myDestBytes); |
FailIf(myErr != noErr, Bail); |
// place the converted data into a handle |
myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes); |
FailIf(myErr != noErr, Bail); |
// move offset to appropriate place in source data |
myDataOffset += mySourceFrames * mySourceBytesPerFrame; |
// keep track of total frames returned by converter |
*theDestFramesMoved += myDestFrames; |
myNumFramesLeft -= mySourceFrames; |
} |
// end the conversion, and see if we get back a few more bytes of data |
myErr = SoundConverterEndConversion(myConverter, myDestPtr, &myDestFrames, &myDestBytes); |
FailIf(myErr != noErr, Bail); |
HUnlock(theSourceHandle); |
// place any leftover converted data into the handle |
myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes); |
FailIf(myErr != noErr, Bail); |
*theDestFramesMoved += myDestFrames; |
Bail: |
*theDestCompParams = myDestCompParamsHandle; |
if (myDestPtr != NULL) |
DisposePtr(myDestPtr); |
if (myConverter != NULL) |
SoundConverterClose(myConverter); |
return(myErr); |
} |
////////// |
// |
// SndConv_CreateSoundMovie |
// Create a sound movie from the specified handle of audio data. |
// |
////////// |
OSErr SndConv_CreateSoundMovie ( Handle theDestAudioData, |
short theMovieRefNum, |
Movie theMovie, |
SoundComponentData theDestInfo, |
Handle *theDestCompParams, |
CompressionInfo theDestCompInfo, |
unsigned long theDestFrameCount) |
{ |
Track myTrack = NULL; |
Media myMedia = NULL; |
TimeScale myTimeScale; |
SoundDescriptionV1Handle mySampleDesc = NULL; |
unsigned long myLengthInSamples; |
OSErr myErr = noErr; |
////////// |
// |
// create the movie track and media |
// |
////////// |
myTrack = NewMovieTrack(theMovie, 0, 0, kFullVolume); |
myErr = GetMoviesError(); |
FailIf(myErr != noErr, Bail); |
myTimeScale = (theDestInfo.sampleRate >> 16); |
// set new track to be a sound track |
myMedia = NewTrackMedia(myTrack, SoundMediaType, myTimeScale, NULL, 0); |
myErr = GetMoviesError(); |
FailIf(myErr != noErr, Bail); |
// start a media editing session |
myErr = BeginMediaEdits(myMedia); |
FailIf(myErr != noErr, Bail); |
////////// |
// |
// 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)); |
myErr = MemError(); |
FailIf(myErr != noErr, Bail); |
// fill in the fields of the sample description |
(*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 only need to add a |
// sound description extension for those that do |
if (*theDestCompParams != NULL) |
AddSoundDescriptionExtension((SoundDescriptionHandle)mySampleDesc, *theDestCompParams, siDecompressionParams); |
////////// |
// |
// add samples to the media |
// |
////////// |
myLengthInSamples = theDestFrameCount * theDestCompInfo.samplesPerPacket; |
myErr = AddMediaSample( myMedia, |
theDestAudioData, |
0, |
theDestFrameCount * theDestCompInfo.bytesPerFrame, |
1, |
(SampleDescriptionHandle)mySampleDesc, |
myLengthInSamples, |
0, |
NULL); |
FailIf(myErr != noErr, Bail); |
myErr = EndMediaEdits(myMedia); |
FailIf(myErr != noErr, Bail); |
////////// |
// |
// add the media to the track |
// |
////////// |
myErr = InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), FixRatio(1, 1)); |
FailIf(myErr != noErr, Bail); |
myErr = AddMovieResource(theMovie, theMovieRefNum, NULL, NULL); |
FailIf(myErr != noErr, Bail); |
Bail: |
if (mySampleDesc != NULL) |
DisposeHandle((Handle)mySampleDesc); |
return(myErr); |
} |
////////// |
// |
// SndConv_UncompressedSineWaveToHandle |
// Create some 16-bit, monophonic, 44.1kHz data and stick it into a handle. |
// |
////////// |
OSErr SndConv_UncompressedSineWaveToHandle (Handle theData, SoundComponentData *theCompInfo, unsigned long *theTotalFrames) |
{ |
long myIndex, myByteCounter; |
double_t mySinResult, myRate; |
long myPlotMe; |
float myPi = 3.14159; |
Byte *myBufferPtr = NULL; |
Byte *myStartOfBufferPtr = NULL; |
unsigned long myBytesCount = 0; |
long myFramesInBuffer = 10240; |
long myHz = kConcertA, |
mySeconds = 3; |
short myBytesPerFrame; |
OSErr myErr = noErr; |
*theTotalFrames = 0; |
theCompInfo->flags = 0; |
theCompInfo->format = k16BitBigEndianFormat; |
theCompInfo->numChannels = 1; |
theCompInfo->sampleSize = 16; |
theCompInfo->sampleRate = rate44khz; |
theCompInfo->sampleCount = mySeconds * (theCompInfo->sampleRate >> 16); |
theCompInfo->buffer = NULL; |
theCompInfo->reserved = 0; |
myBytesPerFrame = theCompInfo->sampleSize / 8; |
myBufferPtr = (Byte *)NewPtrClear(myFramesInBuffer * myBytesPerFrame); |
FailWithAction(myBufferPtr == NULL, myErr = memFullErr, Bail); |
myStartOfBufferPtr = myBufferPtr; |
myRate = theCompInfo->sampleRate / 65536.0; |
////////// |
// 2 ¹ hz |
// (sin(i * ------)) * samplesize |
// rate |
////////// |
for (myIndex = 0; myIndex < theCompInfo->sampleCount; myIndex++) { |
mySinResult = sin(myIndex * ((2 * myPi * myHz) / myRate)); |
myPlotMe = mySinResult * ((1 << (theCompInfo->sampleSize - 1)) - 1); |
for (myByteCounter = 1; myByteCounter <= (theCompInfo->sampleSize / 8); myByteCounter++, myBytesCount++, myBufferPtr++) { |
*myBufferPtr = myPlotMe >> (theCompInfo->sampleSize - (myByteCounter * 8)); |
} |
if (myBytesCount == myFramesInBuffer * myBytesPerFrame) { |
myErr = PtrAndHand(myStartOfBufferPtr, theData, myBytesCount); |
FailIf(myErr != noErr, Bail); |
*theTotalFrames += myBytesCount / myBytesPerFrame; |
myBytesCount = 0; // reset bytes count |
myBufferPtr = myStartOfBufferPtr; // reset myBufferPtr |
} |
} |
myErr = PtrAndHand(myStartOfBufferPtr, theData, myBytesCount); |
FailIf(myErr != noErr, Bail); |
*theTotalFrames += myBytesCount / myBytesPerFrame; |
Bail: |
if (myBufferPtr != NULL) |
DisposePtr((Ptr)myBufferPtr); |
return(myErr); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14