iOS:如何将音频文件读入浮动缓冲区

时间:2011-09-24 06:45:51

标签: ios file core-audio

我有一个非常短的音频文件,比如说(例如).PCM格式的十分之一秒

我想使用RemoteIO重复循环播放文件以产生连续的音乐音调。那么如何将其读入浮点数组呢?

编辑:虽然我可以挖出文件格式,将文件解压缩到NSData并手动处理,我猜测有一种更明智的通用方法......(例如应对不同的格式)

4 个答案:

答案 0 :(得分:17)

您可以使用ExtAudioFile以多种客户端格式从任何支持的数据格式读取数据。以下是将文件读取为16位整数的示例:

CFURLRef url = /* ... */;
ExtAudioFileRef eaf;
OSStatus err = ExtAudioFileOpenURL((CFURLRef)url, &eaf);
if(noErr != err)
  /* handle error */

AudioStreamBasicDescription format;
format.mSampleRate = 44100;
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kAudioFormatFormatFlagIsPacked;
format.mBitsPerChannel = 16;
format.mChannelsPerFrame = 2;
format.mBytesPerFrame = format.mChannelsPerFrame * 2;
format.mFramesPerPacket = 1;
format.mBytesPerPacket = format.mFramesPerPacket * format.mBytesPerFrame;

err = ExtAudioFileSetProperty(eaf, kExtAudioFileProperty_ClientDataFormat, sizeof(format), &format);

/* Read the file contents using ExtAudioFileRead */

如果您想要Float32数据,可以像这样设置format

format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
format.mBitsPerChannel = 32;

答案 1 :(得分:5)

我不熟悉RemoteIO,但我对WAV很熟悉,并且认为我会发布一些格式信息。如果需要,您应该能够轻松解析持续时间,比特率等信息......

首先,这是一个详细介绍WAVE PCM soundfile format的优秀网站。该站点也很好地说明了“fmt”子块中的不同字节地址所指的内容。

WAVE文件格式

  • WAVE由“RIFF”块和后续子块组成
  • 每个块至少为8个字节
  • 前4个字节是块ID
  • 接下来的4个字节是块大小(块大小给出了块的其余部分的大小,不包括用于块ID和块大小的8个字节)
  • 每个WAVE都有以下块/子块
    • “RIFF”(第一个也是唯一的一块。其余的都是技术上的子块。)
    • “fmt”(通常是“RIFF”之后的第一个子块,但可以是“RIFF”和“data”之间的任何一个。此块有关于WAV的信息,如通道数,采样率和字节速率)
    • “data”(必须是最后一个子块并包含所有声音数据)

常见的WAVE音频格式:

  • PCM
  • IEEE_Float
  • PCM_EXTENSIBLE(子格式为PCM或IEEE_FLOAT)

WAVE持续时间和大小

WAVE文件的持续时间可以按如下方式计算:

seconds = DataChunkSize / ByteRate

其中

ByteRate = SampleRate * NumChannels * BitsPerSample/8

和DataChunkSize不包括为“数据”子块的ID和大小保留的8个字节。

知道了这一点,如果你知道WAV和ByteRate的持续时间,就可以计算出DataChunkSize。

DataChunkSize = seconds * ByteRate

这对于从mp3或wma等格式转换时计算wav数据的大小非常有用。请注意,典型的 wav标头是44个字节,后跟DataChunkSize(如果使用Normalizer工具转换wav,则总是如此 - 至少在撰写本文时)。

答案 2 :(得分:5)

这是我用来将音频数据(音频文件)转换为浮点表示并保存到数组中的代码。

