Getting the AVAssetReader's Sample Data - iphone

I'm trying to get the PCM data from an MP3. I'm using AVAssetReaderOutput and it seems to reading the data fine.
while(true)
{
nextBuffer = [assetReaderOutput copyNextSampleBuffer];
if(nextBuffer)
{
countsample = CMSampleBufferGetNumSamples(nextBuffer);
NSLog(#"%i", countsample);
}
}
I noticed that if I add up countsample, it equates to the number of seconds in the song (assuming 44100hz sample rate). For this reason, I'm sure the reading is being handled perfectly. What I'd like to do, however, is perform various DSP filters on this data but I need to do it on the sample information itself. How can I access the sample data? Also, I noticed that the CMSampleBufferGetNumSamples always returned 8192 except at the end of the song. Is there a way to increase/decrease this read rate?
Thank you

Append the data to a NSMutableData object.
NSMutabelData *samples = [NSMutabelData data];
while(countsample)
{
nextBuffer = [assetReaderOutput copyNextSampleBuffer];
if(nextBuffer)
{
countsample = CMSampleBufferGetNumSamples(nextBuffer);
[samples appendBytes:nextBuffer length:countsample];
}
}
countsample and nextBuffer are assumed to already exist in your code.

Related

How to edit the default instrument of an AUGraph?

I'm working with the MusicPlayer API. I understand that when you load in a .mid as a sequence, the API creates a default AUGraph for you that includes an AUSampler. This AUSampler uses a simple sine-wave based instrument to synthesize the notes in the .mid
My question is, how does one change the default instrument in the AUSampler? I understand that you can use SoundFont2 files (.sf2) and add them using the AudioUnitSetProperty method. But, how does one access this default AUGraph? Do you have to open the graph before you can edit the AudioUnit or is opening a graph only for editing connections between nodes?
Thanks :)
I've written a tutorial on this but here but here's an outline of the process:
Function to load a Sound Font file (taken from the Apple documentation):
-(OSStatus) loadFromDLSOrSoundFont: (NSURL *)bankURL withPatch: (int)presetNumber {
OSStatus result = noErr;
// fill out a bank preset data structure
AUSamplerBankPresetData bpdata;
bpdata.bankURL = (__bridge CFURLRef) bankURL;
bpdata.bankMSB = kAUSampler_DefaultMelodicBankMSB;
bpdata.bankLSB = kAUSampler_DefaultBankLSB;
bpdata.presetID = (UInt8) presetNumber;
// set the kAUSamplerProperty_LoadPresetFromBank property
result = AudioUnitSetProperty([pointer to your AUSampler node here],
kAUSamplerProperty_LoadPresetFromBank,
kAudioUnitScope_Global,
0,
&bpdata,
sizeof(bpdata));
// check for errors
NSCAssert (result == noErr,
#"Unable to set the preset property on the Sampler. Error code:%d '%.4s'",
(int) result,
(const char *)&result);
return result; }
Then you need to load the Sound Font from your Resources folder:
NSURL *presetURL = [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Name of sound font" ofType:#"sf2"]];
// Initialise the sound font
[self loadFromDLSOrSoundFont: (NSURL *)presetURL withPatch: (int)10];
Hope this helps!
You might take a look at the Audiograph example. It doesn't use soundFonts but should give you an idea of how to set up a graph.
When I use the MusicPlayer I always generate the midi note data from code/GUI and create the AUGraph (with a mixer) from scratch. There are ways to derive/extract the default generated AUGraph & AUSampler resulting from loading a midi file (example code below) but I never had success setting a new soundFont this way. On the other hand, creating the AUGraph from scratch and then loading an .sf2 file works great.
AUGraph graph;
result = MusicSequenceGetAUGraph (sequence, &graph);
MusicTrack firstTrack;
result = MusicSequenceGetIndTrack (sequence, 0, &firstTrack);
AUNode myNode;
result = MusicTrackGetDestNode(firstTrack,&myNode);
AudioUnit mySamplerUnit;
result = AUGraphNodeInfo(graph, myNode, 0, &mySamplerUnit);

Memory Leak when using Pointer to AudioUnitSampleType in Struct - calloc

I am coding an audio app for the iphone where I need to use some C code to deal with the audio files. In short, I have a memory leak that is causing the app to crash after so many files have been loaded. The problem is related to a Struct that I create that holds the audio files when read in. The Struct is created as follows;
typedef struct {
UInt32 frameCount; // the total number of frames in the audio data
UInt32 sampleNumber; // the next audio sample to play
BOOL isStereo; // set to true if there is data audioDataRight member
AudioUnitSampleType *audioDataLeft; // complete left channel of audio data read from file
AudioUnitSampleType *audioDataRight; // complete right channel of audio data read file
} soundStruct, *soundStructPtr;
The Struct is then Initialized in the header like this;
soundStruct phraseSynthStructArray[3];
I then attempt to join two files that have been read into phraseSynthStructArray[phrase1Index] and phraseSynthStructArray[phrase2Index] and put the combined file into phraseSynthStructArray[synthPhraseIndex] like this;
- (BOOL) joinPhrases:(UInt32)phrase1Index phrase2Index:(UInt32)phrase2Index synthPhraseIndex:(UInt32)synthPhraseIndex{
// get the combined frame count
UInt64 totalFramesInFile = inArray[phrase1Index].frameCount + inArray[phrase2Index].frameCount;
//now resize the synthPhrase slot buffer to be the same size as both files combined
// phraseOut is used to hold the combined data prior to it being passed into the soundStructArray slot
free(phraseSynthStructArray[synthPhraseIndex].audioDataLeft);
phraseSynthStructArray[synthPhraseIndex].audioDataLeft = NULL;
phraseSynthStructArray[synthPhraseIndex].frameCount = 0;
phraseSynthStructArray[synthPhraseIndex].frameCount = totalFramesInFile;
phraseSynthStructArray[synthPhraseIndex].audioDataLeft = (AudioUnitSampleType *) calloc(totalFramesInFile, sizeof (AudioUnitSampleType));
for (UInt32 frameNumber = 0; frameNumber < inArray[phrase1Index].frameCount; ++frameNumber) {
phraseSynthStructArray[synthPhraseIndex].audioDataLeft[frameNumber] = phraseSynthStructArray[phrase1Index].audioDataLeft[frameNumber];
}
UInt32 sampleNumber=0;
for (UInt32 frameNumber = phraseSynthStructArray[phrase1Index].frameCount; frameNumber < totalFramesInFile; ++frameNumber) {
phraseSynthStructArray[synthPhraseIndex].audioDataLeft[frameNumber] = phraseSynthStructArray[phrase2Index].audioDataLeft[sampleNumber];
sampleNumber++;
}
return YES;
}
This all works fine and the resulting file is joined and can be used. The isuue I am having is when I allocate the memory here, phraseSynthStructArray[synthPhraseIndex].audioDataLeft = (AudioUnitSampleType *) calloc(totalFramesInFile, sizeof (AudioUnitSampleType)); then next time the method is called, this memory leaks each time and eventually crashes the app. The reason I need to allocate the memory here is because the memory has to be resized to accomodate the joined file which varies in length depending on the size of the input files.
I cannot free the memory after the operation as its needed elsewhere after the method has been called and I have tried to free it before (in joinPhrases method above), but this does not seem to work. I have also tried using realloc to free/reallocate the memory by passing the pointer to the previously allocated memory but this casues a crash stating EXEC_BAD_ACCESS.
I am not a seasoned C programmer and Cannot figure out what I am doing wrong here to cause the leak. I would appreciate some advice to help me track down this issue as I have been banging my head against this for days with no joy. I have read thats its a bad idea to have Pointers in Structs, could this be the root of my problem?
Thanks in advance,
K.
Maybe this helps:
- (BOOL) joinPhrases:(UInt32)phrase1Index phrase2Index:(UInt32)phrase2Index synthPhraseIndex:(UInt32)synthPhraseIndex{
// get the combined frame count
UInt64 totalFramesInFile = inArray[phrase1Index].frameCount + inArray[phrase2Index].frameCount;
. . .
void* old_ptr = phraseSynthStructArray[synthPhraseIndex].audioDataLeft;
phraseSynthStructArray[synthPhraseIndex].audioDataLeft = (AudioUnitSampleType *) calloc(totalFramesInFile, sizeof (AudioUnitSampleType));
if( old_ptr ) free(old_ptr);
. . .
return YES;
}
And make sure that there is no garbage in phraseSynthStructArray[synthPhraseIndex]

Parsing my flat-file on iPhone is a pain in the ***, please Help me out

I am programming an iPhone App which is supposed to parse a flat-file from the web, create managed objects from the flat-file and later on should display them in an UITableView.
There are no problems with the saving and the displaying, but I just can't get the hang of a good Parser.
Thats the file I want to parse: Flat-file
AS far as I know, I can't use the NSXMLParser for this task (because obviously there are no tags).
So I at first tried to programm a NSScanner which should get me the interesting properties --> didn't work out
Now I am using this method:
- (void) parseMemberDataWithURL: (NSString *)urlString
{
self.memberTempCounter = 1;
//Get data from web
self.downloadedText = [NSString stringWithContentsOfURL: [NSURL URLWithString: urlString] encoding:NSUTF8StringEncoding error:nil ];
memberArray = [downloadedText componentsSeparatedByString:#";"];
while (self.memberTempCounter<[memberArray count])
{
[[ExhibitorController sharedController] createExhibitorWithName:[memberArray objectAtIndex:self.memberTempCounter]
street:[memberArray objectAtIndex:self.memberTempCounter+2]
zip:[memberArray objectAtIndex:self.memberTempCounter+3]
city:[memberArray objectAtIndex:self.memberTempCounter+4]
email:[memberArray objectAtIndex:self.memberTempCounter+7]
phone:[memberArray objectAtIndex:self.memberTempCounter+5]
website:[memberArray objectAtIndex:self.memberTempCounter+8]
produktbereiche:[[memberArray objectAtIndex:self.memberTempCounter+9] componentsSeparatedByString:#","]];
self.memberTempCounter= self.memberTempCounter+13;
}
}
I am using the memberTempCounter to identify the property.
The problems are:
This only works out in like 3 of 4 times.1 of 4 times the App crashes and I have no Idea why...
The method has a performance like a 1962 VW Beetle. Parsing the whole chunk of data takes up to 3 Minutes on my iPhone 3G
Any Ideas or a simpler way to do this?
I would be really gratefull. Thanks in advance: -)
You might as well do all the parsing in the background, and then display as the information gets parsed.
As for memory issues, try doing temporary autorelease pools and release every 50 or so iterations through the loop.
int count = 0;
NSAutoreleasePool * loopPool = [[NSAutoreleasePool alloc] init];
while(someInsanelyLargeCondition){
// Do your stuff here
// .............
count++;
if (count > 50) {
count = 0;
[loopPool release];
loopPool = [[NSAutoreleasePool alloc] init];
}
}
Recursive-descent (LL1) parsers are pretty simple, light on memory, and for speed they go almost as fast as you can run a pointer through characters. Building your data structure would probably be the dominant time-taker.
I was finally able to fix my performance problem.
I have a method in another class, which ads Tags for the different Exhibitors. Therefore it first checks if the Tag already is stored in the database or else creates it.
With an growing Set of Tags in my database the search-process took longer and longer and this led to the long parsing time.
Anyone else having this problem: Take a look at the Performance Core Data Programming guide of apple in the "Implementing Find-or-Create Efficiently"-section:
http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html

How to get the uncompressed file size of an MP3 file using CoreAudio API

Using CoreAudio, I am able to get the sampleRate (frames per second) and the file size, but in order to get the "total" time of the song, I need to know the Real file size of that compressed mp3.
AudioStreamBasicDescription asbd;
UInt32 asbdSize = sizeof(asbd);
// get the stream format.
err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &asbdSize, &asbd);
if (err)
{
[self failWithErrorCode:AS_FILE_STREAM_GET_PROPERTY_FAILED];
return;
}
sampleRate = asbd.mSampleRate;
Is there any way I can know the real size of the song using Objective-C?
Thanks in advance.
See the answer to this question
There's a property you can ask in AudioFileGetProperty called kAudioFilePropertyEstimatedDuration that should do the trick.

Can anyone provide a working example of AudioFileStreamSeek for the iPhone?

I find Apple's documentation quite limited on AudioFileStreamSeek and I cannot find any examples of actual usage anywhere. I have a working streaming audio player, but I just can't seem to get AudioFileStreamSeek to work as advertised...
Any help tips or a little example would be greatly appreciated!
I am told this works:
AudioQueueStop(audioQueue, true);
UInt32 flags = 0;
err = AudioFileStreamParseBytes(audioFileStream, length, bytes,
kAudioFileStreamParseFlag_Discontinuity);
OSStatus status = AudioFileStreamSeek(audioFileStream, framePacket.mPacket,
&currentOffset, &flags);
NSLog(#"Setting next byte offset to: %qi, flags: %d", (long long)currentOffset, flags);
// then read data from the new offset set by AudioFileStreamSeek
[fileHandle seekToFileOffset:currentOffset];
NSData* data = "" readDataOfLength:4096];
flags = kAudioFileStreamParseFlag_Discontinuity;
status = AudioFileStreamParseBytes( stream, [data length], [data bytes], flags);
if (status != noErr)
{
NSLog(#"Error parsing bytes: %d", status);
}
Unless I'm mistaken, this is only available in the 3.0 SDK, and therefore under NDA. Maybe you should take this to the Apple Beta forums?
I stand corrected. AudioFileStreamSeek doesn't show up if you do a search in the online 2.2.1 documentation. You have to manually dig into the docs to find it.
Don't forget to add the data offset (kAudioFileStreamProperty_DataOffset) to the byte offset returned by AudioFileStreamSeek. The return value is an offset into the audio data and ignores the data offset.
It's also a good idea to stop and then re-start the AudioQueue before/after seeking.
Matt Gallagher uses AudioFileStreamSeek in his example "Streaming and playing an MP3 stream".
Look at Matt's code AudioStreamer.m:
SInt64 seekPacket = floor(newSeekTime / packetDuration);
err = AudioFileStreamSeek(audioFileStream, seekPacket, &packetAlignedByteOffset, &ioFlags);