main.cpp
/* |
File: main.cpp |
Abstract: n/a |
Version: 1.0.1 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
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 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. |
Copyright (C) 2012 Apple Inc. All Rights Reserved. |
*/ |
#include <CoreFoundation/CoreFoundation.h> |
#include <CoreServices/CoreServices.h> |
#include <CoreAudio/CoreAudioTypes.h> |
#include <AudioToolbox/AudioToolbox.h> |
#include <CoreMIDI/CoreMIDI.h> |
#include <pthread.h> |
#include <unistd.h> |
#include <set> |
#include "AUOutputBL.h" |
#include "CAStreamBasicDescription.h" |
// file handling utils |
#include "CAAudioFileFormats.h" |
#include "CAFilePathUtils.h" |
#include "CAHostTimeBase.h" |
static OSStatus LoadSMF(const char *filename, MusicSequence& sequence, MusicSequenceLoadFlags loadFlags); |
static OSStatus GetSynthFromGraph (AUGraph & inGraph, AudioUnit &outSynth); |
static OSStatus SetUpGraph (AUGraph &inGraph, UInt32 numFrames, Float64 &outSampleRate, bool isOffline); |
static void WriteOutputFile (const char* outputFilePath, |
OSType dataFormat, |
Float64 srate, |
MusicTimeStamp sequenceLength, |
bool shouldPrint, |
AUGraph inGraph, |
UInt32 numFrames, |
MusicPlayer player); |
static void PlayLoop (MusicPlayer &player, AUGraph &graph, MusicTimeStamp sequenceLength, bool shouldPrint, bool waitAtEnd); |
#define BANK_CMD "[-b /Path/To/Sound/Bank.dls]\n\t" |
#define SMF_CHAN_CMD "[-c] Will Parse MIDI file into channels\n\t" |
#define DISK_STREAM "[-d] Turns disk streaming on\n\t" |
#define MIDI_CMD "[-e] Use a MIDI Endpoint\n\t" |
#define FILE_CMD "[-f /Path/To/File.<EXT FOR FORMAT> 'data' srate] Create a stereo file where\n\t" |
#define FILE_CMD_1 "\t\t 'data' is the data format (lpcm or a compressed type, like 'aac ')\n\t" |
#define FILE_CMD_2 "\t\t srate is the sample rate\n\t" |
#define NUM_FRAMES_CMD "[-i io Sample Size] default is 512\n\t" |
#define NO_PRINT_CMD "[-n] Don't print\n\t" |
#define PLAY_CMD "[-p] Play the Sequence\n\t" |
#define START_TIME_CMD "[-s startTime-Beats]\n\t" |
#define TRACK_CMD "[-t trackIndex] Play specified track(s), e.g. -t 1 -t 2...(this is a one based index)\n\t" |
#define WAIT_CMD "[-w] Play for 10 seconds, then dispose all objects and wait at end\n\t" |
#define SRC_FILE_CMD "/Path/To/File.mid" |
static const char* usageStr = "Usage: PlaySequence\n\t" |
BANK_CMD |
SMF_CHAN_CMD |
DISK_STREAM |
MIDI_CMD |
FILE_CMD |
FILE_CMD_1 |
FILE_CMD_2 |
NUM_FRAMES_CMD |
NO_PRINT_CMD |
PLAY_CMD |
START_TIME_CMD |
TRACK_CMD |
WAIT_CMD |
SRC_FILE_CMD; |
UInt32 didOverload = 0; |
UInt64 overloadTime = 0; |
UInt64 startRunningTime; |
Float32 maxCPULoad = .8; |
int main (int argc, const char * argv[]) |
{ |
if (argc == 1) { |
fprintf (stderr, "%s\n", usageStr); |
exit(0); |
} |
char* filePath = 0; |
bool shouldPlay = false; |
bool shouldSetBank = false; |
bool shouldUseMIDIEndpoint = false; |
bool shouldPrint = true; |
bool waitAtEnd = false; |
bool diskStream = false; |
OSType dataFormat = 0; |
Float64 srate = 0; |
const char* outputFilePath = 0; |
MusicSequenceLoadFlags loadFlags = 0; |
char* bankPath = 0; |
Float32 startTime = 0; |
UInt32 numFrames = 512; |
std::set<int> trackSet; |
for (int i = 1; i < argc; ++i) |
{ |
if (!strcmp ("-p", argv[i])) |
{ |
shouldPlay = true; |
} |
else if (!strcmp ("-w", argv[i])) |
{ |
waitAtEnd = true; |
} |
else if (!strcmp ("-d", argv[i])) |
{ |
diskStream = true; |
} |
else if (!strcmp ("-b", argv[i])) |
{ |
shouldSetBank = true; |
if (++i == argc) goto malformedInput; |
bankPath = const_cast<char*>(argv[i]); |
} |
else if (!strcmp ("-n", argv[i])) |
{ |
shouldPrint = false; |
} |
else if ((filePath == 0) && (argv[i][0] == '/' || argv[i][0] == '~')) |
{ |
filePath = const_cast<char*>(argv[i]); |
} |
else if (!strcmp ("-s", argv[i])) |
{ |
if (++i == argc) goto malformedInput; |
sscanf (argv[i], "%f", &startTime); |
} |
else if (!strcmp ("-t", argv[i])) |
{ |
int index; |
if (++i == argc) goto malformedInput; |
sscanf (argv[i], "%d", &index); |
trackSet.insert(--index); |
} |
else if (!strcmp("-e", argv[i])) |
{ |
shouldUseMIDIEndpoint = true; |
} |
else if (!strcmp("-c", argv[i])) |
{ |
loadFlags = kMusicSequenceLoadSMF_ChannelsToTracks; |
} |
else if (!strcmp ("-i", argv[i])) |
{ |
if (++i == argc) goto malformedInput; |
sscanf (argv[i], "%lu", (unsigned long*)(&numFrames)); |
} |
else if (!strcmp ("-f", argv[i])) |
{ |
if (i + 3 >= argc) goto malformedInput; |
outputFilePath = argv[++i]; |
StrToOSType (argv[++i], dataFormat); |
sscanf (argv[++i], "%lf", &srate); |
} |
else |
{ |
malformedInput: |
fprintf (stderr, "%s\n", usageStr); |
exit (1); |
} |
} |
if (filePath == 0) { |
fprintf (stderr, "You have to specify a MIDI file to print or play\n"); |
fprintf (stderr, "%s\n", usageStr); |
exit (1); |
} |
if (shouldUseMIDIEndpoint && outputFilePath) { |
printf ("can't write a file when you try to play out to a MIDI Endpoint\n"); |
exit (1); |
} |
MusicSequence sequence; |
OSStatus result; |
FailIf ((result = LoadSMF (filePath, sequence, loadFlags)), fail, "LoadSMF"); |
if (shouldPrint) |
CAShow (sequence); |
if (shouldPlay) |
{ |
AUGraph graph = 0; |
AudioUnit theSynth = 0; |
FailIf ((result = MusicSequenceGetAUGraph (sequence, &graph)), fail, "MusicSequenceGetAUGraph"); |
FailIf ((result = AUGraphOpen (graph)), fail, "AUGraphOpen"); |
FailIf ((result = GetSynthFromGraph (graph, theSynth)), fail, "GetSynthFromGraph"); |
FailIf ((result = AudioUnitSetProperty (theSynth, |
kAudioUnitProperty_CPULoad, |
kAudioUnitScope_Global, 0, |
&maxCPULoad, sizeof(maxCPULoad))), fail, "AudioUnitSetProperty: kAudioUnitProperty_CPULoad"); |
if (shouldUseMIDIEndpoint) |
{ |
MIDIClientRef theMidiClient; |
MIDIClientCreate(CFSTR("Play Sequence"), NULL, NULL, &theMidiClient); |
ItemCount destCount = MIDIGetNumberOfDestinations(); |
if (destCount == 0) { |
fprintf (stderr, "No MIDI Endpoints to play to.\n"); |
exit(1); |
} |
FailIf ((result = MusicSequenceSetMIDIEndpoint (sequence, MIDIGetDestination(0))), fail, "MusicSequenceSetMIDIEndpoint"); |
} |
else |
{ |
if (shouldSetBank) { |
CFURLRef soundBankURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)bankPath, strlen(bankPath), false); |
printf ("Setting Sound Bank:%s\n", bankPath); |
result = AudioUnitSetProperty (theSynth, |
kMusicDeviceProperty_SoundBankURL, |
kAudioUnitScope_Global, 0, |
&soundBankURL, sizeof(soundBankURL)); |
if (soundBankURL) CFRelease(soundBankURL); |
FailIf (result, fail, "AudioUnitSetProperty: kMusicDeviceProperty_SoundBankURL"); |
} |
if (diskStream) { |
UInt32 value = diskStream; |
FailIf ((result = AudioUnitSetProperty (theSynth, |
kMusicDeviceProperty_StreamFromDisk, |
kAudioUnitScope_Global, 0, |
&value, sizeof(value))), fail, "AudioUnitSetProperty: kMusicDeviceProperty_StreamFromDisk"); |
} |
if (outputFilePath) { |
// need to tell synth that is going to render a file. |
UInt32 value = 1; |
FailIf ((result = AudioUnitSetProperty (theSynth, |
kAudioUnitProperty_OfflineRender, |
kAudioUnitScope_Global, 0, |
&value, sizeof(value))), fail, "AudioUnitSetProperty: kAudioUnitProperty_OfflineRender"); |
} |
FailIf ((result = SetUpGraph (graph, numFrames, srate, (outputFilePath != NULL))), fail, "SetUpGraph"); |
if (shouldPrint) { |
printf ("Sample Rate: %.1f \n", srate); |
printf ("Disk Streaming is enabled: %c\n", (diskStream ? 'T' : 'F')); |
} |
FailIf ((result = AUGraphInitialize (graph)), fail, "AUGraphInitialize"); |
if (shouldPrint) |
CAShow (graph); |
} |
MusicPlayer player; |
FailIf ((result = NewMusicPlayer (&player)), fail, "NewMusicPlayer"); |
FailIf ((result = MusicPlayerSetSequence (player, sequence)), fail, "MusicPlayerSetSequence"); |
// figure out sequence length |
UInt32 ntracks; |
FailIf ((MusicSequenceGetTrackCount (sequence, &ntracks)), fail, "MusicSequenceGetTrackCount"); |
MusicTimeStamp sequenceLength = 0; |
bool shouldPrintTracks = shouldPrint && !trackSet.empty(); |
if (shouldPrintTracks) |
printf ("Only playing specified tracks:\n\t"); |
for (UInt32 i = 0; i < ntracks; ++i) { |
MusicTrack track; |
MusicTimeStamp trackLength; |
UInt32 propsize = sizeof(MusicTimeStamp); |
FailIf ((result = MusicSequenceGetIndTrack(sequence, i, &track)), fail, "MusicSequenceGetIndTrack"); |
FailIf ((result = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, |
&trackLength, &propsize)), fail, "MusicTrackGetProperty: kSequenceTrackProperty_TrackLength"); |
if (trackLength > sequenceLength) |
sequenceLength = trackLength; |
if (!trackSet.empty() && (trackSet.find(i) == trackSet.end())) |
{ |
Boolean mute = true; |
FailIf ((result = MusicTrackSetProperty(track, kSequenceTrackProperty_MuteStatus, &mute, sizeof(mute))), fail, "MusicTrackSetProperty: kSequenceTrackProperty_MuteStatus"); |
} |
else if (shouldPrintTracks) { |
printf ("%d, ", int(i+1)); |
} |
} |
if (shouldPrintTracks) |
printf ("\n"); |
// now I'm going to add 8 beats on the end for the reverb/long releases to tail off... |
sequenceLength += 8; |
FailIf ((result = MusicPlayerSetTime (player, startTime)), fail, "MusicPlayerSetTime"); |
FailIf ((result = MusicPlayerPreroll (player)), fail, "MusicPlayerPreroll"); |
if (shouldPrint) { |
printf ("Ready to play: %s, %.2f beats long\n\t<Enter> to continue: ", filePath, sequenceLength); |
getc(stdin); |
} |
startRunningTime = CAHostTimeBase::GetTheCurrentTime(); |
/* if (waitAtEnd && graph) |
AUGraphStart(graph); |
*/ |
FailIf ((result = MusicPlayerStart (player)), fail, "MusicPlayerStart"); |
if (outputFilePath) |
WriteOutputFile (outputFilePath, dataFormat, srate, sequenceLength, shouldPrint, graph, numFrames, player); |
else |
PlayLoop (player, graph, sequenceLength, shouldPrint, waitAtEnd); |
FailIf ((result = MusicPlayerStop (player)), fail, "MusicPlayerStop"); |
if (shouldPrint) printf ("finished playing\n"); |
/* if (waitAtEnd) { |
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); |
if (graph) |
AUGraphStop(graph); |
if (shouldPrint) printf ("disposing\n"); |
} |
*/ |
// this shows you how you should dispose of everything |
FailIf ((result = DisposeMusicPlayer (player)), fail, "DisposeMusicPlayer"); |
FailIf ((result = DisposeMusicSequence(sequence)), fail, "DisposeMusicSequence"); |
// don't own the graph so don't dispose it (the seq owns it as we never set it ourselves, we just got it....) |
} |
else { |
FailIf ((result = DisposeMusicSequence(sequence)), fail, "DisposeMusicSequence"); |
} |
while (waitAtEnd) |
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, false); |
return 0; |
fail: |
if (shouldPrint) printf ("Error = %ld\n", (long)result); |
return result; |
} |
void PlayLoop (MusicPlayer &player, AUGraph &graph, MusicTimeStamp sequenceLength, bool shouldPrint, bool waitAtEnd) |
{ |
OSStatus result; |
int waitCounter = 0; |
while (1) { |
usleep (2 * 1000 * 1000); |
if (didOverload) { |
printf ("* * * * * %lu Overloads detected on device playing audio\n", (unsigned long)didOverload); |
overloadTime = CAHostTimeBase::ConvertToNanos (overloadTime - startRunningTime); |
printf ("\tSeconds after start = %lf\n", double(overloadTime / 1000000000.)); |
didOverload = 0; |
} |
if (waitAtEnd && ++waitCounter > 10) break; |
MusicTimeStamp time; |
FailIf ((result = MusicPlayerGetTime (player, &time)), fail, "MusicPlayerGetTime"); |
if (shouldPrint) { |
printf ("current time: %6.2f beats", time); |
if (graph) { |
Float32 load; |
FailIf ((result = AUGraphGetCPULoad(graph, &load)), fail, "AUGraphGetCPULoad"); |
printf (", CPU load = %.2f%%\n", (load * 100.)); |
} else |
printf ("\n"); //no cpu load on AUGraph - its not running - if just playing out to MIDI |
} |
if (time >= sequenceLength) |
break; |
} |
return; |
fail: |
if (shouldPrint) printf ("Error = %ld\n", (long)result); |
exit(1); |
} |
OSStatus GetSynthFromGraph (AUGraph& inGraph, AudioUnit& outSynth) |
{ |
UInt32 nodeCount; |
OSStatus result = noErr; |
FailIf ((result = AUGraphGetNodeCount (inGraph, &nodeCount)), fail, "AUGraphGetNodeCount"); |
for (UInt32 i = 0; i < nodeCount; ++i) |
{ |
AUNode node; |
FailIf ((result = AUGraphGetIndNode(inGraph, i, &node)), fail, "AUGraphGetIndNode"); |
AudioComponentDescription desc; |
FailIf ((result = AUGraphNodeInfo(inGraph, node, &desc, 0)), fail, "AUGraphNodeInfo"); |
if (desc.componentType == kAudioUnitType_MusicDevice) |
{ |
FailIf ((result = AUGraphNodeInfo(inGraph, node, 0, &outSynth)), fail, "AUGraphNodeInfo"); |
return noErr; |
} |
} |
fail: // didn't find the synth AU |
return -1; |
} |
void OverlaodListenerProc( void * inRefCon, |
AudioUnit ci, |
AudioUnitPropertyID inID, |
AudioUnitScope inScope, |
AudioUnitElement inElement) |
{ |
didOverload++; |
overloadTime = CAHostTimeBase::GetTheCurrentTime(); |
} |
OSStatus SetUpGraph (AUGraph &inGraph, UInt32 numFrames, Float64 &sampleRate, bool isOffline) |
{ |
OSStatus result = noErr; |
AudioUnit outputUnit = 0; |
AUNode outputNode; |
// the frame size is the I/O size to the device |
// the device is going to run at a sample rate it is set at |
// so, when we set this, we also have to set the max frames for the graph nodes |
UInt32 nodeCount; |
FailIf ((result = AUGraphGetNodeCount (inGraph, &nodeCount)), home, "AUGraphGetNodeCount"); |
for (int i = 0; i < (int)nodeCount; ++i) |
{ |
AUNode node; |
FailIf ((result = AUGraphGetIndNode(inGraph, i, &node)), home, "AUGraphGetIndNode"); |
AudioComponentDescription desc; |
AudioUnit unit; |
FailIf ((result = AUGraphNodeInfo(inGraph, node, &desc, &unit)), home, "AUGraphNodeInfo"); |
if (desc.componentType == kAudioUnitType_Output) |
{ |
if (outputUnit == 0) { |
outputUnit = unit; |
FailIf ((result = AUGraphNodeInfo(inGraph, node, 0, &outputUnit)), home, "AUGraphNodeInfo"); |
if (!isOffline) { |
// these two properties are only applicable if its a device we're playing too |
FailIf ((result = AudioUnitSetProperty (outputUnit, |
kAudioDevicePropertyBufferFrameSize, |
kAudioUnitScope_Output, 0, |
&numFrames, sizeof(numFrames))), home, "AudioUnitSetProperty: kAudioDevicePropertyBufferFrameSize"); |
FailIf ((result = AudioUnitAddPropertyListener (outputUnit, |
kAudioDeviceProcessorOverload, |
OverlaodListenerProc, 0)), home, "AudioUnitAddPropertyListener: kAudioDeviceProcessorOverload"); |
// if we're rendering to the device, then we render at its sample rate |
UInt32 theSize; |
theSize = sizeof(sampleRate); |
FailIf ((result = AudioUnitGetProperty (outputUnit, |
kAudioUnitProperty_SampleRate, |
kAudioUnitScope_Output, 0, |
&sampleRate, &theSize)), home, "AudioUnitGetProperty: kAudioUnitProperty_SampleRate"); |
} else { |
// remove device output node and add generic output |
FailIf ((result = AUGraphRemoveNode (inGraph, node)), home, "AUGraphRemoveNode"); |
desc.componentSubType = kAudioUnitSubType_GenericOutput; |
FailIf ((result = AUGraphAddNode (inGraph, &desc, &node)), home, "AUGraphAddNode"); |
FailIf ((result = AUGraphNodeInfo(inGraph, node, NULL, &unit)), home, "AUGraphNodeInfo"); |
outputUnit = unit; |
outputNode = node; |
// we render the output offline at the desired sample rate |
FailIf ((result = AudioUnitSetProperty (outputUnit, |
kAudioUnitProperty_SampleRate, |
kAudioUnitScope_Output, 0, |
&sampleRate, sizeof(sampleRate))), home, "AudioUnitSetProperty: kAudioUnitProperty_SampleRate"); |
} |
// ok, lets start the loop again now and do it all... |
i = -1; |
} |
} |
else |
{ |
// we only have to do this on the output side |
// as the graph's connection mgmt will propogate this down. |
if (outputUnit) { |
// reconnect up to the output unit if we're offline |
if (isOffline && desc.componentType != kAudioUnitType_MusicDevice) { |
FailIf ((result = AUGraphConnectNodeInput (inGraph, node, 0, outputNode, 0)), home, "AUGraphConnectNodeInput"); |
} |
FailIf ((result = AudioUnitSetProperty (unit, |
kAudioUnitProperty_SampleRate, |
kAudioUnitScope_Output, 0, |
&sampleRate, sizeof(sampleRate))), home, "AudioUnitSetProperty: kAudioUnitProperty_SampleRate"); |
} |
} |
FailIf ((result = AudioUnitSetProperty (unit, kAudioUnitProperty_MaximumFramesPerSlice, |
kAudioUnitScope_Global, 0, |
&numFrames, sizeof(numFrames))), home, "AudioUnitSetProperty: kAudioUnitProperty_MaximumFramesPerSlice"); |
} |
home: |
return result; |
} |
OSStatus LoadSMF(const char *filename, MusicSequence& sequence, MusicSequenceLoadFlags loadFlags) |
{ |
OSStatus result = noErr; |
CFURLRef url = NULL; |
FailIf ((result = NewMusicSequence(&sequence)), home, "NewMusicSequence"); |
url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)filename, strlen(filename), false); |
FailIf ((result = MusicSequenceFileLoad (sequence, url, 0, loadFlags)), home, "MusicSequenceFileLoad"); |
home: |
if (url) CFRelease(url); |
return result; |
} |
#pragma mark - |
#pragma mark Write Output File |
void WriteOutputFile (const char* outputFilePath, |
OSType dataFormat, |
Float64 srate, |
MusicTimeStamp sequenceLength, |
bool shouldPrint, |
AUGraph inGraph, |
UInt32 numFrames, |
MusicPlayer player) |
{ |
OSStatus result = 0; |
UInt32 size; |
CAStreamBasicDescription outputFormat; |
outputFormat.mChannelsPerFrame = 2; |
outputFormat.mSampleRate = srate; |
outputFormat.mFormatID = dataFormat; |
AudioFileTypeID destFileType; |
CAAudioFileFormats::Instance()->InferFileFormatFromFilename (outputFilePath, destFileType); |
if (dataFormat == kAudioFormatLinearPCM) { |
outputFormat.mBytesPerPacket = outputFormat.mChannelsPerFrame * 2; |
outputFormat.mFramesPerPacket = 1; |
outputFormat.mBytesPerFrame = outputFormat.mBytesPerPacket; |
outputFormat.mBitsPerChannel = 16; |
if (destFileType == kAudioFileWAVEType) |
outputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
| kLinearPCMFormatFlagIsPacked; |
else |
outputFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian |
| kLinearPCMFormatFlagIsSignedInteger |
| kLinearPCMFormatFlagIsPacked; |
} else { |
// use AudioFormat API to fill out the rest. |
size = sizeof(outputFormat); |
FailIf ((result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat)), fail, ""); |
} |
if (shouldPrint) { |
printf ("Writing to file: %s with format:\n* ", outputFilePath); |
outputFormat.Print(); |
} |
CFURLRef url; url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8*)outputFilePath, strlen(outputFilePath), false); |
// create output file, delete existing file |
ExtAudioFileRef outfile; |
result = ExtAudioFileCreateWithURL(url, destFileType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &outfile); |
if (url) CFRelease (url); |
FailIf (result, fail, "ExtAudioFileCreateWithURL"); |
AudioUnit outputUnit; outputUnit = NULL; |
UInt32 nodeCount; |
FailIf ((result = AUGraphGetNodeCount (inGraph, &nodeCount)), fail, "AUGraphGetNodeCount"); |
for (UInt32 i = 0; i < nodeCount; ++i) |
{ |
AUNode node; |
FailIf ((result = AUGraphGetIndNode(inGraph, i, &node)), fail, "AUGraphGetIndNode"); |
AudioComponentDescription desc; |
FailIf ((result = AUGraphNodeInfo(inGraph, node, &desc, NULL)), fail, "AUGraphNodeInfo"); |
if (desc.componentType == kAudioUnitType_Output) |
{ |
FailIf ((result = AUGraphNodeInfo(inGraph, node, 0, &outputUnit)), fail, "AUGraphNodeInfo"); |
break; |
} |
} |
FailIf ((result = (outputUnit == NULL)), fail, "outputUnit == NULL"); |
{ |
CAStreamBasicDescription clientFormat = CAStreamBasicDescription(); |
size = sizeof(clientFormat); |
FailIf ((result = AudioUnitGetProperty (outputUnit, |
kAudioUnitProperty_StreamFormat, |
kAudioUnitScope_Output, 0, |
&clientFormat, &size)), fail, "AudioUnitGetProperty: kAudioUnitProperty_StreamFormat"); |
size = sizeof(clientFormat); |
FailIf ((result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat)), fail, "ExtAudioFileSetProperty: kExtAudioFileProperty_ClientDataFormat"); |
{ |
MusicTimeStamp currentTime; |
AUOutputBL outputBuffer (clientFormat, numFrames); |
AudioTimeStamp tStamp; |
memset (&tStamp, 0, sizeof(AudioTimeStamp)); |
tStamp.mFlags = kAudioTimeStampSampleTimeValid; |
int i = 0; |
int numTimesFor10Secs = (int)(10. / (numFrames / srate)); |
do { |
outputBuffer.Prepare(); |
AudioUnitRenderActionFlags actionFlags = 0; |
FailIf ((result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL())), fail, "AudioUnitRender"); |
tStamp.mSampleTime += numFrames; |
FailIf ((result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL())), fail, "ExtAudioFileWrite"); |
FailIf ((result = MusicPlayerGetTime (player, ¤tTime)), fail, "MusicPlayerGetTime"); |
if (shouldPrint && (++i % numTimesFor10Secs == 0)) |
printf ("current time: %6.2f beats\n", currentTime); |
} while (currentTime < sequenceLength); |
} |
} |
// close |
ExtAudioFileDispose(outfile); |
return; |
fail: |
printf ("Problem: %ld\n", (long)result); |
exit(1); |
} |
Copyright © 2012 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2012-07-05