-
Notifications
You must be signed in to change notification settings - Fork 3
/
listing3.html
executable file
·668 lines (554 loc) · 29 KB
/
listing3.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<!-- BEGIN META TAG INFO -->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="home" href="http://developer.apple.com/">
<link rel="find" href="http://developer.apple.com/search/">
<link rel="stylesheet" type="text/css" href="../../documentation/css/adcstyle.css" title="fonts">
<script language="JavaScript" src="../../documentation/js/adc.js" type="text/javascript"></script>
<!-- END META TAG INFO -->
<!-- BEGIN TITLE -->
<title>SoundPlayer - /PlaySoundFillBuffer.c</title>
<!-- END TITLE -->
<script language="JavaScript">
function JumpToNewPage() {
window.location=document.scpopupmenu.gotop.value;
return true;
}
</script>
</head>
<!-- BEGIN BODY OPEN -->
<body>
<!--END BODY OPEN -->
<!-- START CENTER OPEN -->
<center>
<!-- END CENTER OPEN -->
<!-- BEGIN LOGO AND SEARCH -->
<!--#include virtual="/includes/adcnavbar"-->
<!-- END LOGO AND SEARCH -->
<!-- START BREADCRUMB -->
<div id="breadcrumb">
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr>
<td scope="row"><img width="340" height="10" src="images/1dot.gif" alt=""></td>
<td><img width="340" height="10" src="images/1dot.gif" alt=""></td>
</tr>
<tr valign="middle">
<td align="left" colspan="2">
<a href="http://developer.apple.com/">ADC Home</a> > <a href="../../referencelibrary/index.html">Reference Library</a> > <a href="../../samplecode/index.html">Sample Code</a> > <a href="../../samplecode/QuickTime/index.html">QuickTime</a> > <a href="../../samplecode/QuickTime/idxMusicAudio-date.html">Audio</a> > <A HREF="javascript:location.replace('index.html');">SoundPlayer</A> >
</td>
</tr>
<tr>
<td colspan="2" scope="row"><img width="680" height="35" src="images/1dot.gif" alt=""></td>
</tr>
</table>
</div>
<!-- END BREADCRUMB -->
<!-- START MAIN CONTENT -->
<!-- START TITLE GRAPHIC AND INTRO-->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr align="left" valign="top">
<td><h1><div id="pagehead">SoundPlayer</div></h1></td>
</tr>
</table>
<!-- END TITLE GRAPHIC AND INTRO -->
<!-- START WIDE COLUMN -->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr align="left" valign="top">
<td id="scdetails">
<h2>/PlaySoundFillBuffer.c</h2>
<form name="scpopupmenu" onSubmit="return false;" method=post>
<p><strong>View Source Code:</strong>
<select name="gotop" onChange="JumpToNewPage();" style="width:340px"><option selected value="ingnore">Select File</option>
<option value="listing1.html">/AtomInfo.h</option>
<option value="listing2.html">/Main.c</option>
<option value="listing3.html">/PlaySoundFillBuffer.c</option>
<option value="listing4.html">/SoundPlayer.h</option></select>
</p>
</form>
<p><strong><a href="SoundPlayer.zip">Download Sample</a></strong> (“SoundPlayer.zip”, 406.5K)<BR>
<strong><a href="SoundPlayer.dmg">Download Sample</a></strong> (“SoundPlayer.dmg”, 505.7K)</p>
<!--
<p><strong><a href="#">Download Sample</a></strong> (“filename.sit”, 500K)</p>
-->
</td>
</tr>
<tr>
<td scope="row"><img width="680" height="10" src="images/1dot.gif" alt=""><br>
<img height="1" width="680" src="images/1dot_919699.gif" alt=""><br>
<img width="680" height="20" src="images/1dot.gif" alt=""></td>
</tr>
<tr>
<td scope="row">
<!--googleon: index -->
<pre class="sourcecodebox">/* File: PlaySoundFillBuffer.c Description: This sample demonstates how to use the Sound Description Extention atom information with the SoundConverterFillBuffer API to play VBR and Non-VBR audio. Originally available as part of the MP3 Player sample this file has been updated to support MPEG4 audio added in QT 6.0 NOTE: This Sample Requires QuickTime 6.0 or greater. Author: mc, era Version 1.5 Copyright: © Copyright 2000-2002 Apple Computer, Inc. All rights reserved. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Change History (most recent first): <3> 7/07/02 era added current (QT 6.0) support for VBR, fixed leaky bits, removed the bufferCmd / callBackCmd pair in favour of the scheduledSoundCmd (much better for QT Windows), added MPSemaphors to manage play buffers sync (much better for X) <2> 10/31/00 era modified to use SoundConverterFillBuffer APIs <1> 7/26/00 mc initial release as PlaySound.c*/#include "SoundPlayer.h"// globalsMPSemaphoreID gBufferDoneSignalID;// * ----------------------------// MySoundCallBackFunction//// used to signal when a buffer is done playingstatic pascal void MySoundCallBackFunction(SndChannelPtr theChannel, SndCommand *theCmd){#pragma unused(theChannel, theCmd) MPSignalSemaphore(gBufferDoneSignalID);}// * ----------------------------// SoundConverterFillBufferDataProc//// the callback routine that provides the source data for conversion - it provides data by setting// outData to a pointer to a properly filled out ExtendedSoundComponentData structurestatic pascal Boolean SoundConverterFillBufferDataProc(SoundComponentDataPtr *outData, void *inRefCon){ SCFillBufferDataPtr pFillData = (SCFillBufferDataPtr)inRefCon; OSErr err; // if after getting the last chunk of data the total time is over the duration, we're done if (pFillData->getMediaAtThisTime >= pFillData->sourceDuration) { pFillData->isThereMoreSource = false; pFillData->compData.desc.buffer = NULL; pFillData->compData.desc.sampleCount = 0; pFillData->compData.bufferSize = 0; pFillData->compData.commonFrameSize = 0; } if (pFillData->isThereMoreSource) { long sourceBytesReturned; long numberOfSamples; TimeValue sourceReturnedTime, durationPerSample; // in calling GetMediaSample, we'll get a buffer that consists of equal sized frames - the // degenerate case is only 1 frame -- for non-self-framed vbr formats (like AAC in QT 6.0) // we need to provide some more framing information - either the frameCount, frameSizeArray pair or // commonFrameSize field must be valid -- because we always get equal sized frames, we use // commonFrameSize and set the kExtendedSoundCommonFrameSizeValid flag -- if there is // only 1 frame then (common frame size == media sample size), if there are multiple frames, // then (common frame size == media sample size / number of frames). err = GetMediaSample(pFillData->sourceMedia, // specifies the media for this operation pFillData->hSource, // function returns the sample data into this handle pFillData->maxBufferSize, // maximum number of bytes of sample data to be returned &sourceBytesReturned, // the number of bytes of sample data returned pFillData->getMediaAtThisTime, // starting time of the sample to be retrieved (must be in Media's TimeScale) &sourceReturnedTime, // indicates the actual time of the returned sample data &durationPerSample, // duration of each sample in the media NULL, // sample description corresponding to the returned sample data (NULL to ignore) NULL, // index value to the sample description that corresponds to the returned sample data (NULL to ignore) 0, // maximum number of samples to be returned (0 to use a value that is appropriate for the media) &numberOfSamples, // number of samples it actually returned NULL); // flags that describe the sample (NULL to ignore) if ((noErr != err) || (sourceBytesReturned == 0)) { pFillData->isThereMoreSource = false; pFillData->compData.desc.buffer = NULL; pFillData->compData.desc.sampleCount = 0; pFillData->compData.bufferSize = 0; pFillData->compData.commonFrameSize = 0; if ((err != noErr) && (sourceBytesReturned > 0)) DebugStr("\pGetMediaSample - Failed in FillBufferDataProc"); } pFillData->getMediaAtThisTime = sourceReturnedTime + (durationPerSample * numberOfSamples); // we've specified kExtendedSoundSampleCountNotValid and the 'studly' Sound Converter will // take care of sampleCount for us, so while this is not required we fill out all the information // we have to simply demonstrate how this would be done // sampleCount is the number of PCM samples pFillData->compData.desc.sampleCount = numberOfSamples * durationPerSample; // kExtendedSoundBufferSizeValid was specified - make sure this field is filled in correctly pFillData->compData.bufferSize = sourceBytesReturned; // for VBR audio we specified the kExtendedSoundCommonFrameSizeValid flag - make sure this field is filled in correctly if (pFillData->isSourceVBR) pFillData->compData.commonFrameSize = sourceBytesReturned / numberOfSamples; } // set outData to a properly filled out ExtendedSoundComponentData struct *outData = (SoundComponentDataPtr)&pFillData->compData; return (pFillData->isThereMoreSource);}// * ----------------------------// GetMovieMedia//// returns a Media identifier - if the file is a System 7 Sound a non-in-place import is done and// a handle to the data is passed back to the caller who is responsible for disposing of itstatic OSErr GetMovieMedia(const FSSpec *inFile, Movie *outMovie, Media *outMedia, Handle *outHandle){ Movie theMovie = 0; Track theTrack; FInfo fndrInfo; OSErr err = noErr; err = FSpGetFInfo(inFile, &fndrInfo); BailErr(err); if (kQTFileTypeSystemSevenSound == fndrInfo.fdType) { // if this is an 'sfil' handle it appropriately // QuickTime can't import these files in place, but that's ok, // we just need a new place to put the data MovieImportComponent theImporter = 0; Handle hDataRef = NULL; // create a new movie theMovie = NewMovie(newMovieActive); // allocate the data handle and create a data reference for this handle // the caller is responsible for disposing of the data handle once done with the sound *outHandle = NewHandle(0); err = PtrToHand(outHandle, &hDataRef, sizeof(Handle)); if (noErr == err) { SetMovieDefaultDataRef(theMovie, hDataRef, HandleDataHandlerSubType); OpenADefaultComponent(MovieImportType, kQTFileTypeSystemSevenSound, &theImporter); if (theImporter) { Track ignoreTrack; TimeValue ignoreDuration; long ignoreFlags; err = MovieImportFile(theImporter, inFile, theMovie, 0, &ignoreTrack, 0, &ignoreDuration, 0, &ignoreFlags); CloseComponent(theImporter); } } else { if (*outHandle) { DisposeHandle(*outHandle); *outHandle = NULL; } } if (hDataRef) DisposeHandle(hDataRef); BailErr(err); } else { // import in place short theRefNum; short theResID = 0; // we want the first movie Boolean wasChanged; // open the movie file err = OpenMovieFile(inFile, &theRefNum, fsRdPerm); BailErr(err); // instantiate the movie err = NewMovieFromFile(&theMovie, theRefNum, &theResID, NULL, newMovieActive, &wasChanged); CloseMovieFile(theRefNum); BailErr(err); } // get the first sound track theTrack = GetMovieIndTrackType(theMovie, 1, SoundMediaType, movieTrackMediaType); if (NULL == theTrack ) BailErr(invalidTrack); // get and return the sound track media *outMedia = GetTrackMedia(theTrack); if (NULL == *outMedia) err = invalidMedia; *outMovie = theMovie; bail: return err;}// * ----------------------------// MyGetSoundDescriptionExtension//// this function will extract the information needed to decompress the sound file, this includes // retrieving the sample description, the decompression atom and setting up the sound headerstatic OSErr MyGetSoundDescriptionExtension(Media inMedia, AudioFormatAtomPtr *outAudioAtom, CmpSoundHeaderPtr outSoundInfo){ OSErr err = noErr; Size size; Handle extension; SoundDescriptionHandle hSoundDescription = (SoundDescriptionHandle)NewHandle(0); // get the description of the sample data GetMediaSampleDescription(inMedia, 1, (SampleDescriptionHandle)hSoundDescription); BailErr(GetMoviesError()); extension = NewHandle(0); // get the "magic" decompression atom // This extension to the SoundDescription information stores data specific to a given audio decompressor. // Some audio decompression algorithms require a set of out-of-stream values to configure the decompressor // which are stored in a siDecompressionParams atom. The contents of the siDecompressionParams atom are dependent // on the audio decompressor. err = GetSoundDescriptionExtension(hSoundDescription, &extension, siDecompressionParams); if (noErr == err) { size = GetHandleSize(extension); HLock(extension); *outAudioAtom = (AudioFormatAtomPtr)NewPtr(size); err = MemError(); // copy the atom data to our buffer... BlockMoveData(*extension, *outAudioAtom, size); HUnlock(extension); } else { // if it doesn't have an atom, that's ok err = noErr; } // set up our sound header outSoundInfo->format = (*hSoundDescription)->dataFormat; outSoundInfo->numChannels = (*hSoundDescription)->numChannels; outSoundInfo->sampleSize = (*hSoundDescription)->sampleSize; outSoundInfo->sampleRate = (*hSoundDescription)->sampleRate; outSoundInfo->compressionID = (*hSoundDescription)->compressionID; bail: if (extension) DisposeHandle(extension); if (hSoundDescription) DisposeHandle((Handle)hSoundDescription); return err;}// * ----------------------------// PlaySound//// this function does the actual work of playing the sound file, it sets up the sound converter environment, allocates play buffers,// creates the sound channel and sends the appropriate sound commands to play the converted sound dataOSErr PlaySound(const FSSpec *inFileToPlay){ Movie theMovie = 0; AudioCompressionAtomPtr theDecompressionAtom = NULL; CmpSoundHeader theCmpSoundInfo; SoundComponentData theInputFormat, theOutputFormat; SoundConverter mySoundConverter = NULL; ScheduledSoundHeader mySndHeader0, mySndHeader1; SCFillBufferData scFillBufferData = {NULL}; Media theSoundMedia = NULL; Ptr pDecomBuffer0 = NULL, pDecomBuffer1 = NULL; Handle hSys7SoundData = NULL; Boolean isSoundDone = false; OSErr err = noErr; err = GetMovieMedia(inFileToPlay, &theMovie, &theSoundMedia, &hSys7SoundData); BailErr(err); err = MyGetSoundDescriptionExtension(theSoundMedia, (AudioFormatAtomPtr *)&theDecompressionAtom, &theCmpSoundInfo); if (noErr == err) { // setup input/output format for sound converter theInputFormat.flags = 0; theInputFormat.format = theCmpSoundInfo.format; theInputFormat.numChannels = theCmpSoundInfo.numChannels; theInputFormat.sampleSize = theCmpSoundInfo.sampleSize; theInputFormat.sampleRate = theCmpSoundInfo. sampleRate; theInputFormat.sampleCount = 0; theInputFormat.buffer = NULL; theInputFormat.reserved = 0; theOutputFormat.flags = 0; theOutputFormat.format = kSoundNotCompressed; theOutputFormat.numChannels = theInputFormat.numChannels; theOutputFormat.sampleSize = theInputFormat.sampleSize; theOutputFormat.sampleRate = theInputFormat.sampleRate; theOutputFormat.sampleCount = 0; theOutputFormat.buffer = NULL; theOutputFormat.reserved = 0; // variableCompression means we're going to use the commonFrameSize field and the kExtendedSoundCommonFrameSizeValid flag scFillBufferData.isSourceVBR = (theCmpSoundInfo.compressionID == variableCompression); err = SoundConverterOpen(&theInputFormat, &theOutputFormat, &mySoundConverter); BailErr(err); // this isn't crucial or even required for decompression only, but it does tell // the sound converter that we're cool with VBR audio SoundConverterSetInfo(mySoundConverter, siClientAcceptsVBR, Ptr(true)); // set up the sound converters decompresson 'environment' by passing in the 'magic' decompression atom err = SoundConverterSetInfo(mySoundConverter, siDecompressionParams, theDecompressionAtom); if (theDecompressionAtom) DisposePtr((Ptr)theDecompressionAtom); if (siUnknownInfoType == err) { // clear this error, the decompressor didn't // need the decompression atom and that's OK err = noErr; } else BailErr(err); // the input buffer has to be large enough so GetMediaSample isn't going to fail, your mileage may vary UInt32 inputBytes = ((1000 + (theInputFormat.sampleRate >> 16)) * theInputFormat.numChannels) * 4, outputBytes = kMaxOutputBuffer; // kMaxOutputBuffer is 64k which should be ok, make it larger if you like // allocate play buffers pDecomBuffer0 = NewPtr(outputBytes); BailErr(MemError()); pDecomBuffer1 = NewPtr(outputBytes); BailErr(MemError()); err = SoundConverterBeginConversion(mySoundConverter); BailErr(err); // setup first header mySndHeader0.u.cmpHeader.samplePtr = pDecomBuffer0; mySndHeader0.u.cmpHeader.numChannels = theOutputFormat.numChannels; mySndHeader0.u.cmpHeader.sampleRate = theOutputFormat.sampleRate; mySndHeader0.u.cmpHeader.loopStart = 0; mySndHeader0.u.cmpHeader.loopEnd = 0; mySndHeader0.u.cmpHeader.encode = cmpSH; // compressed sound header encode value mySndHeader0.u.cmpHeader.baseFrequency = kMiddleC; mySndHeader0.u.cmpHeader.numFrames = 0; // numFrames will equal outputFrames as we're converting // mySndHeader0.u.cmpHeader.AIFFSampleRate; // this is not used mySndHeader0.u.cmpHeader.markerChunk = NULL; mySndHeader0.u.cmpHeader.format = theOutputFormat.format; mySndHeader0.u.cmpHeader.futureUse2 = 0; mySndHeader0.u.cmpHeader.stateVars = NULL; mySndHeader0.u.cmpHeader.leftOverSamples = NULL; mySndHeader0.u.cmpHeader.compressionID = fixedCompression; // compression ID for fixed-sized compression, even uncompressed sounds use fixedCompression mySndHeader0.u.cmpHeader.packetSize = 0; // the Sound Manager will figure this out for us mySndHeader0.u.cmpHeader.snthID = 0; mySndHeader0.u.cmpHeader.sampleSize = theOutputFormat.sampleSize; mySndHeader0.u.cmpHeader.sampleArea[0] = 0; // no samples here because we use samplePtr to point to our buffer instead mySndHeader0.flags = kScheduledSoundDoCallBack; mySndHeader0.callBackParam1 = 0; // we don't pass anything to the callback routine mySndHeader0.callBackParam2 = 0; // setup second header, only the buffer ptr is different BlockMoveData(&mySndHeader0, &mySndHeader1, sizeof(mySndHeader0)); mySndHeader1.u.cmpHeader.samplePtr = pDecomBuffer1; // fill in struct that gets passed to SoundConverterFillBufferDataProc via the refcon // this includes the ExtendedSoundComponentData record scFillBufferData.sourceMedia = theSoundMedia; scFillBufferData.getMediaAtThisTime = 0; scFillBufferData.sourceDuration = GetMediaDuration(theSoundMedia); scFillBufferData.isThereMoreSource = true; scFillBufferData.maxBufferSize = inputBytes; scFillBufferData.hSource = NewHandle((long)scFillBufferData.maxBufferSize); // allocate source media buffer BailErr(MemError()); MoveHHi((Handle)scFillBufferData.hSource); HLock((Handle)scFillBufferData.hSource); scFillBufferData.compData.desc = theInputFormat; scFillBufferData.compData.desc.buffer = (BytePtr)*scFillBufferData.hSource; scFillBufferData.compData.desc.flags = kExtendedSoundData; scFillBufferData.compData.recordSize = sizeof(ExtendedSoundComponentData); scFillBufferData.compData.extendedFlags = kExtendedSoundSampleCountNotValid | kExtendedSoundBufferSizeValid; if (scFillBufferData.isSourceVBR) scFillBufferData.compData.extendedFlags |= kExtendedSoundCommonFrameSizeValid; scFillBufferData.compData.bufferSize = 0; // filled in during FillBuffer callback // create a semaphore used to sync the filling of the play buffers, setup the callback, // create the sound channel and play the sound -- we will continue to convert the sound data // into a free (non playing) buffer -- because we want to fill up both buffers immediately // without waiting for the first buffer to complete, a binary semaphore is created with the maximumValue // and initialValue set to 1, when we call MPWaitOnSemaphore if the value of the semaphore is greater // than zero, the value is decremented and the function returns immediately which is what we want the // very first time through - after that, MPWaitOnSemaphore will block waiting for our callback // to fire where we signal the semaphore, this indicates a free buffer and the process can continue MPCreateBinarySemaphore(&gBufferDoneSignalID); SndCallBackUPP theSoundCallBackUPP = NewSndCallBackUPP(MySoundCallBackFunction); SndChannelPtr pSoundChannel = NULL; err = SndNewChannel(&pSoundChannel, sampledSynth, 0, theSoundCallBackUPP); if (err == noErr) { SndCommand thePlayCmd0, thePlayCmd1; SndCommand *pPlayCmd; Ptr pDecomBuffer = NULL; CmpSoundHeaderPtr pSndHeader = NULL; UInt32 outputFrames, actualOutputBytes, outputFlags; eBufferNumber whichBuffer = kFirstBuffer; SoundConverterFillBufferDataUPP theFillBufferDataUPP = NewSoundConverterFillBufferDataUPP(SoundConverterFillBufferDataProc); thePlayCmd0.cmd = scheduledSoundCmd; thePlayCmd0.param1 = 0; // not used, but clear it out anyway just to be safe thePlayCmd0.param2 = (long)&mySndHeader0; thePlayCmd1.cmd = scheduledSoundCmd; thePlayCmd1.param1 = 0; // not used, but clear it out anyway just to be safe thePlayCmd1.param2 = (long)&mySndHeader1; if (noErr == err) { while (!isSoundDone && !Button()) { if (kFirstBuffer == whichBuffer) { pPlayCmd = &thePlayCmd0; pDecomBuffer = pDecomBuffer0; pSndHeader = &mySndHeader0.u.cmpHeader; whichBuffer = kSecondBuffer; } else { pPlayCmd = &thePlayCmd1; pDecomBuffer = pDecomBuffer1; pSndHeader = &mySndHeader1.u.cmpHeader; whichBuffer = kFirstBuffer; } err = SoundConverterFillBuffer(mySoundConverter, // a sound converter theFillBufferDataUPP, // the callback UPP &scFillBufferData, // refCon passed to FillDataProc pDecomBuffer, // the decompressed data 'play' buffer outputBytes, // size of the 'play' buffer &actualOutputBytes, // number of output bytes &outputFrames, // number of output frames &outputFlags); // fillbuffer retured advisor flags if (err) break; if((outputFlags & kSoundConverterHasLeftOverData) == false) { isSoundDone = true; } pSndHeader->numFrames = outputFrames; SndDoCommand(pSoundChannel, pPlayCmd, true); // play the next buffer MPWaitOnSemaphore(gBufferDoneSignalID, kDurationForever); // block waiting for a free buffer } // while } SoundConverterEndConversion(mySoundConverter, pDecomBuffer, &outputFrames, &outputBytes); if (noErr == err && outputFrames) { pSndHeader->numFrames = outputFrames; SndDoCommand(pSoundChannel, pPlayCmd, true); // play the last buffer. } if (theFillBufferDataUPP) DisposeSoundConverterFillBufferDataUPP(theFillBufferDataUPP); } if (pSoundChannel) err = SndDisposeChannel(pSoundChannel, false); // wait until sounds stops playing before disposing of channel if (theSoundCallBackUPP) DisposeSndCallBackUPP(theSoundCallBackUPP); MPDeleteSemaphore(gBufferDoneSignalID); } bail: if (scFillBufferData.hSource) DisposeHandle(scFillBufferData.hSource); if (mySoundConverter) SoundConverterClose(mySoundConverter); if (pDecomBuffer0) DisposePtr(pDecomBuffer0); if (pDecomBuffer1) DisposePtr(pDecomBuffer1); if (theMovie) DisposeMovie(theMovie); if (hSys7SoundData) DisposeHandle(hSys7SoundData); return err;}</pre>
<!--googleoff: index -->
</td>
</tr>
</table>
<!-- END WIDE COLUMN -->
<!-- END MAIN CONTENT -->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr>
<td><div style="width: 100%; height: 1px; background-color: #919699; margin-top: 5px; margin-bottom: 15px"></div></td>
</tr>
<tr>
<td align="center"><br/>
<table border="0" cellpadding="0" cellspacing="0" class="graybox">
<tr>
<th>Did this document help you?</th>
</tr>
<tr>
<td>
<div style="margin-bottom: 8px"><a href="http://developer.apple.com/feedback/?v=1&url=/samplecode/SoundPlayer/listing3.html%3Fid%3DDTS10000922-1.0&media=dvd" target=_new>Yes</a>: Tell us what works for you.</div>
<div style="margin-bottom: 8px"><a href="http://developer.apple.com/feedback/?v=2&url=/samplecode/SoundPlayer/listing3.html%3Fid%3DDTS10000922-1.0&media=dvd" target=_new>It’s good, but:</a> Report typos, inaccuracies, and so forth.</div>
<div><a href="http://developer.apple.com/feedback/?v=3&url=/samplecode/SoundPlayer/listing3.html%3Fid%3DDTS10000922-1.0&media=dvd" target=_new>It wasn’t helpful</a>: Tell us what would have helped.</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- START BOTTOM APPLE NAVIGATION -->
<!--#include virtual="/includes/footer"-->
<!-- END BOTTOM APPLE NAVIGATION -->
<!-- START CENTER CLOSE -->
</center>
<!-- END CENTER CLOSE -->
</body>
</html>