| @@ -48,6 +48,7 @@ public: | |||||
| PlayRangeEndCallback=[](AInputS*){}; | PlayRangeEndCallback=[](AInputS*){}; | ||||
| } | } | ||||
| ~AInputS() {} | ~AInputS() {} | ||||
| void setAudioBuffer(AudioBuffer<float>* buf, int samplerate, int len) | void setAudioBuffer(AudioBuffer<float>* buf, int samplerate, int len) | ||||
| { | { | ||||
| m_afreader = nullptr; | m_afreader = nullptr; | ||||
| @@ -60,6 +61,7 @@ public: | |||||
| m_loop_enabled = true; | m_loop_enabled = true; | ||||
| m_crossfadebuf.setSize(info.nchannels, m_crossfadebuf.getNumSamples()); | m_crossfadebuf.setSize(info.nchannels, m_crossfadebuf.getNumSamples()); | ||||
| m_cached_file_range = { 0,len }; | m_cached_file_range = { 0,len }; | ||||
| seek(m_activerange.getStart()); | |||||
| updateXFadeCache(); | updateXFadeCache(); | ||||
| } | } | ||||
| bool openAudioFile(File file) override | bool openAudioFile(File file) override | ||||
| @@ -33,7 +33,7 @@ extern std::unique_ptr<PropertiesFile> g_propsfile; | |||||
| Control::Control(AudioFormatManager* afm) : m_afm(afm), m_bufferingthread("stretchbufferingthread") | Control::Control(AudioFormatManager* afm) : m_afm(afm), m_bufferingthread("stretchbufferingthread") | ||||
| { | { | ||||
| m_stretch_source = std::make_unique<MultiStretchAudioSource>(2,m_afm); | |||||
| m_stretch_source = std::make_unique<StretchAudioSource>(2,m_afm); | |||||
| wavinfo.samplerate=44100; | wavinfo.samplerate=44100; | ||||
| @@ -619,91 +619,3 @@ void Control::update_process_parameters() | |||||
| //if (player) | //if (player) | ||||
| // player->set_process_parameters(&ppar,&bbpar); | // player->set_process_parameters(&ppar,&bbpar); | ||||
| }; | }; | ||||
| AudioCallback::AudioCallback() : AudioIODeviceCallback(), | |||||
| m_writethread("audio_out_record_thread") | |||||
| { | |||||
| } | |||||
| void AudioCallback::audioDeviceAboutToStart(AudioIODevice * device) | |||||
| { | |||||
| m_debugcount = 0; | |||||
| m_outpos = 0.0; | |||||
| m_outsr = device->getCurrentSampleRate(); | |||||
| if (m_aviscomponent) | |||||
| m_aviscomponent->setNumChannels(m_numoutchans); | |||||
| if (m_bufferingsource) | |||||
| { | |||||
| if (m_prebufferthread->isThreadRunning()==false) | |||||
| m_prebufferthread->startThread(g_propsfile->getIntValue("prebufthreadpriority",5)); | |||||
| int bufsize = std::max(512, device->getCurrentBufferSizeSamples()); | |||||
| //Logger::writeToLog("Using buffer size " + String(bufsize)); | |||||
| m_bufferingsource->prepareToPlay(bufsize, m_outsr); | |||||
| } | |||||
| m_playing = true; | |||||
| //Logger::writeToLog("hw samplerate " + String(m_outsr)); | |||||
| } | |||||
| void AudioCallback::audioDeviceStopped() | |||||
| { | |||||
| m_writer = nullptr; | |||||
| if (m_writethread.isThreadRunning() == true) | |||||
| { | |||||
| if (m_writethread.stopThread(1000) == false) | |||||
| { | |||||
| Logger::writeToLog("OUCH, live output recording thread didn't stop cleanly!"); | |||||
| } | |||||
| } | |||||
| if (m_bufferingsource) | |||||
| { | |||||
| m_bufferingsource->releaseResources(); | |||||
| if (m_prebufferthread->isThreadRunning() == true) | |||||
| { | |||||
| if (m_prebufferthread->stopThread(1000) == false) | |||||
| Logger::writeToLog("OUCH, prebuffering thread did not stop cleanly!"); | |||||
| } | |||||
| } | |||||
| m_playing = false; | |||||
| } | |||||
| void AudioCallback::audioDeviceIOCallback(const float ** /*inputChannelData*/, int, float ** outputChannelData, int numOutputChannels, int numSamples) | |||||
| { | |||||
| if (m_bufferingsource == nullptr) | |||||
| return; | |||||
| AudioBuffer<float> buf(outputChannelData, numOutputChannels, numSamples); | |||||
| AudioSourceChannelInfo ainfo(buf); | |||||
| m_bufferingsource->getNextAudioBlock(ainfo); | |||||
| if (m_aviscomponent && m_aviscomponent->isVisible()) | |||||
| { | |||||
| m_aviscomponent->pushBuffer((const float**)outputChannelData, m_numoutchans, numSamples); | |||||
| } | |||||
| if (m_writer && m_is_recording == true) | |||||
| { | |||||
| m_writer->write((const float**)outputChannelData, numSamples); | |||||
| } | |||||
| m_outpos += (double)numSamples / m_outsr; | |||||
| } | |||||
| String AudioCallback::startRecording(File outfile) | |||||
| { | |||||
| WavAudioFormat wavformat; | |||||
| auto outstream = outfile.createOutputStream(); | |||||
| if (outstream == nullptr) | |||||
| return "Could not create output stream"; | |||||
| auto writer = wavformat.createWriterFor(outstream, m_outsr, m_numoutchans, 32, StringPairArray(), 0); | |||||
| if (writer != nullptr) | |||||
| { | |||||
| if (m_writethread.isThreadRunning()==false) | |||||
| m_writethread.startThread(); | |||||
| m_writer = std::make_unique<AudioFormatWriter::ThreadedWriter>(writer, m_writethread, 65536); | |||||
| m_is_recording = true; | |||||
| return String(); | |||||
| } | |||||
| return "Could not create audio writer"; | |||||
| } | |||||
| void AudioCallback::setNumOutchans(int numchans) | |||||
| { | |||||
| m_numoutchans = jlimit(2, g_maxnumoutchans, numchans); | |||||
| } | |||||
| @@ -26,36 +26,6 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||||
| #include "../JuceLibraryCode/JuceHeader.h" | #include "../JuceLibraryCode/JuceHeader.h" | ||||
| #include "../ps3_BufferingAudioSource.h" | #include "../ps3_BufferingAudioSource.h" | ||||
| class AudioCallback : public AudioIODeviceCallback | |||||
| { | |||||
| public: | |||||
| AudioCallback(); | |||||
| void audioDeviceAboutToStart(AudioIODevice* device) override; | |||||
| void audioDeviceStopped() override; | |||||
| void audioDeviceIOCallback (const float** inputChannelData, | |||||
| int numInputChannels, | |||||
| float** outputChannelData, | |||||
| int numOutputChannels, | |||||
| int numSamples) override; | |||||
| String startRecording(File outfile); | |||||
| std::atomic<bool> m_is_recording{ false }; | |||||
| AudioVisualiserComponent* m_aviscomponent = nullptr; | |||||
| std::unique_ptr<AudioFormatWriter::ThreadedWriter> m_writer; | |||||
| TimeSliceThread m_writethread; | |||||
| TimeSliceThread* m_prebufferthread = nullptr; | |||||
| MyBufferingAudioSource* m_bufferingsource = nullptr; | |||||
| MultiStretchAudioSource* m_sas = nullptr; | |||||
| std::atomic<bool> m_playing{false}; | |||||
| double m_outpos = 0.0; | |||||
| void setNumOutchans(int numchans); | |||||
| private: | |||||
| std::mutex m_mutex; | |||||
| int m_debugcount = 0; | |||||
| int m_outsr = 44100; | |||||
| int m_numoutchans = 2; | |||||
| }; | |||||
| class RenderInfo | class RenderInfo | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -102,7 +72,7 @@ public: | |||||
| void setFFTSize(double size); | void setFFTSize(double size); | ||||
| int getFFTSize() { return m_fft_size_to_use; } | int getFFTSize() { return m_fft_size_to_use; } | ||||
| void setOnsetDetection(double x); | void setOnsetDetection(double x); | ||||
| MultiStretchAudioSource* getStretchAudioSource() { return m_stretch_source.get(); } | |||||
| StretchAudioSource* getStretchAudioSource() { return m_stretch_source.get(); } | |||||
| void set_input_file(File filename, std::function<void(String)> cb);//return non empty String if the file cannot be opened | void set_input_file(File filename, std::function<void(String)> cb);//return non empty String if the file cannot be opened | ||||
| String get_input_filename(); | String get_input_filename(); | ||||
| String get_stretch_info(Range<double> playrange); | String get_stretch_info(Range<double> playrange); | ||||
| @@ -169,7 +139,7 @@ private: | |||||
| REALTYPE seek_pos; | REALTYPE seek_pos; | ||||
| AudioFormatManager* m_afm = nullptr; | AudioFormatManager* m_afm = nullptr; | ||||
| TimeSliceThread m_bufferingthread; | TimeSliceThread m_bufferingthread; | ||||
| std::unique_ptr<MultiStretchAudioSource> m_stretch_source; | |||||
| std::unique_ptr<StretchAudioSource> m_stretch_source; | |||||
| std::unique_ptr<MyBufferingAudioSource> m_buffering_source; | std::unique_ptr<MyBufferingAudioSource> m_buffering_source; | ||||
| int m_prebuffer_amount = 1; | int m_prebuffer_amount = 1; | ||||
| bool m_recreate_buffering_source = true; | bool m_recreate_buffering_source = true; | ||||
| @@ -123,6 +123,8 @@ void StretchAudioSource::setAudioBufferAsInputSource(AudioBuffer<float>* buf, in | |||||
| m_seekpos = 0.0; | m_seekpos = 0.0; | ||||
| m_lastinpos = 0.0; | m_lastinpos = 0.0; | ||||
| m_curfile = File(); | m_curfile = File(); | ||||
| if (m_playrange.isEmpty()) | |||||
| setPlayRange({ 0.0,1.0 }, true); | |||||
| } | } | ||||
| void StretchAudioSource::getNextAudioBlock(const AudioSourceChannelInfo & bufferToFill) | void StretchAudioSource::getNextAudioBlock(const AudioSourceChannelInfo & bufferToFill) | ||||
| @@ -40,6 +40,7 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor (Pa | |||||
| }; | }; | ||||
| m_wavecomponent.ShowFileCacheRange = true; | m_wavecomponent.ShowFileCacheRange = true; | ||||
| startTimer(1, 100); | startTimer(1, 100); | ||||
| m_wavecomponent.startTimer(100); | |||||
| } | } | ||||
| PaulstretchpluginAudioProcessorEditor::~PaulstretchpluginAudioProcessorEditor() | PaulstretchpluginAudioProcessorEditor::~PaulstretchpluginAudioProcessorEditor() | ||||
| @@ -114,7 +114,17 @@ void PaulstretchpluginAudioProcessor::changeProgramName (int index, const String | |||||
| void PaulstretchpluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) | void PaulstretchpluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) | ||||
| { | { | ||||
| if (m_using_memory_buffer == true) | if (m_using_memory_buffer == true) | ||||
| m_control->getStretchAudioSource()->setAudioBufferAsInputSource(&m_recbuffer, getSampleRate(), m_recbuffer.getNumSamples()); | |||||
| { | |||||
| int len = jlimit(100,m_recbuffer.getNumSamples(), m_rec_pos); | |||||
| m_control->getStretchAudioSource()->setAudioBufferAsInputSource(&m_recbuffer, | |||||
| getSampleRate(), | |||||
| len); | |||||
| auto ed = dynamic_cast<PaulstretchpluginAudioProcessorEditor*>(getActiveEditor()); | |||||
| if (ed) | |||||
| { | |||||
| ed->setAudioBuffer(&m_recbuffer, getSampleRate(), len); | |||||
| } | |||||
| } | |||||
| if (m_ready_to_play == false) | if (m_ready_to_play == false) | ||||
| { | { | ||||
| m_control->update_player_stretch(); | m_control->update_player_stretch(); | ||||
| @@ -180,7 +190,8 @@ bool PaulstretchpluginAudioProcessor::isBusesLayoutSupported (const BusesLayout& | |||||
| void PaulstretchpluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) | void PaulstretchpluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) | ||||
| { | { | ||||
| ScopedNoDenormals noDenormals; | |||||
| std::lock_guard<std::mutex> locker(m_mutex); | |||||
| ScopedNoDenormals noDenormals; | |||||
| const int totalNumInputChannels = getTotalNumInputChannels(); | const int totalNumInputChannels = getTotalNumInputChannels(); | ||||
| const int totalNumOutputChannels = getTotalNumOutputChannels(); | const int totalNumOutputChannels = getTotalNumOutputChannels(); | ||||
| @@ -245,6 +256,7 @@ void PaulstretchpluginAudioProcessor::setStateInformation (const void* data, int | |||||
| void PaulstretchpluginAudioProcessor::setRecordingEnabled(bool b) | void PaulstretchpluginAudioProcessor::setRecordingEnabled(bool b) | ||||
| { | { | ||||
| std::lock_guard<std::mutex> locker(m_mutex); | |||||
| if (b == true) | if (b == true) | ||||
| { | { | ||||
| m_is_recording = true; | m_is_recording = true; | ||||
| @@ -272,10 +284,11 @@ void PaulstretchpluginAudioProcessor::finishRecording(int lenrecording) | |||||
| { | { | ||||
| m_is_recording = false; | m_is_recording = false; | ||||
| m_control->getStretchAudioSource()->setAudioBufferAsInputSource(&m_recbuffer, getSampleRate(), lenrecording); | m_control->getStretchAudioSource()->setAudioBufferAsInputSource(&m_recbuffer, getSampleRate(), lenrecording); | ||||
| m_control->getStretchAudioSource()->setPlayRange({ *getFloatParameter(5),*getFloatParameter(6) }, true); | |||||
| auto ed = dynamic_cast<PaulstretchpluginAudioProcessorEditor*>(getActiveEditor()); | auto ed = dynamic_cast<PaulstretchpluginAudioProcessorEditor*>(getActiveEditor()); | ||||
| if (ed) | if (ed) | ||||
| { | { | ||||
| ed->setAudioBuffer(&m_recbuffer, getSampleRate(), m_rec_pos); | |||||
| ed->setAudioBuffer(&m_recbuffer, getSampleRate(), lenrecording); | |||||
| } | } | ||||
| } | } | ||||
| @@ -74,7 +74,7 @@ private: | |||||
| int m_rec_pos = 0; | int m_rec_pos = 0; | ||||
| void finishRecording(int lenrecorded); | void finishRecording(int lenrecorded); | ||||
| bool m_using_memory_buffer = true; | bool m_using_memory_buffer = true; | ||||
| std::mutex m_mutex; | |||||
| //============================================================================== | //============================================================================== | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PaulstretchpluginAudioProcessor) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PaulstretchpluginAudioProcessor) | ||||
| }; | }; | ||||
| @@ -1,7 +1,7 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||
| <JUCERPROJECT id="fn1Rg8" name="paulstretchplugin" displaySplashScreen="0" | <JUCERPROJECT id="fn1Rg8" name="paulstretchplugin" displaySplashScreen="0" | ||||
| reportAppUsage="1" splashScreenColour="Dark" projectType="audioplug" | |||||
| reportAppUsage="0" splashScreenColour="Dark" projectType="audioplug" | |||||
| version="1.0.0" bundleIdentifier="com.yourcompany.paulstretchplugin" | version="1.0.0" bundleIdentifier="com.yourcompany.paulstretchplugin" | ||||
| includeBinaryInAppConfig="1" cppLanguageStandard="latest" companyCopyright="" | includeBinaryInAppConfig="1" cppLanguageStandard="latest" companyCopyright="" | ||||
| buildVST="1" buildVST3="0" buildAU="1" buildAUv3="0" buildRTAS="0" | buildVST="1" buildVST3="0" buildAU="1" buildAUv3="0" buildRTAS="0" | ||||