/* ============================================================================== This is an automatically generated file created by the Jucer! Creation date: 13 Nov 2009 3:52:50 pm Be careful when adding custom code to these files, as only the code within the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded and re-saved. Jucer version: 1.12 ------------------------------------------------------------------------------ The Jucer is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-6 by Raw Material Software ltd. ============================================================================== */ //[Headers] You can add your own extra header files here... //[/Headers] #include "AudioDemoRecordPage.h" //[MiscUserDefs] You can add your own user definitions and misc code here... //============================================================================== /* This is a rough-and-ready circular buffer, used to allow the audio thread to push data quickly into a queue, allowing a background thread to come along and write it to disk later. */ class CircularAudioBuffer { public: CircularAudioBuffer (const int numChannels, const int numSamples) : buffer (numChannels, numSamples) { clear(); } ~CircularAudioBuffer() { } void clear() { buffer.clear(); const ScopedLock sl (bufferLock); bufferValidStart = bufferValidEnd = 0; } void addSamplesToBuffer (const AudioSampleBuffer& sourceBuffer, int numSamples) { const int bufferSize = buffer.getNumSamples(); bufferLock.enter(); int newDataStart = bufferValidEnd; int newDataEnd = newDataStart + numSamples; const int actualNewDataEnd = newDataEnd; bufferValidStart = jmax (bufferValidStart, newDataEnd - bufferSize); bufferLock.exit(); newDataStart %= bufferSize; newDataEnd %= bufferSize; if (newDataEnd < newDataStart) { for (int i = jmin (buffer.getNumChannels(), sourceBuffer.getNumChannels()); --i >= 0;) { buffer.copyFrom (i, newDataStart, sourceBuffer, i, 0, bufferSize - newDataStart); buffer.copyFrom (i, 0, sourceBuffer, i, bufferSize - newDataStart, newDataEnd); } } else { for (int i = jmin (buffer.getNumChannels(), sourceBuffer.getNumChannels()); --i >= 0;) buffer.copyFrom (i, newDataStart, sourceBuffer, i, 0, newDataEnd - newDataStart); } const ScopedLock sl (bufferLock); bufferValidEnd = actualNewDataEnd; } int readSamplesFromBuffer (AudioSampleBuffer& destBuffer, int numSamples) { const int bufferSize = buffer.getNumSamples(); bufferLock.enter(); int availableDataStart = bufferValidStart; const int numSamplesDone = jmin (numSamples, bufferValidEnd - availableDataStart); int availableDataEnd = availableDataStart + numSamplesDone; bufferValidStart = availableDataEnd; bufferLock.exit(); availableDataStart %= bufferSize; availableDataEnd %= bufferSize; if (availableDataEnd < availableDataStart) { for (int i = jmin (buffer.getNumChannels(), destBuffer.getNumChannels()); --i >= 0;) { destBuffer.copyFrom (i, 0, buffer, i, availableDataStart, bufferSize - availableDataStart); destBuffer.copyFrom (i, bufferSize - availableDataStart, buffer, i, 0, availableDataEnd); } } else { for (int i = jmin (buffer.getNumChannels(), destBuffer.getNumChannels()); --i >= 0;) destBuffer.copyFrom (i, 0, buffer, i, availableDataStart, numSamplesDone); } return numSamplesDone; } private: CriticalSection bufferLock; AudioSampleBuffer buffer; int bufferValidStart, bufferValidEnd; }; //============================================================================== class AudioRecorder : public AudioIODeviceCallback, public Thread { public: AudioRecorder() : Thread ("audio recorder"), circularBuffer (2, 48000), recording (false), sampleRate (0) { } ~AudioRecorder() { stop(); } //============================================================================== void startRecording (const File& file) { stop(); if (sampleRate > 0) { fileToRecord = file; startThread(); circularBuffer.clear(); recording = true; } } void stop() { recording = false; stopThread (5000); } bool isRecording() const { return isThreadRunning() && recording; } //============================================================================== void audioDeviceAboutToStart (AudioIODevice* device) { sampleRate = device->getCurrentSampleRate(); } void audioDeviceStopped() { sampleRate = 0; } void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels, float** outputChannelData, int numOutputChannels, int numSamples) { if (recording) { const AudioSampleBuffer incomingData ((float**) inputChannelData, numInputChannels, numSamples); circularBuffer.addSamplesToBuffer (incomingData, numSamples); } // We need to clear the output buffers, in case they're full of junk.. for (int i = 0; i < numOutputChannels; ++i) if (outputChannelData[i] != 0) zeromem (outputChannelData[i], sizeof (float) * numSamples); } //============================================================================== void run() { fileToRecord.deleteFile(); OutputStream* outStream = fileToRecord.createOutputStream(); if (outStream == 0) return; WavAudioFormat wavFormat; AudioFormatWriter* writer = wavFormat.createWriterFor (outStream, sampleRate, 1, 16, StringPairArray(), 0); if (writer == 0) { delete outStream; return; } AudioSampleBuffer tempBuffer (2, 8192); while (! threadShouldExit()) { int numSamplesReady = circularBuffer.readSamplesFromBuffer (tempBuffer, tempBuffer.getNumSamples()); if (numSamplesReady > 0) tempBuffer.writeToAudioWriter (writer, 0, numSamplesReady); Thread::sleep (1); } delete writer; } File fileToRecord; double sampleRate; bool recording; CircularAudioBuffer circularBuffer; }; //[/MiscUserDefs] //============================================================================== AudioDemoRecordPage::AudioDemoRecordPage (AudioDeviceManager& deviceManager_) : deviceManager (deviceManager_), liveAudioDisplayComp (0), explanationLabel (0), recordButton (0) { addAndMakeVisible (liveAudioDisplayComp = new LiveAudioInputDisplayComp()); addAndMakeVisible (explanationLabel = new Label (String::empty, T("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."))); explanationLabel->setFont (Font (15.0000f, Font::plain)); explanationLabel->setJustificationType (Justification::topLeft); explanationLabel->setEditable (false, false, false); explanationLabel->setColour (TextEditor::textColourId, Colours::black); explanationLabel->setColour (TextEditor::backgroundColourId, Colour (0x0)); addAndMakeVisible (recordButton = new TextButton (String::empty)); recordButton->setButtonText (T("Record")); recordButton->addButtonListener (this); recordButton->setColour (TextButton::buttonColourId, Colour (0xffff5c5c)); recordButton->setColour (TextButton::textColourOffId, Colours::black); //[UserPreSize] //[/UserPreSize] setSize (600, 400); //[Constructor] You can add your own custom stuff here.. recorder = new AudioRecorder(); deviceManager.addAudioCallback (recorder); deviceManager.addAudioCallback (liveAudioDisplayComp); //[/Constructor] } AudioDemoRecordPage::~AudioDemoRecordPage() { //[Destructor_pre]. You can add your own custom destruction code here.. recorder->stop(); deviceManager.removeAudioCallback (recorder); delete recorder; deviceManager.removeAudioCallback (liveAudioDisplayComp); //[/Destructor_pre] deleteAndZero (liveAudioDisplayComp); deleteAndZero (explanationLabel); deleteAndZero (recordButton); //[Destructor]. You can add your own custom destruction code here.. //[/Destructor] } //============================================================================== void AudioDemoRecordPage::paint (Graphics& g) { //[UserPrePaint] Add your own custom painting code here.. //[/UserPrePaint] g.fillAll (Colours::lightgrey); //[UserPaint] Add your own custom painting code here.. //[/UserPaint] } void AudioDemoRecordPage::resized() { liveAudioDisplayComp->setBounds (8, 8, getWidth() - 16, 64); explanationLabel->setBounds (160, 88, getWidth() - 169, 216); recordButton->setBounds (8, 88, 136, 40); //[UserResized] Add your own custom resize handling here.. //[/UserResized] } void AudioDemoRecordPage::buttonClicked (Button* buttonThatWasClicked) { //[UserbuttonClicked_Pre] //[/UserbuttonClicked_Pre] if (buttonThatWasClicked == recordButton) { //[UserButtonCode_recordButton] -- add your button handler code here.. if (recorder->isRecording()) { recorder->stop(); } else { File file (File::getSpecialLocation (File::userDocumentsDirectory) .getNonexistentChildFile ("Juce Demo Audio Recording", ".wav")); recorder->startRecording (file); } if (recorder->isRecording()) recordButton->setButtonText ("Stop"); else recordButton->setButtonText ("Record"); //[/UserButtonCode_recordButton] } //[UserbuttonClicked_Post] //[/UserbuttonClicked_Post] } void AudioDemoRecordPage::visibilityChanged() { //[UserCode_visibilityChanged] -- Add your code here... recorder->stop(); recordButton->setButtonText ("Record"); //[/UserCode_visibilityChanged] } //[MiscUserCode] You can add your own definitions of your custom methods or any other code here... //[/MiscUserCode] //============================================================================== #if 0 /* -- Jucer information section -- This is where the Jucer puts all of its metadata, so don't change anything in here! BEGIN_JUCER_METADATA END_JUCER_METADATA */ #endif