Skip to content

Commit

Permalink
Fix several bugs, add audio player at bottom for sosci
Browse files Browse the repository at this point in the history
  • Loading branch information
jameshball committed Jan 6, 2025
1 parent ed027bb commit dd3d051
Show file tree
Hide file tree
Showing 22 changed files with 427 additions and 108 deletions.
1 change: 1 addition & 0 deletions Resources/svg/play.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions Resources/svg/repeat.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions Resources/svg/stop.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Source/CommonPluginEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ CommonPluginEditor::CommonPluginEditor(CommonAudioProcessor& p, juce::String app

recordingSettings.setLookAndFeel(&getLookAndFeel());
recordingSettings.setSize(300, 200);
recordingSettingsWindow.centreWithSize(300, 200);
recordingSettingsWindow.centreWithSize(300, 230);
#if JUCE_WINDOWS
// if not standalone, use native title bar for compatibility with DAWs
recordingSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
Expand Down
6 changes: 2 additions & 4 deletions Source/CommonPluginEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,13 @@ class CommonPluginEditor : public juce::AudioProcessorEditor {
RecordingSettings recordingSettings = RecordingSettings(audioProcessor.recordingParameters);
SettingsWindow recordingSettingsWindow = SettingsWindow("Recording Settings", recordingSettings);
VisualiserComponent visualiser{
audioProcessor.lastOpenedDirectory,
audioProcessor,
#if SOSCI_FEATURES
sharedTextureManager,
#endif
applicationFolder.getChildFile(ffmpegFileName),
audioProcessor.haltRecording,
audioProcessor.threadManager,
visualiserSettings,
audioProcessor.recordingParameters,
recordingSettings,
nullptr,
appName == "sosci"
};
Expand Down
34 changes: 34 additions & 0 deletions Source/CommonPluginProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "CommonPluginProcessor.h"
#include "CommonPluginEditor.h"
#include "audio/EffectParameter.h"
#include "components/AudioPlayerComponent.h"

//==============================================================================
CommonAudioProcessor::CommonAudioProcessor()
Expand Down Expand Up @@ -182,3 +183,36 @@ bool CommonAudioProcessor::hasEditor() const {
double CommonAudioProcessor::getSampleRate() {
return currentSampleRate;
}

void CommonAudioProcessor::loadAudioFile(const juce::File& file) {
auto stream = std::make_unique<juce::FileInputStream>(file);
if (stream->openedOk()) {
juce::SpinLock::ScopedLockType lock(wavParserLock);
wavParser = std::make_shared<WavParser>(*this, std::move(stream));

juce::SpinLock::ScopedLockType lock2(audioPlayerListenersLock);
for (auto listener : audioPlayerListeners) {
listener->parserChanged(wavParser);
}
}
}

void CommonAudioProcessor::stopAudioFile() {
juce::SpinLock::ScopedLockType lock(wavParserLock);
wavParser = nullptr;

juce::SpinLock::ScopedLockType lock2(audioPlayerListenersLock);
for (auto listener : audioPlayerListeners) {
listener->parserChanged(wavParser);
}
}

void CommonAudioProcessor::addAudioPlayerListener(AudioPlayerListener* listener) {
juce::SpinLock::ScopedLockType lock(audioPlayerListenersLock);
audioPlayerListeners.push_back(listener);
}

void CommonAudioProcessor::removeAudioPlayerListener(AudioPlayerListener* listener) {
juce::SpinLock::ScopedLockType lock(audioPlayerListenersLock);
audioPlayerListeners.erase(std::remove(audioPlayerListeners.begin(), audioPlayerListeners.end(), listener), audioPlayerListeners.end());
}
16 changes: 13 additions & 3 deletions Source/CommonPluginProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
#include "visualiser/VisualiserSettings.h"
#include "visualiser/RecordingSettings.h"
#include "audio/Effect.h"
#include "wav/WavParser.h"

//==============================================================================
/**
*/

class AudioPlayerListener;
class CommonAudioProcessor : public juce::AudioProcessor, public SampleRateManager
#if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension
Expand Down Expand Up @@ -54,6 +54,13 @@ class CommonAudioProcessor : public juce::AudioProcessor, public SampleRateMana
const juce::String getProgramName(int index) override;
void changeProgramName(int index, const juce::String& newName) override;
double getSampleRate() override;
void loadAudioFile(const juce::File& file);
void stopAudioFile();
void addAudioPlayerListener(AudioPlayerListener* listener);
void removeAudioPlayerListener(AudioPlayerListener* listener);

juce::SpinLock audioPlayerListenersLock;
std::vector<AudioPlayerListener*> audioPlayerListeners;

std::atomic<double> volume = 1.0;
std::atomic<double> threshold = 1.0;
Expand Down Expand Up @@ -82,6 +89,9 @@ class CommonAudioProcessor : public juce::AudioProcessor, public SampleRateMana
)
);

juce::SpinLock wavParserLock;
std::shared_ptr<WavParser> wavParser;

std::atomic<double> currentSampleRate = 0.0;
juce::SpinLock effectsLock;
VisualiserParameters visualiserParameters;
Expand Down
3 changes: 3 additions & 0 deletions Source/SosciPluginEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@ void SosciPluginEditor::resized() {
visualiserSettings.setSize(settingsArea.getWidth(), 550);
visualiserSettingsWrapper.setBounds(settingsArea);

if (area.getWidth() < 10 || area.getHeight() < 10) {
return;
}
visualiser.setBounds(area);
}
15 changes: 1 addition & 14 deletions Source/SosciPluginProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void SosciAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::M
auto inputArray = input.getArrayOfWritePointers();
auto outputArray = output.getArrayOfWritePointers();

juce::CriticalSection::ScopedLockType lock2(wavParserLock);
juce::SpinLock::ScopedLockType lock2(wavParserLock);
bool readingFromWav = wavParser != nullptr;

for (int sample = 0; sample < input.getNumSamples(); ++sample) {
Expand Down Expand Up @@ -175,19 +175,6 @@ void SosciAudioProcessor::setStateInformation(const void* data, int sizeInBytes)
}
}

