Sun Jul 6, 2014
Working with audio recordings has several pain points associated with them, which I’ll talk about below. The problems get compounded when you’re working on devices with limited capacities like mobile phones.
Back to the problems:
RAW audio data are huge. A 3 Seconds recording sampled @ 44100 is about 529 KB in size. Storing it permanently in iphone/android/ipad wouldn’t be a good idea. See (http://stackoverflow.com/questions/22292677/apps-must-follow-the-ios-data-storage-guidelines-or-they-will-be-rejected-in-app)
In OpenFrameworks, as of now there isn’t any great solution to converting audio data to compressed format. Or even uncompressed format for that matter. See (https://github.com/openframeworks/openFrameworks/issues/2511)
One solution is to simply upload the raw audio data and let the server decide the correct treatment.
Openframeworks App Setup
With the following setup, we’re recording audio sampled @ 16000 for 3 seconds. The samples are accumulated in the buffer periodically. For details, checkout this book. (http://books.google.com.au/books?id=WRr2AAAAQBAJ)
void ofApp::setup() {
sampleRate = 16000;
duration = 3;
recPos = 0;
size = duration * sampleRate
isRecording = false;
buffer.resize(size, 0.0); //buffer is a vector<float>
//Note: these are some low fidelity settings.
//You'd want to sample at least @44100 with 2 output channels and 4 buffers
ofSoundStreamSetup(1, 1, this, sampleRate, 512, 1);
}
void ofApp::audioIn(float * input, int bufferSize, int nChannels){
if (!isRecording) {
return;
}
for (int i=0; i<bufferSize; i++) {
buffer[recPos] = input[i];
recPos++;
recPos %= size;
}
}
Turning on/off the recording
void ofApp::startListening() {
std::fill(buffer.begin(), buffer.end(), 0)
isRecording = true
}
void ofApp::stopListening() {
isRecording = false;
//Copy the vector into a buffer
int size = sizeof(float) * buffer.size();
float *myBuffer = new float[size];
memcpy(myBuffer, &buffer[0], size);
uploadSound(myBuffer, size);
delete [] myBuffer;
}
Uploading sound with ASIHTTPRequest
We’ll use POST request to send the raw data to the server.
void ofApp::uploadSound(float *input, int bufferSize) {
NSData *data = [[NSMutableData alloc] initWithBytes:input length:bufferSize];
ASIFormDataRequest *dataReq =
[ASIFormDataRequest requestWithURL:
[NSURL URLWithString:@"http://192.168.0.7:3000/rawsound/sample"]];
[dataReq setData:data
withFileName:@"audio"
andContentType:nil forKey:@"audio"];
[dataReq setCompletionBlock:^{
NSLog(@"Success.");
}];
[dataReq setFailedBlock:^{
NSLog(@"POST Error: %@", dataReq.error);
}];
[dataReq startAsynchronous];
}
That’s for the client side. You’d still have to process the request in the sever.
Server Side - GO
func UploadRawSound(r *http.Request, params martini.Params) string {
file, _, err := r.FormFile("audio")
defer file.Close()
if err != nil {
return "500"
}
//Save a raw file
out, err := os.Create("upload/sample.raw")
defer out.Close()
if err != nil {
return "500"
}
_, err = io.Copy(out, file)
if err != nil {
return "500"
}
return "200"
}
That’s it. Now you can import the sample.raw file in Audacity with the following settings:
32 Bit Float
Little Endian
Sample Rate: 16000
Channels: 1
Also depicted in the image below: