Can someone please help explain how to record audio. I’m looking at the demo, but it’s a bit confusing.
The secret of getting good answers on forums is to ask very specific questions… Asking something as vague as this makes it almost impossible for anyone to answer - try breaking your problem down into smaller tasks that you need help with.
Well the current issue I’m having is that I used the demo code but removed all the visual parts as far as seeing the recording goes and I keep getting this:
ld: warning: ignoring file /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks//QuickTime.framework/QuickTime.tbd, missing required architecture x86_64 in file /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks//QuickTime.framework/QuickTime.tbd
Undefined symbols for architecture x86_64:
“MainAppWindow::getSharedAudioDeviceManager()”, referenced from:
MainContentComponent::MainContentComponent() in MainComponent.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
MainComponent.cpp:
#ifndef MAINCOMPONENT_H_INCLUDED
#define MAINCOMPONENT_H_INCLUDED
#include “…/JuceLibraryCode/JuceHeader.h”
#include “MainAppWindow.h”
//==============================================================================
/*
This component lives inside our window, and this is where you should put all
your controls and content.
*/
class AudioRecorder : public AudioIODeviceCallback
{
public:
AudioRecorder(AudioThumbnail& thumbnailToUpdate)
: thumbnail (thumbnailToUpdate),
backgroundThread(“Audio Recorder Thread”),
sampleRate (0), nextSampleNum (0), activeWriter(nullptr)
{
backgroundThread.startThread();
}
~AudioRecorder()
{
stop();
}
void beginRec (const File& file)
{
stop();
if (sampleRate > 0)
{
file.deleteFile();
ScopedPointer<FileOutputStream> fileStream(file.createOutputStream());
if (fileStream != nullptr)
{
WavAudioFormat wavFormat;
AudioFormatWriter* writer = wavFormat.createWriterFor (fileStream, sampleRate, 1, 16, StringPairArray(), 0);
if (writer != nullptr)
{
fileStream.release();
threadedWriter = new AudioFormatWriter::ThreadedWriter
(writer, backgroundThread, 32768);
thumbnail.reset (writer->getNumChannels(), writer->getSampleRate());
nextSampleNum = 0;
const ScopedLock sl(writerLock);
activeWriter = threadedWriter;
}
}
}
}
void stop()
{
{
const ScopedLock sl (writerLock);
activeWriter = nullptr;
}
threadedWriter = nullptr;
}
bool isRec() const
{
return activeWriter != nullptr;
}
void audioDeviceAboutToStart (AudioIODevice* device) override
{
sampleRate = device ->getCurrentSampleRate();
}
void audioDeviceStopped() override
{
sampleRate = 0;
}
void audioDeviceIOCallback (const float** inputChannelData, int /*numInputChannels*/, float** outputChannelData, int numOutputChannels, int numSamples) override
{
const ScopedLock sl (writerLock);
if (activeWriter != nullptr)
{
activeWriter->write (inputChannelData, numSamples);
const AudioSampleBuffer buffer(const_cast<float**> (inputChannelData), thumbnail.getNumChannels(), numSamples);
thumbnail.addBlock (nextSampleNum, buffer, 0, numSamples);
nextSampleNum += numSamples;
}
for (int i = 0; i < numOutputChannels; ++i)
if(outputChannelData[i] !=nullptr)
FloatVectorOperations::clear(outputChannelData[i], numSamples);
}
private:
AudioThumbnail& thumbnail;
TimeSliceThread backgroundThread;
ScopedPointerAudioFormatWriter::ThreadedWriter threadedWriter;
double sampleRate;
int64 nextSampleNum;
CriticalSection writerLock;
AudioFormatWriter::ThreadedWriter* volatile activeWriter;
};
class RecordingThumbnail : public Component,
private ChangeListener
{
public:
RecordingThumbnail()
:thumbnailCache(10),
thumbnail(512, formatManager, thumbnailCache),
displayFullThumb(false)
{
formatManager.registerBasicFormats();
thumbnail.addChangeListener (this);
}
~RecordingThumbnail()
{
thumbnail.removeChangeListener (this);
}
AudioThumbnail& getAudioThumbnail()
{
return thumbnail;
}
void setDisplayFullThumbnail(bool displayFull)
{
displayFullThumb = displayFull;
repaint();
}
void paint (Graphics& g) override
{
g.fillAll (Colours::darkgrey);
g.setColour (Colours::lightgrey);
if (thumbnail.getTotalLength() > 0.0)
{
const double endTime = displayFullThumb ? thumbnail.getTotalLength()
: jmax(30.0, thumbnail.getTotalLength());
Rectangle<int> thumbArea (getLocalBounds());
thumbnail.drawChannels(g, thumbArea.reduced (2), 0.0, endTime, 1.0f);
}
else
{
g.setFont (14.0f);
g.drawFittedText ("(No file recorded)", getLocalBounds(), Justification::centred, 2);
}
}
private:
AudioFormatManager formatManager;
AudioThumbnailCache thumbnailCache;
AudioThumbnail thumbnail;
bool displayFullThumb;
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (source == &thumbnail)
repaint();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RecordingThumbnail)
};
class MainContentComponent : public AudioAppComponent,
//public Component,
private Button::Listener
{
public:
//==============================================================================
MainContentComponent()
:deviceManager(MainAppWindow::getSharedAudioDeviceManager()), recorder (recordingThumbnail.getAudioThumbnail())
{
setOpaque(true);
addAndMakeVisible(explanationLabel);
explanationLabel.setText ("This page demonstrates how to record a wave file from the live audio input..\n\nPressing record will start recording a file in your \"Documents\" folder.", dontSendNotification);
explanationLabel.setFont(Font (15.00f, Font::plain));
explanationLabel.setJustificationType(Justification::topLeft);
explanationLabel.setEditable(false, false, false);
explanationLabel.setColour(TextButton::buttonColourId, Colour(0xffff5c5c));
recordButton.setColour(TextButton::textColourOnId, Colours::black);
setSize (800, 600);
// specify the number of input and output channels that we want to open
setAudioChannels (2, 2);
}
~MainContentComponent()
{
shutdownAudio();
}
//==============================================================================
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override
{
// This function will be called when the audio device is started, or when
// its settings (i.e. sample rate, block size, etc) are changed.
// You can use this function to initialise any resources you might need,
// but be careful - it will be called on the audio thread, not the GUI thread.
// For more details, see the help for AudioProcessor::prepareToPlay()
}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
// Your audio-processing code goes here!
// For more details, see the help for AudioProcessor::getNextAudioBlock()
// Right now we are not producing any data, in which case we need to clear the buffer
// (to prevent the output of random noise)
bufferToFill.clearActiveBufferRegion();
}
void releaseResources() override
{
// This will be called when the audio device stops, or when it is being
// restarted due to a setting change.
// For more details, see the help for AudioProcessor::releaseResources()
}
//==============================================================================
void paint (Graphics& g) override
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (Colours::black);
// You can add your drawing code here!
}
void resized() override
{
// This is called when the MainContentComponent is resized.
// If you add any child components, this is where you should
// update their positions.
Rectangle<int> area (getLocalBounds());
recordButton.setBounds (area.removeFromTop(80).reduced(8));
recordButton.setBounds (area.removeFromTop (36).removeFromLeft(140).reduced(8));
explanationLabel.setBounds(area.reduced(8));
}
private:
//==============================================================================
AudioDeviceManager& deviceManager;
RecordingThumbnail recordingThumbnail;
AudioRecorder recorder;
Label explanationLabel;
TextButton recordButton;
// Your private member variables go here...
void beginRec()
{
const File file (File::getSpecialLocation (File::userDocumentsDirectory)
.getNonexistentChildFile("Juce Demo Audio Recording", ".wav"));
recorder.beginRec (file);
recordButton.setButtonText("Stop");
}
void endRec()
{
recorder.stop();
recordButton.setButtonText("Record");
}
void buttonClicked(Button* button) override
{
if (button==&recordButton)
{
if(recorder.isRec())
endRec();
else
beginRec();
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};
// (This function is called by the app startup code to create our main component)
Component* createMainContentComponent() { return new MainContentComponent(); }
#endif // MAINCOMPONENT_H_INCLUDED
My main issue is that with all the GUI components that are built in it is making it hard for me to discern what code I should be studying for the recording and what code I shouldn’t.
