Ios: How to Read an Audio File into a Float Buffer

How can I generate an array of floats from an audio file in Swift

AVAudioFile built-in to iOS (and OS X), is very convenient and will also do format conversions for you:

import AVFoundation
// ...

let url = NSBundle.mainBundle().URLForResource("your audio file", withExtension: "wav")
let file = try! AVAudioFile(forReading: url!)
let format = AVAudioFormat(commonFormat: .PCMFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: 1, interleaved: false)

let buf = AVAudioPCMBuffer(PCMFormat: format, frameCapacity: 1024)
try! file.readIntoBuffer(buf)

// this makes a copy, you might not want that
let floatArray = Array(UnsafeBufferPointer(start: buf.floatChannelData[0], count:Int(buf.frameLength)))

print("floatArray \(floatArray)\n")

Sadly, for doubles it doesn't seem to be enough to substitute .PCMFormatFloat32 with .PCMFormatFloat64 because AVAudioPCMBuffer doesn't have a float64ChannelData method.

update because I don't know swift well

You can avoid copying the array by working with the UnsafeBufferPointer, which is a perfectly good collection type:

let floatArray = UnsafeBufferPointer(start: buf.floatChannelData[0], count:Int(buf.frameLength))

iOS float buffer to audio playback

Thanks @jaybers I followed a similar approach for the solution. Apologies for not posting it earlier.

Solution:
1) I built/coded the header for playback (PCM as its easiest) in a BYTE * type array.
2) Appended the header to a NSDATA array.
3) appended the float data to NSDATA array
4) played it using AVAudioPlayer ....

An issue I faced : My data was 16bits while float is 32 bit in IOS so playing it as 16bit PCM / 32 bit PCM was introducing noise - I think because of the extra zeros. So i transferred the float data into short datatype array and appended that to the NSDATA and played it as 16bit-PCM - perfectly played.

Codes:

//mynewdata1 is a short datatype 16bit array

NSData *Wave1= [NSMutableData dataWithData:mynewdata1];

unsigned long totalAudioLen=[Wave1 length];
unsigned long totalDataLen = totalAudioLen + 44;
unsigned long longSampleRate = 4*11025.0;
unsigned int channels = 1;
unsigned long byteRate = (16 * longSampleRate * channels)/8;

Byte *header = (Byte*)malloc(44);
header[0] = 'R'; // RIFF/WAVE header
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (Byte) (totalDataLen & 0xff);
header[5] = (Byte) ((totalDataLen >> 8) & 0xff);
header[6] = (Byte) ((totalDataLen >> 16) & 0xff);
header[7] = (Byte) ((totalDataLen >> 24) & 0xff);
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
header[12] = 'f'; // 'fmt ' chunk
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
header[16] = 16; // 4 bytes: size of 'fmt ' chunk
header[17] = 0;
header[18] = 0;
header[19] = 0;
header[20] = 1; // format = 1 for pcm and 2 for byte integer
header[21] = 0;
header[22] = (Byte) channels;
header[23] = 0;
header[24] = (Byte) (longSampleRate & 0xff);
header[25] = (Byte) ((longSampleRate >> 8) & 0xff);
header[26] = (Byte) ((longSampleRate >> 16) & 0xff);
header[27] = (Byte) ((longSampleRate >> 24) & 0xff);
header[28] = (Byte) (byteRate & 0xff);
header[29] = (Byte) ((byteRate >> 8) & 0xff);
header[30] = (Byte) ((byteRate >> 16) & 0xff);
header[31] = (Byte) ((byteRate >> 24) & 0xff);
header[32] = (Byte) (16*1)/8; // block align
header[33] = 0;
header[34] = 16; // bits per sample
header[35] = 0;
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (Byte) (totalAudioLen & 0xff);
header[41] = (Byte) ((totalAudioLen >> 8) & 0xff);
header[42] = (Byte) ((totalAudioLen >> 16) & 0xff);
header[43] = (Byte) ((totalAudioLen >> 24) & 0xff);

NSData *headerData = [NSData dataWithBytes:header length:44];
NSMutableData * soundFileData1 = [NSMutableData alloc];
[soundFileData1 appendData:headerData];
[soundFileData1 appendData:Wave1];

self.avap1 = [[AVAudioPlayer alloc] initWithData:soundFileData1 fileTypeHint:@"wav" error:&error1];
[self.avap3 play]; //to play

ExtAudioFile into a float buffer produces zeros

You're setting up your ExtAudioFile and its client format, but you're not actually reading from it (with ExtAudioFileRead), so your "output" is actually uninitialised, and in your case, very small.

Writing array of floats to audio file

Scrap the code that I posted in the question. I found the solution here: How to write array of float values to audio file in Core Audio?. Look at the first answer. I went to the given link, downloaded the source code, then added EAFWrite.h and EAFWrite.mm to my project. The class assumes that the audio will be read from multiple buffers (ie: a 2D array), but needed it to work with a 1D array, so I modified the function writeToFloats as follows:

