Hey everyone,
New JUCE (and really new to C++ as well) user here, and had a quick question as I begin writing a simple audio/sensor recording application. I’m a little confused after reading other threads on the topic, and looking at the AudioDemo example w/Juce, so figured I’d ask and see if someone could help clarify a bit. Basically, I’m creating an application to record audio/sensor data as uncompressed .wav files. The first test is simply to stream in live audio (microphone/line, soundflower from another application…etc) and record it to disk.
I have created a class called AudioRecorder (see below) which should basically set up an input device, with a channel selector combobox, that would enable recording. So far, from pulling some stuff out of the juce AudioDemo, I’ve managed to make the basis of my record class, with an auto-updating channel selector, and a graphical waveform display (ala the one in the demo). All is good! Well…almost I can’t seem to get it writing to disk or even playing out of speakers
What is the best approach? Do I need to make a mixer/transport? Any help would be greatly appreciated. Below is my current AudioRecorder class (there are a few things commented out which are still left from the various ways i’ve tried getting audio output/recording to disk as .wav’s)
Right now, I can instantiate as many AudioRecorder classes in my main (MainComponent) class as desired, and all seem to pull audio (at least in the wave-form display) from AudioDeviceManager nicely-- although, when I create more than one instance, I get a nasty sound out of my speakers… any info on that would also be helpful as I debug!
AudioRecorder.h
[code]/*
- AudioRecorder.h
- juce_application
- Created by Jordan Hochenbaum on 11/19/09.
*/
#ifndef AUDIORECORDER_H
#define AUDIORECORDER_H
#include “includes.h”
class AudioRecorder : public Component,
public Timer,
//public AudioSource,
public AudioIODeviceCallback,
public ComboBoxListener
{
public:
AudioRecorder ();
~AudioRecorder();
WavAudioFormat wavFormat;
// this wraps the actual audio device
void paint (Graphics& g);
void timerCallback();
void addSample (const float sample);
void audioDeviceIOCallback (const float** inputChannelData,
int totalNumInputChannels,
float** outputChannelData,
int totalNumOutputChannels,
int numSamples);
void audioDeviceAboutToStart (AudioIODevice*);
void audioDeviceStopped();
void setChannel(int channel);
void createListOfInputChannels(AudioIODevice*);
void comboBoxChanged(ComboBox*);
/*void prepareToPlay( int samplesPerBlockExpected, double sampleRate);
void releaseResources();
void getNextAudioBlock(const AudioSourceChannelInfo &bufferToFill);*/
private:
int channel;
int volatile bufferPos, bufferSize, numSamplesIn;
float* circularBuffer;
float currentInputLevel;
String _inputDeviceName;
ComboBox* inputChannelSelector;
//void recordButton (Button* recordButtonWasClicked);
//void audioRec();
};
#endif//AUDIORECORDER_H[/code]
AudioRecorder.cpp
[code]/*
- AudioRecorder.cpp
- juce_application
- Created by Jordan Hochenbaum on 11/19/09.
*/
#include “AudioRecorder.h”
#include “jucedemo_headers.h”
AudioRecorder::AudioRecorder()
{
channel = 0;
bufferPos = 0;
bufferSize = 512;
circularBuffer = (float*) juce_calloc (sizeof (float) * bufferSize);
currentInputLevel = 0.0f;
numSamplesIn = 0;
setOpaque (true);
startTimer (1000 / 50); // repaint every 1/50 of a second
addAndMakeVisible (inputChannelSelector = new ComboBox (T("input")));
inputChannelSelector->setSelectedId (1);
inputChannelSelector->addListener(this);
inputChannelSelector->setTextWhenNothingSelected("Select Input Channel");
}
AudioRecorder::~AudioRecorder()
{
juce_free (circularBuffer);
}
//this method draws the incoming audio waveform display
void AudioRecorder::paint (Graphics& g)
{
g.fillAll (Colours::white);
g.setColour (Colours::black);
inputChannelSelector->setBounds (getWidth()/1.6, (getHeight()/4)+15, 250, 24);
const float halfHeight = getHeight() * 0.5f;
int bp = bufferPos;
for (int x = getWidth(); --x >= 0;)
{
const int samplesAgo = getWidth() - x;
const float level = circularBuffer [(bp + bufferSize - samplesAgo) % bufferSize];
if (level > 0.01f)
g.drawLine ((float) getWidth()-x, halfHeight - halfHeight * level,
(float) getWidth()-x, halfHeight + halfHeight * level);
}
}
void AudioRecorder::timerCallback()
{
repaint();
}
void AudioRecorder::addSample (const float sample)
{
currentInputLevel += fabsf (sample);
const int samplesToAverage = 128;
if (++numSamplesIn > samplesToAverage)
{
circularBuffer [bufferPos++ % bufferSize] = currentInputLevel / samplesToAverage;
numSamplesIn = 0;
currentInputLevel = 0.0f;
}
}
void AudioRecorder::audioDeviceIOCallback (const float** inputChannelData, int totalNumInputChannels, float** outputChannelData, int totalNumOutputChannels, int numSamples) {
for (int i = 0; i < totalNumInputChannels; ++i)
{
if (inputChannelData [i] != 0)
{
for (int j = 0; j < numSamples; ++j){
addSample (inputChannelData [channel][j]);
}
break;
}
}
}
void AudioRecorder::audioDeviceAboutToStart (AudioIODevice* _audioIODevice)
{
zeromem (circularBuffer, sizeof (float) * bufferSize);
//test to see if input device has changed. If so, update list of input Channels.
if (_inputDeviceName != _audioIODevice->getName())
{
channel = 0;
createListOfInputChannels( _audioIODevice);
_inputDeviceName ==_audioIODevice->getName();
}
}
void AudioRecorder::audioDeviceStopped()
{
zeromem (circularBuffer, sizeof (float) * bufferSize);
}
/*void AudioRecorder::prepareToPlay(int samplesPerBlockExpected, double sampleRate)
{
}
void AudioRecorder::releaseResources()
{
}
void AudioRecorder::getNextAudioBlock(const AudioSourceChannelInfo &bufferToFill)
{
bufferToFill.clearActiveBufferRegion();
}*/
void AudioRecorder::createListOfInputChannels(AudioIODevice* _audioIODevice)
{
//clear current contents IN comboBox’s
inputChannelSelector->clear();
//Add input channel names to all channel select comboBox's
for (int i = 0; i< _audioIODevice->getInputChannelNames().size(); i++)
{
inputChannelSelector->addItem(_audioIODevice->getInputChannelNames()[i], i+1);
}
}
void AudioRecorder::comboBoxChanged(ComboBox* comboBoxThatHasChanged)
{
//if the inputChannelSelector box has changed, update/set the AudioRecorder’s channel
setChannel(inputChannelSelector->getSelectedItemIndex());
}
void AudioRecorder::setChannel(int _channel) {
channel = _channel;
}
//==============================================================================
[/code]