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

标签 ios objective-c audio core-audio reverse

我正在尝试反转 AVAsset 音频并将其保存到文件中。为了清楚起见,我对问题 https://github.com/ksenia-lyagusha/AudioReverse.git 做了简单的应用程序

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

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

下面的代码部分负责反转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 更改为另一个,则根本不会在 Sandbox 中创建结果文件。

感谢您的帮助。

最佳答案

您会收到白噪音,因为您的输入和输出文件格式不兼容。您有不同的采样率和 channel ,可能还有其他差异。要实现此功能,您需要在读取和写入之间使用通用 (PCM) 格式进行调解。对于新的 AVAudio 框架来说,这是一项合理的工作。我们从文件读取到 PCM,对缓冲区进行洗牌,然后从 PCM 写入文件。这种方法并未针对大文件进行优化,因为所有数据都会一次性读入缓冲区,但足以让您入门。

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

- (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];
    }

关于ios - 无法正确反转 AVAsset 音频。唯一的结果是白噪声,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35581531/

相关文章:

ios - 重复播放 3 秒的电影以获取帮助?

objective-c - 在 NSTextField 内绘制固定符号

matlab - 使用Matlab检测两个音频文件中多次点击声音之间的延迟

IOS 如何使用按钮发送消息?

ios - SwiftUI NavigationView 在滚动时折叠

iOS Swift 在 View 关闭时停止 MoviePlayer

objective-c - 如何以编程方式获取 MacBook 的型号?

ios - UITabBarControllerDelegate 方法未被调用

android - Ionic 2 Native Audio在Android上没有声音

c# - 如何获得默认音频设备?