Recording to memory


#1

Hello everyone,

I’m currently writing an application in which it should be possible to record and play back audio as well as MIDI. This seems to be harder than I thought.

Let’s start with audio recording:
In the JUCE demo this is done by streaming directly to a file. My idea was to stream the samples to a vector (for each channel) in the main memory. Since I’ll be using no more than 2 channels, it seems to me that space won’t be an issue. Since a vector doubles in size when it’s underlying array is full, streaming to a vector is prone to glitches.
I’m struggling to get the samples from the callback to my main thread so I can get them into these vectors. Since I can’t seem to find any examples on how to accomplish this, I was wondering if this was bad practice?
If not, does JUCE provide classes to aid this operation? And again, if not, does anyone have any suggestions on how to tacke this problem?

As far as MIDI recording is concerned:
I would like to record incoming MIDI messages to a MIDIMessageSequence in memory. Since the rate of incoming MIDI messages is much lower than the samplerate of digital audio, it seems that this task is less tedious than recording audio. MIDI and audio can be recorded together, so it would be nice if the timestamps of the MIDI messages are synchronised with the audio samples. Though what if I record MIDI without recording audio, do I use some sort of timer class to timestamp the messages, and how do I transfer them from the callback to the main thread?

MIDI playback:
Do I use a timer, passing MIDI messages to the callback whenever the timer exceeds their timestamp?

As you can see I’m new to audio programming and I’m still trying to find out what the good and bad practices are, so every word of advice is very much welcome!

Maybe I’m not the only one with these questions and we can make this into a general recording thread in which all JUCE classes concerning recording are discussed.
(Or maybe the answers to these questions are trivial, which would explain why nobody has asked them yet :oops: )


#2

If your aim is to manipulate the audio in one way or another before storing it (or else, why your desire to store the samples in a vector instead of just streaming to a file as in the demo?), I’d suggest you take a look at the plugin host demo. There you can add your own plugin/Audioprocessor by subclasing the Audioprocessor and do your processing in its processBlock function.


#3

Maybe I’m looking at this problem from the wrong perspective, since the preferred way to record audio seems to be to stream it to a file, so please inform me if i’m on the wrong track here.
You might notice that I really have no experience in recording audio or MIDI, but here are my thoughts anyway.

  • Streaming to disk requires the user to specify a file path (or at least a directory) before anything has been recorded. If it would be possible to stream to memory, the user can choose the path after recording. If he decides not to save his recording, no files or directories should be deleted afterwards.
  • Say the user has recorded a file, but he would like to re-record a part of that file, starting from a given position. It seems to me that there is no way to specify a position in a file where the next incoming samples should be added. This problem becomes much simpler if the audio is in memory, say in a vector.
  • Intuitively I would say that allocating a chunk of memory requires less time than allocating a block of disk space.

As far as MIDI is concerned, is it safe to acquire a lock in the callback and to add the message to a MidiMessageSequence, or do I also have to take precautions to avoid glitches?
Or perhaps there is another more established way of MIDI recording?


#4

I would rather recommend to use a buffered stream for storing on disk.
You can use a large sample buffer for storage between writes to disk, and have a background thread do the actual writing in chunks.
This way, you will be able to optimize DMA access to the best disk access performance, and the least cache misses in the process.
To do that, you could write a wrapper around the AudioSampleBuffer and MidiBuffer classes with queued block semantic.


#5

Do you name a photo before you take it? Do you name a word document before you write it? Do you name a child before you conceive it? Did Columbus name America before he discovered it? Did God name day and night before he created them? I’d say -adhere to long established practices… :smiley:

Well, what if he makes an error? Then he’d spoil the first recording… and if he wants to re-record the first time there’s a good chance he wants to do it a second time. And a third…
People not only expect perfect copies when working with digital audio, but also to be able to undo previous (mis)takes.

The way to deal with this is to only record (to file), never delete and never overwrite. Then you take the parts you like and, for example, shove them into one of all these audiosources that resides in /juce_audio_basics/sources. PositionableAudioSource for example can setNextReadPosition to whatever part of the file you want to play (back). And with MixerAudioSource you can mix two or more Audiosources. Or perhaps you can use Jules new MemoryMappedAudioFormatReader.