无法正确反转AVAsset音频。唯一的结果是白噪声

时间:2016-02-23 15:28:56

标签: ios objective-c audio core-audio reverse

我试图反转fruit = 'orange'; namesWorkspace = who; vars = namesWorkspace(~cellfun('isempty',regexpi(namesWorkspace, fruit))); try assignin('base',fruit,evalin('base',vars{1})) catch error('No matching variable name!') end 音频并将其保存到文件中。为了清楚起见,我已经使用问题https://github.com/ksenia-lyagusha/AudioReverse.git

进行了简单的应用

应用程序从bundle中获取AVAsset视频文件,将其作为单个mp4文件导出到沙箱中的Temporary文件夹,然后尝试从那里读取它,反向并保存结果文件。 临时m4a文件正常。

我的反向部分的唯一结果是m4a中带有白噪声的音频文件。

下面是代码的一部分,负责撤销Sandbox。它基于相关问题

但是,它对我没用。

AVAsset

如果我将OSStatus theErr = noErr; UInt64 fileDataSize = 0; AudioFileID inputAudioFile; AudioStreamBasicDescription theFileFormat; UInt32 thePropertySize = sizeof(theFileFormat); theErr = AudioFileOpenURL((__bridge CFURLRef)[NSURL URLWithString:inputPath], kAudioFileReadPermission, 0, &inputAudioFile); thePropertySize = sizeof(fileDataSize); theErr = AudioFileGetProperty(inputAudioFile, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize); UInt32 ps = sizeof(AudioStreamBasicDescription) ; AudioFileGetProperty(inputAudioFile, kAudioFilePropertyDataFormat, &ps, &theFileFormat); UInt64 dataSize = fileDataSize; void *theData = malloc(dataSize); // set up output file AudioFileID outputAudioFile; AudioStreamBasicDescription myPCMFormat; myPCMFormat.mSampleRate = 44100; myPCMFormat.mFormatID = kAudioFormatLinearPCM; // kAudioFormatFlagsCanonical is deprecated myPCMFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved; myPCMFormat.mChannelsPerFrame = 1; myPCMFormat.mFramesPerPacket = 1; myPCMFormat.mBitsPerChannel = 32; myPCMFormat.mBytesPerPacket = (myPCMFormat.mBitsPerChannel / 8) * myPCMFormat.mChannelsPerFrame; myPCMFormat.mBytesPerFrame = myPCMFormat.mBytesPerPacket; NSString *exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"ReverseAudio.caf"]; NSURL *outputURL = [NSURL fileURLWithPath:exportPath]; theErr = AudioFileCreateWithURL((__bridge CFURLRef)outputURL, kAudioFileCAFType, &myPCMFormat, kAudioFileFlags_EraseFile, &outputAudioFile); //Read data into buffer //if readPoint = dataSize, then bytesToRead = 0 in while loop and //it is endless SInt64 readPoint = dataSize-1; UInt64 writePoint = 0; while(readPoint > 0) { UInt32 bytesToRead = 2; AudioFileReadBytes(inputAudioFile, false, readPoint, &bytesToRead, theData); // bytesToRead is now the amount of data actually read UInt32 bytesToWrite = bytesToRead; AudioFileWriteBytes(outputAudioFile, false, writePoint, &bytesToWrite, theData); // bytesToWrite is now the amount of data actually written writePoint += bytesToWrite; readPoint -= bytesToRead; } free(theData); AudioFileClose(inputAudioFile); AudioFileClose(outputAudioFile); 中的文件类型从AudioFileCreateWithURL更改为另一个,则根本不会在kAudioFileCAFType中创建结果文件。

感谢您的帮助。

2 个答案:

答案 0 :(得分:4)

由于您的输入和输出文件格式不兼容,您会收到白噪声。您有不同的采样率和频道,可能还有其他差异。要完成这项工作,您需要在读取和写入之间使用通用(PCM)格式。对于新的(ish)AVAudio框架来说,这是一个合理的工作。我们从文件读取到PCM,随机播放缓冲区,然后从PCM写入文件。这种方法没有针对大型文件进行优化,因为所有数据都可以一次性读入缓冲区,但足以让您入门。

您可以从getAudioFromVideo完成版块调用此方法。为清楚起见,忽略了错误处理。

