I am currently working on an application as part of my bachelor's degree in computer science. The application will correlate data from the iPhone hardware (accelerometer, gps) and playable music.
The project is still in its infancy, working on it for only 2 months.
At the moment I am now, and where I need help, reads PCM samples from songs from the itunes library and plays them using an audio unit. Currently, the implementation that I would like to perform does the following: selects a random song from iTunes and reads samples from it when necessary, and saves it in the buffer, lets call it sampleBuffer. Later in the consumer model, the sound block (with mixer and remoteIO output) has a callback, where I simply copy the required number of samples from the SampleBuffer to the buffer specified in the callback. What I then hear through the speakers is not at all what I expect; I can understand that he is playing a song, but it seems that it is incorrectly decoded, and she has a lot of noise! I attached an image that shows the first half second (24576 samples @ 44.1 kHz), and this does not look like normal output. Before getting into the list, I checked that the file is not corrupted, similarly I wrote test cases for the buffer (so I know that the buffer does not change the samples), and although this may not be the best way to do this (some claim to go along the path of the audio queue), I want to perform various manipulations with the samples, as well as change the song until it is completed, rebuild the playback of a song, etc. In addition, it is possible that there are some incorrect block settings in the sound, however, the graph that displays the samples (which shows that the samples are decoded incorrectly) is taken directly from the buffer, so I'm just looking to decide why reading from disk and decoding not working properly. Right now I just want to get the game through work. Cant post images, because the new one for stackoverflow is so similar to the image link: http://i.stack.imgur.com/RHjlv.jpg
Listing:
Here I set the audioReadSettigns to be used for AVAssetReaderAudioMixOutput
// Set the read settings audioReadSettings = [[NSMutableDictionary alloc] init]; [audioReadSettings setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey]; [audioReadSettings setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey]; [audioReadSettings setValue:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey]; [audioReadSettings setValue:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey]; [audioReadSettings setValue:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsNonInterleaved]; [audioReadSettings setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
Now the following list of codes is a method that receives an NSString with the persistant_id of a song:
-(BOOL)setNextSongID:(NSString*)persistand_id { assert(persistand_id != nil); MPMediaItem *song = [self getMediaItemForPersistantID:persistand_id]; NSURL *assetUrl = [song valueForProperty:MPMediaItemPropertyAssetURL]; AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetUrl options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey]]; NSError *assetError = nil; assetReader = [[AVAssetReader assetReaderWithAsset:songAsset error:&assetError] retain]; if (assetError) { NSLog(@"error: %@", assetError); return NO; } CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, songAsset.duration); [assetReader setTimeRange:timeRange]; track = [[songAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; assetReaderOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:[NSArray arrayWithObject:track] audioSettings:audioReadSettings]; if (![assetReader canAddOutput:assetReaderOutput]) { NSLog(@"cant add reader output... die!"); return NO; } [assetReader addOutput:assetReaderOutput]; [assetReader startReading];
The following is the function - (void) copyEnoughSamplesToBufferForLength:
-(void)copyEnoughSamplesToBufferForLength:(UInt32)samples_count { [w_lock lock]; int stillToCopy = 0; if (sampleBuffer->numSamples() < samples_count) { stillToCopy = samples_count; } NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init]; CMSampleBufferRef sampleBufferRef; SInt16 *dataBuffer = (SInt16*)malloc(8192 * sizeof(SInt16)); int a = 0; while (stillToCopy > 0) { sampleBufferRef = [assetReaderOutput copyNextSampleBuffer]; if (!sampleBufferRef) { // end of song or no more samples return; } CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBufferRef); CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(sampleBufferRef); AudioBufferList audioBufferList; CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBufferRef, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL, 0, &blockBuffer); int data_length = floorf(numSamplesInBuffer * 1.0f); int j = 0; for (int bufferCount=0; bufferCount < audioBufferList.mNumberBuffers; bufferCount++) { SInt16* samples = (SInt16 *)audioBufferList.mBuffers[bufferCount].mData; for (int i=0; i < numSamplesInBuffer; i++) { dataBuffer[j] = samples[i]; j++; } } CFRelease(sampleBufferRef); sampleBuffer->putSamples(dataBuffer, j); stillToCopy = stillToCopy - data_length; } free(dataBuffer); [w_lock unlock]; [apool release]; }
Now sampleBuffer will have improperly decoded samples. Can someone help me why this is so? This happens for different files in my iTunes library (mp3, aac, wav, etc.). Any help would be greatly appreciated, in addition, if you need any other list of my code, or maybe something similar to the result, I will attach it to the request. I sat on this for the past week, trying to debug it and did not find any help on the Internet - everyone seems to be doing it in my way, but it seems that only I have this problem.
Thanks for any help!
Peter