I managed to get the raw data from MPMediaItem using AVAssetReader after combining the answers to several SO questions like this and this and a nice post. I can also play this raw data using FMOD, but then a problem arises.
It seems that the resulting sound has lower quality than the original track. Although the AVAssetTrackDescription format tells me that there are 2 channels in the data, the result sounds monophonic. It also sounds slightly moist (less crunchy), like the bitrate.
Am I doing something wrong or is it the quality of MPMediaItem data specifically designed for AVAssetReader (due to piracy)?
#define OUTPUTRATE 44100
Initializing AVAssetReader and AVAssetReaderTrackOutput
// prepare AVAsset and AVAssetReaderOutput etc MPMediaItem* mediaItem = ...; NSURL* ipodAudioUrl = [mediaItem valueForProperty:MPMediaItemPropertyAssetURL]; AVURLAsset * asset = [[AVURLAsset alloc] initWithURL:ipodAudioUrl options:nil]; NSError * error = nil; assetReader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; if(error) NSLog(@"error creating reader: %@", [error debugDescription]); AVAssetTrack* songTrack = [asset.tracks objectAtIndex:0]; NSArray* trackDescriptions = songTrack.formatDescriptions; numChannels = 2; for(unsigned int i = 0; i < [trackDescriptions count]; ++i) { CMAudioFormatDescriptionRef item = (CMAudioFormatDescriptionRef)[trackDescriptions objectAtIndex:i]; const AudioStreamBasicDescription* bobTheDesc = CMAudioFormatDescriptionGetStreamBasicDescription (item); if(bobTheDesc && bobTheDesc->mChannelsPerFrame == 1) { numChannels = 1; } } NSDictionary* outputSettingsDict = [[[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey, [NSNumber numberWithInt:OUTPUTRATE],AVSampleRateKey, [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey, [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey, [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey, [NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved, nil] autorelease]; AVAssetReaderTrackOutput * output = [[[AVAssetReaderTrackOutput alloc] initWithTrack:songTrack outputSettings:outputSettingsDict] autorelease]; [assetReader addOutput:output]; [assetReader startReading];
Initializing FMOD and FMOD Sound
// Init FMOD FMOD_RESULT result = FMOD_OK; unsigned int version = 0; /* Create a System object and initialize */ result = FMOD::System_Create(&system); ERRCHECK(result); result = system->getVersion(&version); ERRCHECK(result); if (version < FMOD_VERSION) { fprintf(stderr, "You are using an old version of FMOD %08x. This program requires %08x\n", version, FMOD_VERSION); exit(-1); } result = system->setSoftwareFormat(OUTPUTRATE, FMOD_SOUND_FORMAT_PCM16, 1, 0, FMOD_DSP_RESAMPLER_LINEAR); ERRCHECK(result); result = system->init(32, FMOD_INIT_NORMAL | FMOD_INIT_ENABLE_PROFILE, NULL); ERRCHECK(result); // Init FMOD sound stream CMTimeRange timeRange = [songTrack timeRange]; float durationInSeconds = timeRange.duration.value / timeRange.duration.timescale; FMOD_CREATESOUNDEXINFO exinfo = {0}; memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); /* required. */ exinfo.decodebuffersize = OUTPUTRATE; /* Chunk size of stream update in samples. This will be the amount of data passed to the user callback. */ exinfo.length = OUTPUTRATE * numChannels * sizeof(signed short) * durationInSeconds; /* Length of PCM data in bytes of whole song (for Sound::getLength) */ exinfo.numchannels = numChannels; /* Number of channels in the sound. */ exinfo.defaultfrequency = OUTPUTRATE; /* Default playback rate of sound. */ exinfo.format = FMOD_SOUND_FORMAT_PCM16; /* Data format of sound. */ exinfo.pcmreadcallback = pcmreadcallback; /* User callback for reading. */ exinfo.pcmsetposcallback = pcmsetposcallback; /* User callback for seeking. */ result = system->createStream(NULL, FMOD_OPENUSER, &exinfo, &sound); ERRCHECK(result); result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel); ERRCHECK(result);
Reading from AVAssetReaderTrackOutput to the ring buffer
AVAssetReaderTrackOutput * trackOutput = (AVAssetReaderTrackOutput *)[assetReader.outputs objectAtIndex:0]; CMSampleBufferRef sampleBufferRef = [trackOutput copyNextSampleBuffer]; if (sampleBufferRef) { AudioBufferList audioBufferList; CMBlockBufferRef blockBuffer; CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBufferRef, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL, 0, &blockBuffer); if(blockBuffer == NULL) { stopLoading = YES; continue; } if(&audioBufferList == NULL) { stopLoading = YES; continue; } if(audioBufferList.mNumberBuffers != 1) NSLog(@"numBuffers = %lu", audioBufferList.mNumberBuffers); for( int y=0; y<audioBufferList.mNumberBuffers; y++ ) { AudioBuffer audioBuffer = audioBufferList.mBuffers[y]; SInt8 *frame = (SInt8*)audioBuffer.mData; for(int i=0; i<audioBufferList.mBuffers[y].mDataByteSize; i++) { ringBuffer->push_back(frame[i]); } } CMSampleBufferInvalidate(sampleBufferRef); CFRelease(sampleBufferRef); }