- (void)readAudioFromURL:(NSURL*)inURL reverseToURL:(NSURL*)outURL {

//prepare the in and outfiles

  AVAudioFile* inFile = 
     [[AVAudioFile alloc] initForReading:inURL error:nil];

  AVAudioFormat* format = inFile.processingFormat;
  AVAudioFrameCount frameCount =(UInt32)inFile.length;
  NSDictionary* outSettings = @{
             AVNumberOfChannelsKey:@(format.channelCount)
            ,AVSampleRateKey:@(format.sampleRate)};

  AVAudioFile* outFile = 
    [[AVAudioFile alloc] initForWriting:outURL
                               settings:outSettings
                                  error:nil];

//prepare the forward and reverse buffers
  self.forwaredBuffer = 
    [[AVAudioPCMBuffer alloc] initWithPCMFormat:format 
                                  frameCapacity:frameCount];
  self.reverseBuffer = 
    [[AVAudioPCMBuffer alloc] initWithPCMFormat:format 
                                  frameCapacity:frameCount];

//read file into forwardBuffer
    [inFile readIntoBuffer:self.forwaredBuffer error:&error];

//set frameLength of reverseBuffer to forwardBuffer framelength
    AVAudioFrameCount frameLength = self.forwaredBuffer.frameLength; 
    self.reverseBuffer.frameLength = frameLength;

//iterate over channels 

     //stride is 1 or 2 depending on interleave format
     NSInteger stride = self.forwaredBuffer.stride;  

    for (AVAudioChannelCount channelIdx = 0;                    
          channelIdx < self.forwaredBuffer.format.channelCount;
          channelIdx++) {
      float* forwaredChannelData = 
          self.forwaredBuffer.floatChannelData[channelIdx];
      float* reverseChannelData = 
          self.reverseBuffer.floatChannelData[channelIdx];
      int32_t reverseIdx = 0;

     //iterate over samples, allocate to reverseBuffer in reverse order   
      for (AVAudioFrameCount frameIdx = frameLength; 
                    frameIdx >0; 
                    frameIdx--) {
           float sample = forwaredChannelData[frameIdx*stride];
           reverseChannelData[reverseIdx*stride] = sample;
           reverseIdx++;
            }
        }

//write reverseBuffer to outFile
        [outFile writeFromBuffer:self.reverseBuffer error:nil];
    }

答案 1 :(得分:0)

我无法在您的代码中找到问题,但我建议您使用AVAsset撤消AVAssetWriter。以下代码基于iOS reverse audio through AVAssetWritet。我在那里添加了其他方法以使其工作。最后我得到了反转文件。

static NSMutableArray *samples;

static OSStatus sampler(CMSampleBufferRef sampleBuffer, CMItemCount index, void *refcon)
{
    [samples addObject:(__bridge id _Nonnull)(sampleBuffer)];
    return noErr;
}

- (void)reversePlayAudio:(NSURL *)inputURL
{
    AVAsset *asset = [AVAsset assetWithURL:inputURL];

    AVAssetReader* reader = [[AVAssetReader alloc] initWithAsset:asset error:nil];

    AVAssetTrack* audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];

    NSMutableDictionary* audioReadSettings = [NSMutableDictionary dictionary];
    [audioReadSettings setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM]
                         forKey:AVFormatIDKey];

    AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:audioReadSettings];
    [reader addOutput:readerOutput];
    [reader startReading];

    NSDictionary *outputSettings = @{AVFormatIDKey     : @(kAudioFormatMPEG4AAC),
                                 AVSampleRateKey       : @(44100.0),
                                 AVNumberOfChannelsKey : @(1),
                                 AVEncoderBitRateKey   : @(128000),
                                 AVChannelLayoutKey    : [NSData data]};

    AVAssetWriterInput *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio
                                                                     outputSettings:outputSettings];

    NSString *exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"reverseAudio.m4a"];

    NSURL *exportURL = [NSURL fileURLWithPath:exportPath];
    NSError *writerError = nil;
    AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:exportURL
                                                      fileType:AVFileTypeAppleM4A
                                                         error:&writerError];
    [writerInput setExpectsMediaDataInRealTime:NO];
    writer.shouldOptimizeForNetworkUse = NO;
    [writer addInput:writerInput];
    [writer startWriting];
    [writer startSessionAtSourceTime:kCMTimeZero];

    CMSampleBufferRef sample;// = [readerOutput copyNextSampleBuffer];
    samples = [[NSMutableArray alloc] init];

    while (sample != NULL) {
        sample = [readerOutput copyNextSampleBuffer];

        if (sample == NULL)
            continue;

        CMSampleBufferCallForEachSample(sample, &sampler, NULL);

        CFRelease(sample);
    }

    NSArray* reversedSamples = [[samples reverseObjectEnumerator] allObjects];

    for (id reversedSample in reversedSamples) {
        if (writerInput.readyForMoreMediaData)  {
            [writerInput appendSampleBuffer:(__bridge CMSampleBufferRef)(reversedSample)];
        }
        else {
            [NSThread sleepForTimeInterval:0.05];
        }
    }

    [samples removeAllObjects];
    [writerInput markAsFinished];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_async(queue, ^{

        [writer finishWritingWithCompletionHandler:^{

            //  writing is finished
            // reversed audio file in TemporaryDirectory in the Sandbox
        }];
    });
}

代码的已知问题。

  1. 如果音频很长,内存可能会出现问题。
  2. 音频文件的持续时间比原始文件长。 (作为快速修复,您可以像往常一样将其剪切掉AVAsset。)