Hi all,

I’m still relatively new to Bela and still getting familiar with the programming, so I’m learning as I go at the moment. I’m currently trying to record audio from a lav mic onto Bela utilising the libsndfile API to write to a .WAV file. The .WAV file is being created in the path and the code compiles/runs without error but unfortunately I do not have anything picked up from the microphone and the WAV file is empty. Just to note that no pure data is being used.

At present the mic is plugged into the 3.5mm jack audio adapter cable connected to the stereo audio input on the board. Here is the Lavalier mic that I’m using at the moment: http://www.boya-mic.com/lavaliermicrophones/BY-M1.html

At present I can’t get it to function as intended, so I’m currently slightly stuck on this issue. I know its probably some code that I’m missing.

I was also wondering the best approach with regards to code, for recording from more than the one microphone using the audio expander capelet?

Any help or suggestions would be greatly appreciated. Many thanks.

The current code that I’m using for testing is outlined below for reference:

#include <Bela.h>
#include <cmath>
#include <SampleData.h>
#include <libraries/sndfile/sndfile.h>
#include <cstdlib>

#define NUM_CHANNELS 2    // NUMBER OF CHANNELS IN THE FILE
#define BUFFER_LEN 2048   // BUFFER LENGTH

float bLSort[BUFFER_LEN*2];
int gCount = 1;

float gDuration = 10 * (60 * 44100); //longer duration recording (minutes)

float gFrequency = 440.0;
float gPhase;
float gInverseSampleRate;

// name of file generated
SampleData gSampleBuf[2][NUM_CHANNELS];
//index value of buffer array
int gPos = 1;

//start on second buffer because it will switch on first run through
int gActiveBuffer = 0;
int gDoneLoadingBuffer = 1;
int gChunk = 0;

/////

AuxiliaryTask gFillBufferTask;

SF_INFO sfinfo;

sf_count_t count;

SNDFILE * outfile;


const char* path = "./recording.wav";


void setupFile() {
    sfinfo.channels = NUM_CHANNELS;
    sfinfo.samplerate = 44100;
    sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
}

void closeFile(void*) {
    sf_write_sync(outfile);
    sf_close(outfile);
    printf(".wav file written and closed\n");
}

void writeFile(float *buf, int startSamp, int endSamp){
    //calculate duration of write operation
    int frameLen = endSamp - startSamp;

	sf_write_float(outfile, &buf[0], frameLen);
}

void fillBuffer(void*) {

    int end = gChunk + (BUFFER_LEN*2);

    for (size_t i = 0; i < BUFFER_LEN; i++) {
        bLSort[i*2] = gSampleBuf[!gActiveBuffer][0].samples[i];
        bLSort[(i*2)+1] = gSampleBuf[!gActiveBuffer][1].samples[i];
    }

    writeFile(bLSort, gChunk, end);

    //signal the file is written
    gDoneLoadingBuffer = 1;
    //increment by BUFFER_LEN
    gChunk += BUFFER_LEN;
}

bool setup(BelaContext *context, void *userData) {

	
    outfile = sf_open(path, SFM_WRITE, &sfinfo);
	
    //initialise sine variables
    gInverseSampleRate = 1.0 / context->audioSampleRate;
    gPhase = 0.0;
    
    //initialize auxiliary task
    if((gFillBufferTask = Bela_createAuxiliaryTask(&fillBuffer, 90, "fill-buffer")) == 0) {
        return false;
    }

    setupFile(); 

    //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++) {
        
        //Update wrap phase and 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(!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;
        }
    }
    
}

This is probably more complex than it needs to be.
You are never closing the file, that may be the reason why it is empty? Does the fillBuffer() function ever run?

Anyhow, this seems more complex than it could be. You could use a Pipe to send data reliably from the audio thread to the other thread. See here the documentation.

An example (untested) would be something like this:

#include <Bela.h>
#include <libraries/Pipe/Pipe.h>
#include <libraries/sndfile/sndfile.h>

const char* path = "./recording.wav";