-(OSStatus) writeFloats:(long)numFrames fromArray:(float *)data
{
OSStatus err = noErr;

if (!data) return -1;
if (!numFrames) return -1;

AudioBufferList *abl = AllocateAudioBufferList(mStreamFormat.mChannelsPerFrame, numFrames*sizeof(short));
if (!abl) return -1;

abl->mBuffers[0].mNumberChannels = 1;
abl->mBuffers[0].mDataByteSize = numFrames*sizeof(short);
short *buffer = (short*)abl->mBuffers[0].mData;
for (long v = 0; v < numFrames; v++) {
if (data[v] > 0.999)
data[v] = 0.999;
else if (data[v] < -1)
data[v] = -1;
buffer[v] = (short)(data[v]*32768.f);
}

abl->mBuffers[0].mData = buffer;

err = ExtAudioFileWrite(mOutputAudioFile, numFrames, abl);

DestroyAudioBufferList(abl);

if(err != noErr)
{
char formatID[5];
*(UInt32 *)formatID = CFSwapInt32HostToBig(err);
formatID[4] = '\0';
fprintf(stderr, "ExtAudioFileWrite FAILED! %d '%-4.4s'\n",(int)err, formatID);
return err;
}

return err;

}

And to call this function you need:

NSString *outputPath = @"outputFile.caf"
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager createFileAtPath:outputPath contents:nil attributes:nil];
NSURL* fileURL = [NSURL URLWithString:outputPath];

EAFWrite *writer = [[EAFWrite alloc] init];
[writer openFileForWrite:fileURL sr:44100.0 channels:1 wordLength:32 type:kAudioFileCAFType];
[writer writeFloats:_recordingLength fromArray:_recordingSamples];

Write array of floats to a wav audio file in swift

With a great colleague help we've managed to get it to work. Apparently, AudioPCMBuffer after filling also needs to be notified about it's new size.
Also i was using totally wrong formats.

Here is the code:

let SAMPLE_RATE =  Float64(16000.0)

let outputFormatSettings = [
AVFormatIDKey:kAudioFormatLinearPCM,
AVLinearPCMBitDepthKey:32,
AVLinearPCMIsFloatKey: true,
// AVLinearPCMIsBigEndianKey: false,
AVSampleRateKey: SAMPLE_RATE,
AVNumberOfChannelsKey: 1
] as [String : Any]

let audioFile = try? AVAudioFile(forWriting: url, settings: outputFormatSettings, commonFormat: AVAudioCommonFormat.pcmFormatFloat32, interleaved: true)

let bufferFormat = AVAudioFormat(settings: outputFormatSettings)

let outputBuffer = AVAudioPCMBuffer(pcmFormat: bufferFormat, frameCapacity: AVAudioFrameCount(buff.count))

// i had my samples in doubles, so convert then write

for i in 0..<buff.count {
outputBuffer.floatChannelData!.pointee[i] = Float( buff[i] )
}
outputBuffer.frameLength = AVAudioFrameCount( buff.count )

do{
try audioFile?.write(from: outputBuffer)

} catch let error as NSError {
print("error:", error.localizedDescription)
}

Get audio floats from AudioQueueBufferRef output queue callback

Try replacing:

let numSamples = array.count / MemoryLayout<Int16>.size //2

By:

let numSamples = array.count

I believe that your float buffers are half the expected size, otherwise.

Here is a modified, standalone version of a subset of your code, that I used for testing:

import Foundation
import Accelerate

let size = 7
var data : [Int16] = [ 0, 1, 2, 3, 4, -Int16.max, Int16.max ]

let array = Array(UnsafeMutableBufferPointer(start: &data, count: size))

var floatsArr: [Float] = Array(repeating: 0.0, count: array.count)
var scaledFloats: [Float] = Array(repeating: 0.0, count: array.count)

vDSP_vflt16(array, 1, &floatsArr, 1, vDSP_Length(array.count))

var factor = Float(Int16.max)
vDSP_vsdiv(floatsArr, 1, &factor, &scaledFloats, 1, vDSP_Length(array.count))

print( array[0 ... array.count - 1], array.count, "int16")
print( floatsArr[0 ... floatsArr.count - 1], floatsArr.count, "float")
print(scaledFloats[0 ... scaledFloats.count - 1], scaledFloats.count, "scaled")

And its output:

[0, 1, 2, 3, 4, -32767, 32767] 7 int16
[0.0, 1.0, 2.0, 3.0, 4.0, -32767.0, 32767.0] 7 float
[0.0, 3.05185094e-05, 6.10370189e-05, 9.15555283e-05, 0.000122074038, -1.0, 1.0] 7 scaled


Related Topics



Leave a reply



Submit