void SosciAudioProcessor::loadAudioFile(const juce::File& file) {
auto stream = std::make_unique<juce::FileInputStream>(file);
if (stream->openedOk()) {
juce::CriticalSection::ScopedLockType lock(wavParserLock);
wavParser = std::make_unique<WavParser>(*this, std::move(stream));
}
}

void SosciAudioProcessor::stopAudioFile() {
juce::CriticalSection::ScopedLockType lock(wavParserLock);
wavParser = nullptr;
}

juce::AudioProcessorEditor* SosciAudioProcessor::createEditor() {
auto editor = new SosciPluginEditor(*this);
return editor;
Expand Down
7 changes: 0 additions & 7 deletions Source/SosciPluginProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,9 @@ class SosciAudioProcessor : public CommonAudioProcessor

void getStateInformation(juce::MemoryBlock& destData) override;
void setStateInformation(const void* data, int sizeInBytes) override;

void loadAudioFile(const juce::File& file);
void stopAudioFile();

juce::AudioProcessorEditor* createEditor() override;

private:
juce::CriticalSection wavParserLock;
std::unique_ptr<WavParser> wavParser;

//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SosciAudioProcessor)
};
6 changes: 0 additions & 6 deletions Source/audio/EffectParameter.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,6 @@ class FloatParameter : public juce::AudioProcessorParameterWithID {
// value is not necessarily in the range [min, max] so effect applications may need to clip to a valid range
std::atomic<float> value = 0.0;
juce::String label;

JUCE_HEAVYWEIGHT_LEAK_DETECTOR(FloatParameter)
};