AuxiliaryTask gFillBufferTask;
SNDFILE * outfile;
unsigned int gAudioFrames;
unsigned int gAudioInChannels;
float gAudioSampleRate;

void openFile() {
    SF_INFO sfinfo;
    sfinfo.channels = gAudioInChannels;
    sfinfo.samplerate = gAudioSampleRate;
    sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
    outfile = sf_open(path, SFM_WRITE, &sfinfo);
}

void closeFile() {
    sf_write_sync(outfile);
    sf_close(outfile);
    printf(".wav file written and closed\n");
}

void writeBuffer(void*) {
  unsigned int numItems = gAudioFrames * gAudioInChannels;
  float buf[numItems];
  while((ret = pipe.readNonRt(buf, numItems) ) > 0)
  {
    sf_write_float(outfile, &buf[0], ret);
  }
}

bool setup(BelaContext* context, void* arg)
{
  gAudioSampleRate = context->audioSampleRate;
  gAudioFrames = context->audioFrames;
  gAudioInChannels = context->audioInChannels;
  pipe.setup("sndfile-write", 65536, false, false);
  setupFile();
  if((gFillBufferTask = Bela_createAuxiliaryTask(&writeBuffer, 90, "writeBuffer")) == 0) {
    return false;
  }

}

void render(BelaContext* context, void* arg)
{
  // context->audioIn is the float* that points to all the input samples, stored as interleaved channels)
  pipe.writeRt(context->audioIn, context->audioFrames * context->audioInChannels);
  Bela_scheduleAuxiliaryTask(gFillBufferTask);
}

void cleanup(BelaContext* context, void* arg)
{
    closeFile();
}

Hi Giulio, Thank you for getting back to me and for the code example. It seems you are correct I hadn't actually closed the file but as you say the code is probably more complex than it needs to be.

Unfortunately with the untested code that you sent over, I receive an error stating:
member reference base type 'int (int *) throw()' is not a structure or union column

for the following three lines: pipe.setup("sndfile-write", 65536, false, false);,
while((ret = pipe.readNonRt(buf, numItems) ) > 0)
and pipe.writeRt(context->audioIn, context->audioFrames * context->audioInChannels);.
I haven't used Pipe to send data before, so not too sure how to resolve the issue in order to test this approach.

ok that was a naming conflict between the pipe variable and unistd.h's pipe, brought in by Bela.h.
this compiles

#include <Bela.h>
#include <libraries/Pipe/Pipe.h>
#include <libraries/sndfile/sndfile.h>

const char* path = "./recording.wav";

AuxiliaryTask gFillBufferTask;
SNDFILE * outfile;
unsigned int gAudioFrames;
unsigned int gAudioInChannels;
float gAudioSampleRate;
Pipe gPipe;

void openFile() {
    SF_INFO sfinfo;
    sfinfo.channels = gAudioInChannels;
    sfinfo.samplerate = gAudioSampleRate;
    sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
    outfile = sf_open(path, SFM_WRITE, &sfinfo);
}

void closeFile() {
    sf_write_sync(outfile);
    sf_close(outfile);
    printf(".wav file written and closed\n");
}

void writeBuffer(void*) {
  unsigned int numItems = gAudioFrames * gAudioInChannels;
  float buf[numItems];
  int ret;
  while((ret = gPipe.readNonRt(buf, numItems) ) > 0)
  {
    sf_write_float(outfile, &buf[0], ret);
  }
}

bool setup(BelaContext* context, void* arg)
{
  gAudioSampleRate = context->audioSampleRate;
  gAudioFrames = context->audioFrames;
  gAudioInChannels = context->audioInChannels;
  gPipe.setup("sndfile-write", 65536, false, false);
  openFile();
  if((gFillBufferTask = Bela_createAuxiliaryTask(&writeBuffer, 90, "writeBuffer")) == 0) {
    return false;
  }
  return true;
}