-(void) PrintFloatDataFromAudioFile {

NSString *  name = @"Filename";  //YOUR FILE NAME
NSString * source = [[NSBundle mainBundle] pathForResource:name ofType:@"m4a"]; // SPECIFY YOUR FILE FORMAT

const char *cString = [source cStringUsingEncoding:NSASCIIStringEncoding];

CFStringRef str = CFStringCreateWithCString(
                                            NULL,
                                            cString,
                                            kCFStringEncodingMacRoman
                                            );
CFURLRef inputFileURL = CFURLCreateWithFileSystemPath(
                                                      kCFAllocatorDefault,
                                                      str,
                                                      kCFURLPOSIXPathStyle,
                                                      false
                                                      );

ExtAudioFileRef fileRef;
ExtAudioFileOpenURL(inputFileURL, &fileRef);


  AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100;   // GIVE YOUR SAMPLING RATE 
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
audioFormat.mBitsPerChannel = sizeof(Float32) * 8;
audioFormat.mChannelsPerFrame = 1; // Mono
audioFormat.mBytesPerFrame = audioFormat.mChannelsPerFrame * sizeof(Float32);  // == sizeof(Float32)
audioFormat.mFramesPerPacket = 1;
audioFormat.mBytesPerPacket = audioFormat.mFramesPerPacket * audioFormat.mBytesPerFrame; // = sizeof(Float32)

// 3) Apply audio format to the Extended Audio File
ExtAudioFileSetProperty(
                        fileRef,
                        kExtAudioFileProperty_ClientDataFormat,
                        sizeof (AudioStreamBasicDescription), //= audioFormat
                        &audioFormat);

int numSamples = 1024; //How many samples to read in at a time
UInt32 sizePerPacket = audioFormat.mBytesPerPacket; // = sizeof(Float32) = 32bytes
UInt32 packetsPerBuffer = numSamples;
UInt32 outputBufferSize = packetsPerBuffer * sizePerPacket;

// So the lvalue of outputBuffer is the memory location where we have reserved space
UInt8 *outputBuffer = (UInt8 *)malloc(sizeof(UInt8 *) * outputBufferSize);



AudioBufferList convertedData ;//= malloc(sizeof(convertedData));

convertedData.mNumberBuffers = 1;    // Set this to 1 for mono
convertedData.mBuffers[0].mNumberChannels = audioFormat.mChannelsPerFrame;  //also = 1
convertedData.mBuffers[0].mDataByteSize = outputBufferSize;
convertedData.mBuffers[0].mData = outputBuffer; //

UInt32 frameCount = numSamples;
float *samplesAsCArray;
int j =0;
    double floatDataArray[882000]   ; // SPECIFY YOUR DATA LIMIT MINE WAS 882000 , SHOULD BE EQUAL TO OR MORE THAN DATA LIMIT

while (frameCount > 0) {
    ExtAudioFileRead(
                     fileRef,
                     &frameCount,
                     &convertedData
                     );
    if (frameCount > 0)  {
        AudioBuffer audioBuffer = convertedData.mBuffers[0];
        samplesAsCArray = (float *)audioBuffer.mData; // CAST YOUR mData INTO FLOAT

       for (int i =0; i<1024 /*numSamples */; i++) { //YOU CAN PUT numSamples INTEAD OF 1024

            floatDataArray[j] = (double)samplesAsCArray[i] ; //PUT YOUR DATA INTO FLOAT ARRAY
              printf("\n%f",floatDataArray[j]);  //PRINT YOUR ARRAY'S DATA IN FLOAT FORM RANGING -1 TO +1
            j++;


        }
    }
}}

答案 3 :(得分:2)

<块引用>

Swift 5 更新

这是一个简单的函数,可帮助您将音频文件放入浮点数组中。这适用于单声道和立体声音频,要获得立体声音频的第二个通道,只需取消注释示例 2


import AVFoundation

//..

do {
    guard let url = Bundle.main.url(forResource: "audio_example", withExtension: "wav") else { return }
    let file = try AVAudioFile(forReading: url)
    if let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: file.fileFormat.channelCount, interleaved: false), let buf = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(file.length)) {

        try file.read(into: buf)
        guard let floatChannelData = buf.floatChannelData else { return }
        let frameLength = Int(buf.frameLength)
        
        let samples = Array(UnsafeBufferPointer(start:floatChannelData[0], count:frameLength))
//        let samples2 = Array(UnsafeBufferPointer(start:floatChannelData[1], count:frameLength))
        
        print("samples")
        print(samples.count)
        print(samples.prefix(10))
//        print(samples2.prefix(10))
    }
} catch {
    print("Audio Error: \(error)")
}