我有一个非常短的音频文件,比如说10分之一秒,格式可能是.PCM。
我想使用RemoteIO循环播放该文件,以产生连续的音乐音调。那么,我该如何将其读入一个浮点数数组中?
编辑:虽然我可以挖掘文件格式,将文件提取到NSData中并手动处理它,但我猜想有一个更明智的通用方法...(例如能够处理不同的格式)。
我有一个非常短的音频文件,比如说10分之一秒,格式可能是.PCM。
我想使用RemoteIO循环播放该文件,以产生连续的音乐音调。那么,我该如何将其读入一个浮点数数组中?
编辑:虽然我可以挖掘文件格式,将文件提取到NSData中并手动处理它,但我猜想有一个更明智的通用方法...(例如能够处理不同的格式)。
您可以使用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;
ExtAudioFileSetProperty(...)
设置格式?如果可以,那么这是否会对数据进行某种转换,因为WAV文件可能与指定的格式不同?老实说,我很想知道。在你发布之前,我不知道扩展音频文件服务,所以我没有任何经验。 - SamExtAudioFile
是一个包装器,它将 AudioFile
和 AudioConverter
结合在一起。无论客户端格式设置为什么,都将在对 ExtAudioFileRead
和 ExtAudioFileWrite
的调用中使用,并且内部的 AudioConverter
将从客户端格式转换为文件的数据格式。 - sbooth这是我用来将音频数据(音频文件)转换为浮点表示并保存到数组中的代码。
-(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++;
}
}
}}
我不熟悉RemoteIO,但我熟悉WAV格式,并想分享一些格式信息。如果需要,您应该可以轻松地解析出持续时间、比特率等信息...
首先,这里有一个非常好的网站,详细介绍了WAVE PCM声音文件格式。该网站还很好地说明了“fmt”子块内部不同字节地址所指的内容。
可以通过以下方式计算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,则始终如此-至少在本文撰写之时)。
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)")
}