所以,我想對先前錄製的音頻(來自AVAsset)做一個簡單的計算,以創建一個波形可視化。我目前通過對一組樣本進行平均來實現這一點,其大小通過將音頻文件大小除以我想要的波形的分辨率來確定。在AVAssetReader中設置時間範圍導致凍結
這一切工作正常,除了一個問題....它太慢了。在3GS上運行時,處理音頻文件需要花費大約3%的時間來播放音頻文件,這會降低速度(例如,處理1小時的音頻文件需要大約2.5分鐘)。我試圖儘可能優化該方法,但它不起作用。我將發佈我用來處理文件的代碼。也許有人能夠提供幫助,但是我真正想要的是一種處理文件的方法,無需查看每一個字節。所以說,如果分辨率爲2,000,我想訪問該文件並在2000點的每個點上進行抽樣。我認爲這會更快,特別是如果文件較大。但我知道獲取原始數據的唯一方法是以線性方式訪問音頻文件。有任何想法嗎?這裏是我用來處理文件的代碼(注意,所有類變量都以'_'開頭):
所以我完全改變了這個問題。我遲到了,意識到AVAssetReader具有用於「尋找」的timeRange屬性,這正是我所尋找的(見上面的原始問題)。此外,這個問題已經被問到和回答了(我之前沒有找到),我不想重複問題。不過,我仍然有問題。我的應用程序凍結了一段時間,然後最終崩潰,當我嘗試copyNextSampleBuffer
。我不確定發生了什麼事。我似乎沒有在任何類型的遞歸循環中,它從來沒有從函數調用返回。檢查日誌顯示給我這個錯誤:
Exception Type: 00000020
Exception Codes: 0x8badf00d
Highlighted Thread: 0
Application Specific Information:
App[10570] has active assertions beyond permitted time:
{(
<SBProcessAssertion: 0xddd9300> identifier: Suspending process: App[10570] permittedBackgroundDuration: 10.000000 reason: suspend owner pid:52 preventSuspend preventThrottleDownCPU preventThrottleDownUI
)}
我用時間分析器上的應用程序,是的,這只是處理最少量的坐在那裏。無法弄清楚發生了什麼事。請注意,如果不設置AVAssetReader的timeRange屬性,則不會發生這種情況。我已經檢查過,timeRange的值是有效的,但是由於某種原因將其設置爲導致問題。這是我的處理代碼:
- (void) processSampleData{
if (!_asset || CMTimeGetSeconds(_asset.duration) <= 0) return;
NSError *error = nil;
AVAssetTrack *songTrack = _asset.tracks.firstObject;
if (!songTrack) return;
NSDictionary *outputSettingsDict = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,
[NSNumber numberWithInt:16], AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved,
nil];
UInt32 sampleRate = 44100.0;
_channelCount = 1;
NSArray *formatDesc = songTrack.formatDescriptions;
for(unsigned int i = 0; i < [formatDesc count]; ++i) {
CMAudioFormatDescriptionRef item = (__bridge_retained CMAudioFormatDescriptionRef)[formatDesc objectAtIndex:i];
const AudioStreamBasicDescription* fmtDesc = CMAudioFormatDescriptionGetStreamBasicDescription (item);
if(fmtDesc) {
sampleRate = fmtDesc->mSampleRate;
_channelCount = fmtDesc->mChannelsPerFrame;
}
CFRelease(item);
}
UInt32 bytesPerSample = 2 * _channelCount; //Bytes are hard coded by AVLinearPCMBitDepthKey
_normalizedMax = 0;
_sampledData = [[NSMutableData alloc] init];
SInt16 *channels[_channelCount];
char *sampleRef;
SInt16 *samples;
NSInteger sampleTally = 0;
SInt16 cTotal;
_sampleCount = DefaultSampleSize * [UIScreen mainScreen].scale;
NSTimeInterval intervalBetweenSamples = _asset.duration.value/_sampleCount;
NSTimeInterval sampleSize = fmax(100, intervalBetweenSamples/_sampleCount);
double assetTimeScale = _asset.duration.timescale;
CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(0, assetTimeScale), CMTimeMake(sampleSize, assetTimeScale));
SInt16 totals[_channelCount];
@autoreleasepool {
for (int i = 0; i < _sampleCount; i++) {
AVAssetReader *reader = [AVAssetReader assetReaderWithAsset:_asset error:&error];
AVAssetReaderTrackOutput *trackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:songTrack outputSettings:outputSettingsDict];
[reader addOutput:trackOutput];
reader.timeRange = timeRange;
[reader startReading];
while (reader.status == AVAssetReaderStatusReading) {
CMSampleBufferRef sampleBufferRef = [trackOutput copyNextSampleBuffer];
if (sampleBufferRef){
CMBlockBufferRef blockBufferRef = CMSampleBufferGetDataBuffer(sampleBufferRef);
size_t length = CMBlockBufferGetDataLength(blockBufferRef);
int sampleCount = length/bytesPerSample;
for (int i = 0; i < sampleCount ; i += _channelCount) {
CMBlockBufferAccessDataBytes(blockBufferRef, i * bytesPerSample, _channelCount, channels, &sampleRef);
samples = (SInt16 *)sampleRef;
for (int channel = 0; channel < _channelCount; channel++)
totals[channel] += samples[channel];
sampleTally++;
}
CMSampleBufferInvalidate(sampleBufferRef);
CFRelease(sampleBufferRef);
}
}
for (int i = 0; i < _channelCount; i++){
cTotal = abs(totals[i]/sampleTally);
if (cTotal > _normalizedMax) _normalizedMax = cTotal;
[_sampledData appendBytes:&cTotal length:sizeof(cTotal)];
totals[i] = 0;
}
sampleTally = 0;
timeRange.start = CMTimeMake((intervalBetweenSamples * (i + 1)) - sampleSize, assetTimeScale); //Take the sample just before the interval
}
}
_assetNeedsProcessing = NO;
}