class IntParameter : public juce::AudioProcessorParameterWithID {
Expand Down Expand Up @@ -253,8 +251,6 @@ class IntParameter : public juce::AudioProcessorParameterWithID {
private:
// value is not necessarily in the range [min, max] so effect applications may need to clip to a valid range
std::atomic<int> value = 0;

JUCE_HEAVYWEIGHT_LEAK_DETECTOR(IntParameter)
};

enum class LfoType : int {
Expand Down Expand Up @@ -404,6 +400,4 @@ class EffectParameter : public FloatParameter {
}

EffectParameter(juce::String name, juce::String description, juce::String id, int versionHint, float value, float min, float max, float step = 0.01, bool smoothValueChange = true) : FloatParameter(name, id, versionHint, value, min, max, step), smoothValueChange(smoothValueChange), description(description) {}

JUCE_HEAVYWEIGHT_LEAK_DETECTOR(EffectParameter)
};
146 changes: 146 additions & 0 deletions Source/components/AudioPlayerComponent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#include "AudioPlayerComponent.h"
#include "../CommonPluginProcessor.h"

AudioPlayerComponent::AudioPlayerComponent(CommonAudioProcessor& processor) : audioProcessor(processor) {
setOpaque(false);

audioProcessor.addAudioPlayerListener(this);

parser = audioProcessor.wavParser;

addAndMakeVisible(slider);
slider.setSliderStyle(juce::Slider::SliderStyle::LinearHorizontal);
slider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
slider.setOpaque(false);
slider.setRange(0, 1, 0.001);
slider.setLookAndFeel(&timelineLookAndFeel);
slider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::black);

slider.onValueChange = [this]() {
juce::SpinLock::ScopedLockType sl(audioProcessor.wavParserLock);

if (parser != nullptr) {
parser->setProgress(slider.getValue());
} else {
slider.setValue(0, juce::dontSendNotification);
}
};

addChildComponent(playButton);
addChildComponent(pauseButton);

playButton.setTooltip("Play audio file");
pauseButton.setTooltip("Pause audio file");

repeatButton.setToggleState(true, juce::dontSendNotification);

playButton.onClick = [this]() {
juce::SpinLock::ScopedLockType sl(audioProcessor.wavParserLock);

if (parser != nullptr) {
parser->setPaused(false);
playButton.setVisible(false);
pauseButton.setVisible(true);
}
};

pauseButton.onClick = [this]() {
juce::SpinLock::ScopedLockType sl(audioProcessor.wavParserLock);

if (parser != nullptr) {
parser->setPaused(true);
playButton.setVisible(true);
pauseButton.setVisible(false);
}
};

addAndMakeVisible(repeatButton);

repeatButton.setTooltip("Repeat audio file once it finishes playing");

repeatButton.onClick = [this]() {
juce::SpinLock::ScopedLockType sl(audioProcessor.wavParserLock);

if (parser != nullptr) {
parser->setLooping(repeatButton.getToggleState());
}
};

addAndMakeVisible(stopButton);

stopButton.setTooltip("Stop and close audio file");

stopButton.onClick = [this]() {
audioProcessor.stopAudioFile();
};

{
juce::SpinLock::ScopedLockType sl(audioProcessor.wavParserLock);
setup();
}
}

AudioPlayerComponent::~AudioPlayerComponent() {
audioProcessor.removeAudioPlayerListener(this);
juce::SpinLock::ScopedLockType sl(audioProcessor.wavParserLock);
if (parser != nullptr) {
parser->onProgress = nullptr;
}
}

// must hold lock
void AudioPlayerComponent::setup() {
if (parser != nullptr) {
slider.setVisible(true);
repeatButton.setVisible(true);
stopButton.setVisible(true);
parser->onProgress = [this](double progress) {
juce::WeakReference<AudioPlayerComponent> weakRef(this);
juce::MessageManager::callAsync([this, progress, weakRef]() {
if (weakRef) {
slider.setValue(progress, juce::dontSendNotification);
repaint();
}
});
};
playButton.setVisible(parser->isPaused());
pauseButton.setVisible(!parser->isPaused());
parser->setLooping(repeatButton.getToggleState());
} else {
slider.setVisible(false);
repeatButton.setVisible(false);
stopButton.setVisible(false);
slider.setValue(0);
playButton.setVisible(false);
pauseButton.setVisible(false);
}

parserWasNull = parser == nullptr;
}

void AudioPlayerComponent::setPaused(bool paused) {
if (paused) {
pauseButton.triggerClick();
} else {
playButton.triggerClick();
}
}

void AudioPlayerComponent::parserChanged(std::shared_ptr<WavParser> parser) {
this->parser = parser;
setup();
repaint();
}

void AudioPlayerComponent::resized() {
auto r = getLocalBounds();

auto playPauseBounds = r.removeFromLeft(25);
playButton.setBounds(playPauseBounds);
pauseButton.setBounds(playPauseBounds);
stopButton.setBounds(r.removeFromLeft(25));

repeatButton.setBounds(r.removeFromRight(25));

slider.setBounds(r);
}
Loading

0 comments on commit dd3d051

Please sign in to comment.