Hello! I've been working on and off on a project for Bela that (for now) would simply be a compact audio recording device. I like to take my modular setup or other instruments out to coastal views, redwoods, etc. with a portable power source, so not having to also bring my laptop and interface is desirable.
I know this could be more easily done with PD or something, but I'm doing it in C++ for the learning experience.
For now, I'm starting simple. My code thus far is mostly a hybrid of the sinetone example and a modified version of the sample streamer example; now it is writing audio files rather than reading them. The only external file being used is a header file from the examples defining the SampleData struct.
The issue I've been stuck on for a while now is that only the last iteration through my buffer is being written to the file, and everything prior is silence. Here's a screencap of a 2 second file generated by the program in a Max/MSP buffer.
I'm sure it's some small mistake being made on my part. I've shifted things around in the code several times to see if things were just happening in the wrong order or something, but while I made some small efficiency and fixes along the way, so far the audio file output has not changed. My code is attached below. Any insight is appreciated!
#include <Bela.h>
#include <cmath>
#include <SampleData.h>
#include <sndfile.h>
#include <cstdlib>
#define NUM_CHANNELS 1 // NUMBER OF CHANNELS IN THE FILE
#define BUFFER_LEN 22050 // BUFFER LENGTH
float gFrequency = 440.0;
float gPhase;
float gInverseSampleRate;
float gDuration = 2 * 44100;
int gCount = 0;
// figure out how to pass vars to AuxiliaryTask / use less globals
// name of file generated
SampleData gSampleBuf[2][NUM_CHANNELS];
//index value of buffer array
int gPos = 0;
//start on second buffer bc it will switch on first run thru
int gActiveBuffer = 1;
int gDoneLoadingBuffer = 1;
int gChunk = 0;
AuxiliaryTask gFillBufferTask;
void writeFile(float *buf, int startSamp, int endSamp){
SF_INFO sfinfo ;
sfinfo.channels = NUM_CHANNELS;
sfinfo.samplerate = 44100;
sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
const char* path = "./test.wav";
//calculate duration of write operation - wouldn't this just be BUFFER_LEN?
int frameLen = endSamp - startSamp;
//open sndfile pointer??
// sf_open(filepath, mode, pointer to sfinfo)
// mode can be read, write, or read/write
SNDFILE * outfile = sf_open(path, SFM_WRITE, &sfinfo);
sf_seek(outfile, startSamp, SEEK_SET);
//to use sf_write_xxxx functions, you must declare it as an sf_count variable
//because it returns the number of samples/frames written (but this value does not
//necessarily need to be used)
// sf_write_float(pointer to file opened by sf_open, array start, array length)
sf_count_t count = sf_write_float(outfile, &buf[0], frameLen);
//i think this writes the appropriate header info for the file?
// ie non-audio data for wav, aiff, etc
sf_write_sync(outfile);
//closes file
sf_close(outfile);
}
void fillBuffer(void*) {
int end = gChunk + BUFFER_LEN;
writeFile(gSampleBuf[!gActiveBuffer][0].samples, gChunk, end);
printf("writing file at %d - %d\n", gChunk, end);
//signal the file is written
gDoneLoadingBuffer = 1;
//increment by BUFFER_LEN
gChunk += BUFFER_LEN;
}
bool setup(BelaContext *context, void *userData) {
//initialize sine vars
gInverseSampleRate = 1.0 / context->audioSampleRate;
gPhase = 0.0;
//initialize auxiliary task
if((gFillBufferTask = Bela_createAuxiliaryTask(&fillBuffer, 90, "fill-buffer")) == 0) {
return false;
}
//initialize sample data struct with buffers/arrays
for(int ch=0;ch<NUM_CHANNELS;ch++) {
for(int i=0;i<2;i++) {
gSampleBuf[i][ch].sampleLen = BUFFER_LEN;
gSampleBuf[i][ch].samples = new float[BUFFER_LEN];
}
}
return true;
}
void render(BelaContext *context, void *userData) {
for(unsigned int n = 0; n < context->audioFrames; n++) {
//generate sine wave
float out = 0.8f * sinf(gPhase);
gPhase += 2.0f * (float)M_PI * gFrequency * gInverseSampleRate;
if(gPhase > M_PI) {
gPhase -= 2.0f * (float)M_PI;
}
//swap buffers when gPos reaches BUFFER_LEN
if(gPos == 0) {
if(0 == gDoneLoadingBuffer && gCount <= gDuration && gCount > 0) {
printf("increase buffer size!\n");
}
gDoneLoadingBuffer = 0;
gActiveBuffer = !gActiveBuffer;
if (gCount < gDuration) {
Bela_scheduleAuxiliaryTask(gFillBufferTask);
}
}
//if samples counted are more than duration, mute, otherwise output sine and write file
if (gCount > gDuration) {
for(unsigned int channel = 0; channel < context->audioOutChannels; channel++) {
audioWrite(context, n, channel, 0);
}
} else {
gSampleBuf[gActiveBuffer][0].samples[gPos] = out;
for(unsigned int channel = 0; channel < context->audioOutChannels; channel++) {
audioWrite(context, n, channel, out);
}
}
gCount++;
gPos = gCount%BUFFER_LEN;
}
}
void cleanup(BelaContext *context, void *userData) {
// Delete the allocated buffers
for(int ch=0;ch<NUM_CHANNELS;ch++) {
for(int i=0;i<2;i++) {
delete[] gSampleBuf[i][ch].samples;
}
}
}
```