void render(BelaContext* context, void* arg)
{
  // context->audioIn is the float* that points to all the input samples, stored as interleaved channels)
  gPipe.writeRt(context->audioIn, context->audioFrames * context->audioInChannels);
  Bela_scheduleAuxiliaryTask(gFillBufferTask);
}

void cleanup(BelaContext* context, void* arg)
{
    closeFile();
}
  • Ward replied to this.

    That's great! Thanks Giulio. Tested the code and it works really well.
    My intention is to use the Bela as a mini recorder for a microphone array. I have an audio expander capelet that I would like to utilise, so that I can record more than one microphone. I know I can activate the audio expander and its inputs in the settings within the IDE, but I assume that the code requires some modification to accommodate the additional inputs for recording?

    I was also wondering when external power (such as a power bank supplying 5V) is connected to the Bela, am I right in thinking that after it boots up it automatically begins running the project? Or can it only run from the IDE (connected to laptop)? I only ask as I essentially would like the Bela (acting as a mini recorder) to be portable.

      mpc I was also wondering when external power (such as a power bank supplying 5V) is connected to the Bela, am I right in thinking that after it boots up it automatically begins running the project?

      That can be done by selecting the project you want to start at boot from the top menu in the Settings tab of the IDE.

      mpc but I assume that the code requires some modification to accommodate the additional inputs for recording?

      you have to use analogRead() instead of audioRead(), and be mindful of the difference in sampling rate (and hence the number of frames per block) between audio and analog channels as explained here.

      Last, be aware that there will be a significant difference in quality between the analog in channels and the audio channels, see here for more.

      • mpc replied to this.

        That's great, thank you Giulio. I'll have a look through the extra info that you sent over and approach it from there. I'll be in touch if I have any more queries regarding the audio expander.

        a month later

        giuliomoro

        Hi Guilio,

        Is it possible to utilise more than one pipe so that I can record both the audio and analogue inputs. So I adapted the code (which was originally for recording the audio input) to work with the analogue inputs to use the 8 channels (audio expander capelet) but would like to also be able to record the audio input. As using the same pipe causes issues but attempting to use two pipes (one for audio, one for analogue) causes errors when the code is running.

        I assume it is probably best to record to two separate files (one for recording analogue, one for recording audio and fairly straightforward to implement the code) because of the different sampling rates, due to the analogue channels being sampled half as often as the audio channels etc.

        The current setup is that I’m recording four lapel microphones plugged into the audio expander capelet but would be great to record a fifth lapel that is plugged into the audio input.

        I’ve pasted the current code for reference, most of the audio input related code is commented out. So this is working for analogue at the moment. The code also works for audio input when the analogue related code is commented out.

        #include <Bela.h>
        #include <libraries/Pipe/Pipe.h>
        #include <libraries/sndfile/sndfile.h>
        
        const char* path = "./recording.wav";
        
        AuxiliaryTask gFillBufferTask;
        SNDFILE * outfile;
        unsigned int gAudioFrames;
        unsigned int gAudioInChannels;
        float gAudioSampleRate;
        Pipe gPipe;
        
        //analog 
        unsigned int numAnalogFrames;
        unsigned int numAnalogChannels;
        float numAnalogSampleRate;
        
        void openFile() {
            SF_INFO sfinfo;
            //sfinfo.channels = gAudioInChannels;
            //sfinfo.samplerate = gAudioSampleRate;
            sfinfo.channels = numAnalogChannels;
            sfinfo.samplerate = numAnalogSampleRate;
            sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
            outfile = sf_open(path, SFM_WRITE, &sfinfo);
        }
        
        /*void openFileAnalog() {
        	SF_INFO sfinfo;
            sfinfo.channels = numAnalogChannels;
            sfinfo.samplerate = numAnalogSampleRate;
            sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
            outfile = sf_open(path, SFM_WRITE, &sfinfo);
        }*/
        
        void closeFile() {
            sf_write_sync(outfile);
            sf_close(outfile);
            printf(".wav file written and closed\n");
        }
        
        void writeBuffer(void*) {
          
        /*
          unsigned int numItems = gAudioFrames * gAudioInChannels; 
          float buf[numItems];
          int ret;
          while((ret = gPipe.readNonRt(buf, numItems) ) > 0) 
          {
            sf_write_float(outfile, &buf[0], ret);
          }*/
          
          unsigned int numItemsAn = numAnalogFrames * numAnalogChannels;
          float bufAnalog[numItemsAn];
          int retAn;
          while ((retAn = gPipe.readNonRt(bufAnalog, numItemsAn)) > 0)
          {
          	sf_write_float(outfile, &bufAnalog[0], retAn);
          }
          
        }
        
        bool setup(BelaContext* context, void* arg)
        {
        
          //setup audio frames and channels
          gAudioSampleRate = context->audioSampleRate;
          gAudioFrames = context->audioFrames;
          gAudioInChannels = context->audioInChannels;
          
          //setup analog frames and channels 
          numAnalogSampleRate = context->analogSampleRate;
          numAnalogFrames = context->analogFrames;
          numAnalogChannels = context->analogInChannels;
          
          gPipe.setup("sndfile-write", 65536, false, false);
          openFile();
          
          if((gFillBufferTask = Bela_createAuxiliaryTask(&writeBuffer, 90, "writeBuffer")) == 0) {
            return false;
          }
          return true;
        }
        
        void render(BelaContext* context, void* arg)
        {
          // context->audioIn is the float* that points to all the input samples, stored as interleaved channels)
          // audioIn is an array of 4 frames * 2 channels = 8 audio input samples.
          //gPipe.writeRt(context->audioIn, context->audioFrames * context->audioInChannels);
          
          // analogIn is an array of 2 frames * 8 channels = 16 analog input samples. 
          // (2 frames because analog I/O runs at half the sample rate of audio I/O.) 
          gPipe.writeRt(context->analogIn, context->analogFrames * context->analogInChannels);
          
          Bela_scheduleAuxiliaryTask(gFillBufferTask);
          
        }
        
        void cleanup(BelaContext* context, void* arg)
        {
            closeFile();
        }

          mpc As using the same pipe causes issues but attempting to use two pipes (one for audio, one for analogue) causes errors when the code is running.

          what error would you get? Two Pipes should work just fine, as long as you use two separate AuxiliaryTasks.

          Using one Pipe is also fine, you should just make sure that you are sending data in exactly the same format and order as you are receiving them. In the example below I send audio first and analog second, and I receive them the same way. The while(1) with break is to make sure that if the thread didn't get a chance to run between invocations of render(), it gets a chance to run and empty the content of gPipe.

          For instance:

          void writeBuffer(void*) {
           while(1) {
            unsigned int numItems = gAudioFrames * gAudioInChannels; 
            float buf[numItems];
            int ret;
            if((ret = gPipe.readNonRt(buf, numItems) ) > 0) 
            {
              sf_write_float(outfile, &buf[0], ret);
            } else {
              break;
            }
            
            unsigned int numItemsAn = numAnalogFrames * numAnalogChannels;
            float bufAnalog[numItemsAn];
            int retAn;
            if ((retAn = gPipe.readNonRt(bufAnalog, numItemsAn)) > 0)
            {
            	sf_write_float(outfile, &bufAnalog[0], retAn);
            }
           }
          }
          
          
          void render(BelaContext* context, void*)
          {
          ...
          gPipe.writeRt(context->audioIn, context->audioFrames * context->audioInChannels);
          gPipe.writeRt(context->analogIn, context->analogFrames * context->analogInChannels);
          ...
          }

          A few notes:

          • the code above is thread-safe only as long as it runs on one CPU. If you were to port it to an architecture that uses multiple CPUs, you'd have to be a bit more clever
          • if you can accept a (probably minor or insignificant) performance penalty, you could pack all the relevant analog and audio samples in a single buffer and send them with a single gPIpe.writeRt() call, which would simplify the writeBuffer() function and be thread-safe. If currently you are sending more data than you need (e.g.: you are sending all the analog channels, but you are only using 4), then this solution would actually save some CPU time, instead of causing a performance penalty, as you would reduce the memory that gets copied to the pipe.
          • mpc replied to this.

            giuliomoro I realised just before your response that I had forgot to use another AuxiliaryTask for the two pipe approach. Thank you for the example, this was very helpful and everything is working as intended. The notes are also really helpful as I'll keep this in mind for future reference.

            4 days later

            giuliomoro

            Hi Giulio,

            Can you access the files from the SD card after recording without using the IDE? Basically directly off the SD card. As when the external power is plugged into the Bela, the project runs on boot and it starts recording. I can stop the currently running program by pressing the power button on the BBB for a small interval (I assume 1 second?). The problem would be that when plugging into the computer to review recordings (if using the IDE), the project will run (on boot) and begin overwriting those recordings. So I would like to avoid this.

            If the SD card is formatted with a filesystem that your computer can read, then you can. If this is the regular Bela SD card you are talking about, it is formatted as ext4, which can only be read natively from Linux, though I remember there were some free drivers for Windows. There was also a solution for macos, but that would be a commercial ($$$) one. However, if you have a USB SD card reader, you should be able to read it from a Linux Virtual Machine running on macos, but that is a bulky option I'd say. One possibility is to store data to an external medium, e.g.: a USB stick.

            The safest option is probably to never overwrite existing files. You use WriteFile::generateUniqueFilename(const char* original) to get a unique filename and make sure you don't overwrite the existing one. See here for details on usage.

            Alternatively, what you could do is modifying your program so that it only creates the file and starts recording if a certain GPIO is in a certain state. So for instance, you could put a wire between the GPIO and 3.3V or GND when it has to be recording and then removing it before you boot the board in "read only" mode. Given how you are calling openFile() (which will truncate the previous file) from setup(), you wouldn't be able to use digitalRead(), but you'd have to use an arbitrary GPIO pin.

            Ultimately, what should be implemented is this, so that you'd be able to write a file to the BELABOOT partition (which you can read/write from your host without drivers, given how it's FAT32) to prevent the file from running at boot. Or even better, repurpose The Bela Button so that if it is pressed before the program starts it disables running at boot for the current boot.

            • mpc replied to this.
              4 days later

              giuliomoro

              Thanks for the reply Giulio, I think potentially re-formatting the SD card (back-up everything prior) to a recognisable file system might be the way to go. I may potentially look at using a USB stick for additional storage for the future. With regards to avoiding overwriting existing files, original in the following line static char* generateUniqueFilename(const char* original); refers to the path/filename of the original file which would look something like this: char* generateUniqueFilename(const char* path = “audioCh.wav”);. Although this compiles fine, it doesn’t work at the moment and I’ve tried a couple of other different versions of writing it using just the filename. I read the details on usage (I understand what each part is doing) but not 100% sure on how to implement it.

                mpc I think potentially re-formatting the SD card (back-up everything prior) to a recognisable file system might be the way to go

                it's not easy: Linux has to run from an ext4 partition, but it can access many other filesystems. You could instead add a partition on the SD card with a different filesystem.

                mpc char generateUniqueFilename(const char path = “audioCh.wav”);.

                what is this?

                You'd use it like this:

                char originalFilename[] = "my-file.wav";
                char* uniqueFilename = WriteFile::generateUniqueFilename(originalFilename);

                and then subsequently use uniqueFilename (and free() it when you are done).

                • mpc replied to this.

                  giuliomoro

                  Ah I see I'm using mac operating system and was thinking of using FAT32 for the filesystem for the SD card. I did reformat the SD card to this filesystem and then flashed the Bela image but unfortunately that didn't seem to work. I'll try adding a partition instead and see how it goes.

                  char originalFilename[] = "my-file.wav";
                  char* uniqueFilename = WriteFile::generateUniqueFilename(originalFilename);
                  my apologies this code makes a lot more sense, as it wasn't quite clear with regards to the implementation when reading it in the Writefile.h on Bela github. When you say 'subsequently use uniqueFilename' would you be able to elaborate abit on this? When calling free() on uniqueFilename would I be correct in saying that this is done within void cleanup. Similar to how we close the file when creating wav files with sndfile.

                    mpc When you say 'subsequently use uniqueFilename' would you be able to elaborate abit on this?

                    I mean, in the reminder of your code use uniqueFilename and not originalFilename. You can free() it as soon as you no longer need it. Usually that would be as soon as you opened the file, as henceforth you'd be referring to it via the file descriptor. However, cleaning it in cleanup() is also just fine.

                    mpc I did reformat the SD card to this filesystem and then flashed the Bela image but unfortunately that didn't seem to work.

                    Flashing the Bela image on it will override any partitioning/formatting you may have done before. You should start with a flashed image and then add a FAT32 partition to it. I am not familiar with macos's disk utility, but it should be able to do this. Alternatively you can use fdisk on the board.

                    • mpc replied to this.
                    • mpc likes this.

                      giuliomoro

                      Ah ok I see what you mean. I do get the following come up in the console:
                      File audioCh.wav exists, writing to audioCh1.wav instead
                      File analogueCh.wav exists, writing to analogueCh1.wav instead

                      The files are showing up in resources in the IDE, as I open, write buffer and then close them. At present when the new files for example audioCh1.wav or audioCh2.wav are created and its recording, the original file is overwritten at the same time. Once it gets beyond audioCh1.wav that file is fine. audioCh2.wav is created and the original file is overwritten and so on for each new audioCh.wav file that gets created. I believe that this is the section of code that is causing it:

                      void writeBuffer(void*) {
                      	 while(1) {
                      	  unsigned int numItems = gAudioFrames * gAudioInChannels; 
                      	  float buf[numItems];
                      	  int ret;
                      	  if((ret = gPipe.readNonRt(buf, numItems) ) > 0) 
                      	  {
                      	    sf_write_float(outfile, &buf[0], ret);
                      	    sf_write_float(outfile2, &buf[0], ret);
                      	  } else {
                      	    break;
                      	  }
                      	  
                      	  unsigned int numItemsAn = numAnalogFrames * numAnalogChannels;
                      	  float bufAnalog[numItemsAn];
                      	  int retAn;
                      	  if ((retAn = gPipe.readNonRt(bufAnalog, numItemsAn)) > 0)
                      	  {
                      	  	sf_write_float(outfileAnalog, &bufAnalog[0], retAn);
                      	  	sf_write_float(outfileAnalog2, &bufAnalog[0], retAn);
                      	  }
                      	  }
                      	}

                      as I've got two sf_write_float()in each of the if statements but I'm not quite sure how to amend it to work as intended. I've tried a couple of different approaches (with other conditional statements) but been unsuccessful.

                      With regards to the SD card unfortunately after the Bela image is flashed onto the card it registers as Master Boot Record format, which can’t be partitioned using disk utility through the application or terminal on Mac. I was wondering if it would be easier to use a USB stick (formatted as FAT32 and mounted using instructions on the wiki) for storage and then I assume specify the path from what I have currently in my code const char* path = "./audioCh.wav”; to the USB path with something like const char* path = “./root/Bela/projects/project_name/usb/audioCh.wav”?

                      4 years later

                      I want to be able to record audio signals without knowing beforehand for how long I will be recording. The recording time could be in the range of 10s of seconds to hours (assuming enough disk space exists). Ideally if the program where to crash I would want the recorded material to still be on the disk.

                      I saw this recording example but that writes into a single vector and then writes that to disk. For recording large amounts of time that would require a whole lot of allocation.

                      Then I found this code you wrote earlier: giuliomoro

                      Does sf_write_float actually write data to the disk or does that happen when sf_write_sync or sf_close is called? It seems like it does when looking at the libsndfile source but I'm not 100% sure. Assuming it does directly write to disk and disk space is infinite, I can use this code to record until infinity?