我正在完成计算机科学本科课程中的一个应用程序。该应用程序将关联来自iPhone硬件(加速度计,GPS)和正在播放的音乐的数据。
这个项目还处于初期阶段,只进行了2个月的工作。
目前我需要帮助的地方是从iTunes库中读取歌曲的PCM样本,并使用音频单元播放它们。
当前我想要的实现方式是:从iTunes选择一首随机歌曲,在需要时从中读取样本并存储在缓冲区(称为sampleBuffer),然后在消费者模型中,音频单元(具有混合器和远程IO输出)具有回调,在其中将所需数量的样本从sampleBuffer中复制到回调中指定的缓冲区中。然后我通过扬声器听到的是不太符合我的预期;我可以识别出它正在播放歌曲,但似乎解码不正确,有很多噪音!我附上了一张图片,显示了前约半秒钟(24576个样本@44.1kHz),这与正常输出不太相似。
在进入代码之前,我检查了文件是否损坏,同样我已经为缓冲区编写了测试用例(因此我知道缓冲区不会改变样本),虽然这可能不是做到这一点的最佳方法(有人会建议走音频队列路线),但是,我想在完成前对样本进行各种操作,并更改歌曲,在播放结束之前重新排列播放顺序等。此外,也许音频单元中存在某些不正确的设置,但是,显示样本的图表(显示样本被错误解码)是直接从缓冲区中获取的,因此我现在只想解决为什么从磁盘读取和解码无法正常工作的问题。现在我只想获得播放过程。
由于我是stackoverflow的新手,所以不能发布图片,因此这里是图片链接:http://i.stack.imgur.com/RHjlv.jpg
代码如下:
这是我设置audioReadSettings用于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];
现在下面的代码清单是一个接收歌曲 persistant_id 的 NSString 的方法:
-(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];
// just getting some basic information about the track to print
NSArray *formatDesc = ((AVAssetTrack*)[[assetReaderOutput audioTracks] objectAtIndex:0]).formatDescriptions;
for (unsigned int i = 0; i < [formatDesc count]; ++i) {
CMAudioFormatDescriptionRef item = (CMAudioFormatDescriptionRef)[formatDesc objectAtIndex:i];
const CAStreamBasicDescription *asDesc = (CAStreamBasicDescription*)CMAudioFormatDescriptionGetStreamBasicDescription(item);
if (asDesc) {
// get data
numChannels = asDesc->mChannelsPerFrame;
sampleRate = asDesc->mSampleRate;
asDesc->Print();
}
}
[self copyEnoughSamplesToBufferForLength:24000];
return YES;
}
以下是函数-(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];
}
现在的样本缓冲区将会有错误解码的样本。有人能帮我解释一下为什么会这样吗?我的iTunes库中不同类型的文件(mp3,aac,wav等)都会出现这种情况。非常感谢任何帮助,如果需要我的代码列表或输出声音,请告诉我,我会根据请求附上它们。我已经花了过去一周的时间来尝试调试它,但在网上没有找到任何帮助-每个人似乎都是按照我的方式做的,但只有我遇到了这个问题。
非常感谢任何帮助!
彼得
#define kUnitSize sizeof(AudioSampleType) #define kBufferUnit 655360 #define kTotalBufferSize kBufferUnit * kUnitSize
- infiniteloop