Fix several bugs, add audio player at bottom for sosci

pre-release-3
James H Ball 2025-01-06 18:27:23 +00:00
rodzic ed027bbfc4
commit dd3d051793
22 zmienionych plików z 427 dodań i 108 usunięć

Wyświetl plik

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M10,16.5L16,12L10,7.5V16.5Z" /></svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 273 B

Wyświetl plik

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6,5.75L10.25,10H7V16H13.5L15.5,18H7A2,2 0 0,1 5,16V10H1.75L6,5.75M18,18.25L13.75,14H17V8H10.5L8.5,6H17A2,2 0 0,1 19,8V14H22.25L18,18.25Z" /></svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 216 B

Wyświetl plik

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4M9,9V15H15V9" /></svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 257 B

Wyświetl plik

@ -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);

Wyświetl plik

@ -52,15 +52,13 @@ public:
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"
};

Wyświetl plik

@ -9,6 +9,7 @@
#include "CommonPluginProcessor.h"
#include "CommonPluginEditor.h"
#include "audio/EffectParameter.h"
#include "components/AudioPlayerComponent.h"
//==============================================================================
CommonAudioProcessor::CommonAudioProcessor()
@ -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());
}

Wyświetl plik

@ -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
@ -54,6 +54,13 @@ public:
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;
@ -82,6 +89,9 @@ public:
)
);
juce::SpinLock wavParserLock;
std::shared_ptr<WavParser> wavParser;
std::atomic<double> currentSampleRate = 0.0;
juce::SpinLock effectsLock;
VisualiserParameters visualiserParameters;

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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) {
@ -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;

Wyświetl plik

@ -30,16 +30,9 @@ public:
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)
};

Wyświetl plik

@ -129,8 +129,6 @@ private:
// 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 {
@ -253,8 +251,6 @@ public:
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 {
@ -404,6 +400,4 @@ public:
}
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)
};

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -0,0 +1,83 @@
#pragma once
#include <JuceHeader.h>
#include "../CommonPluginProcessor.h"
#include "../LookAndFeel.h"
#include "../wav/WavParser.h"
class TimelineLookAndFeel : public OscirenderLookAndFeel {
public:
TimelineLookAndFeel() {}
void drawLinearSlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPos, float minSliderPos, float maxSliderPos, const juce::Slider::SliderStyle style, juce::Slider& slider) override {
auto trackWidth = juce::jmin (6.0f, slider.isHorizontal() ? (float) height * 0.25f : (float) width * 0.25f);
juce::Point<float> startPoint (slider.isHorizontal() ? (float) x : (float) x + (float) width * 0.5f,
slider.isHorizontal() ? (float) y + (float) height * 0.5f : (float) (height + y));
juce::Point<float> endPoint (slider.isHorizontal() ? (float) (width + x) : startPoint.x,
slider.isHorizontal() ? startPoint.y : (float) y);
juce::Path backgroundTrack;
backgroundTrack.startNewSubPath (startPoint);
backgroundTrack.lineTo (endPoint);
g.setColour (slider.findColour (juce::Slider::backgroundColourId));
g.strokePath (backgroundTrack, { trackWidth, juce::PathStrokeType::curved, juce::PathStrokeType::rounded });
juce::Path valueTrack;
juce::Point<float> minPoint, maxPoint, thumbPoint;
auto kx = slider.isHorizontal() ? sliderPos : ((float) x + (float) width * 0.5f);
auto ky = slider.isHorizontal() ? ((float) y + (float) height * 0.5f) : sliderPos;
minPoint = startPoint;
maxPoint = { kx, ky };
auto thumbWidth = getSliderThumbRadius (slider);
valueTrack.startNewSubPath (minPoint);
valueTrack.lineTo (maxPoint);
g.setColour (slider.findColour (juce::Slider::trackColourId));
g.strokePath (valueTrack, { trackWidth, juce::PathStrokeType::curved, juce::PathStrokeType::rounded });
g.setColour (slider.findColour (juce::Slider::thumbColourId));
g.fillRect (juce::Rectangle<float> (thumbWidth / 2, 1.5 * thumbWidth).withCentre (maxPoint));
g.setColour(slider.findColour(sliderThumbOutlineColourId).withAlpha(slider.isEnabled() ? 1.0f : 0.5f));
g.drawRect(juce::Rectangle<float>(thumbWidth / 2, 1.5 * thumbWidth).withCentre(maxPoint));
}
};
class AudioPlayerListener {
public:
virtual void parserChanged(std::shared_ptr<WavParser> parser) = 0;
};
class CommonAudioProcessor;
class AudioPlayerComponent : public juce::Component, public AudioPlayerListener {
public:
AudioPlayerComponent(CommonAudioProcessor& processor);
~AudioPlayerComponent() override;
void resized() override;
void setup();
void parserChanged(std::shared_ptr<WavParser> parser) override;
void setPaused(bool paused);
private:
CommonAudioProcessor& audioProcessor;
bool parserWasNull = true;
std::shared_ptr<WavParser> parser;
TimelineLookAndFeel timelineLookAndFeel;
SvgButton playButton{ "Play", BinaryData::play_svg, juce::Colours::white, juce::Colours::white};
SvgButton pauseButton{ "Pause", BinaryData::pause_svg, juce::Colours::white, juce::Colours::white };
SvgButton repeatButton{ "Repeat", BinaryData::repeat_svg, juce::Colours::white, Colours::accentColor };
SvgButton stopButton{ "Stop", BinaryData::stop_svg, juce::Colours::white, juce::Colours::white };
juce::Slider slider;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioPlayerComponent)
JUCE_DECLARE_WEAK_REFERENCEABLE(AudioPlayerComponent)
};

Wyświetl plik

@ -37,18 +37,16 @@ SosciMainMenuBarModel::SosciMainMenuBarModel(SosciPluginEditor& e, SosciAudioPro
};
addMenuItem(0, "Open Audio File", [&]() {
fileChooser = std::make_unique<juce::FileChooser>("Open Audio File", processor.lastOpenedDirectory, "*.wav;*.aiff;*.flac;*.ogg");
fileChooser = std::make_unique<juce::FileChooser>("Open Audio File", processor.lastOpenedDirectory, "*.wav;*.aiff;*.flac;*.ogg;*.mp3");
auto flags = juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectFiles;
fileChooser->launchAsync(flags, [&](const juce::FileChooser& chooser) {
auto file = chooser.getResult();
if (file != juce::File()) {
processor.loadAudioFile(file);
processor.lastOpenedDirectory = file.getParentDirectory();
}
});
});
addMenuItem(0, "Stop Audio File", [&]() {
processor.stopAudioFile();
});
addMenuItem(0, "Open Project", [&]() { editor.openProject(); });
addMenuItem(0, "Save Project", [&]() { editor.saveProject(); });
addMenuItem(0, "Save Project As", [&]() { editor.saveProjectAs(); });

Wyświetl plik

@ -38,24 +38,6 @@ public:
juce::String compressionPreset = "fast";
int getCRF() {
double quality = juce::jlimit(0.0, 1.0, qualityEffect.getValue());
// mapping to 0-51 for ffmpeg's crf value
return 51 * (1.0 - quality) ;
}
bool recordingVideo() {
return recordVideo.getBoolValue();
}
bool recordingAudio() {
return recordAudio.getBoolValue();
}
juce::String getCompressionPreset() {
return compressionPreset;
}
void save(juce::XmlElement* xml) {
auto settingsXml = xml->createNewChildElement("recordingSettings");
recordAudio.save(settingsXml->createNewChildElement("recordAudio"));
@ -94,6 +76,24 @@ public:
void resized() override;
int getCRF() {
double quality = juce::jlimit(0.0, 1.0, parameters.qualityEffect.getValue());
// mapping to 0-51 for ffmpeg's crf value
return 51 * (1.0 - quality) ;
}
bool recordingVideo() {
return parameters.recordVideo.getBoolValue();
}
bool recordingAudio() {
return parameters.recordAudio.getBoolValue();
}
juce::String getCompressionPreset() {
return parameters.compressionPreset;
}
RecordingParameters& parameters;
private:

Wyświetl plik

@ -1,5 +1,7 @@
#include "../LookAndFeel.h"
#include "VisualiserComponent.h"
#include "../CommonPluginProcessor.h"
#include "BlurFragmentShader.glsl"
#include "BlurVertexShader.glsl"
#include "WideBlurFragmentShader.glsl"
@ -16,28 +18,24 @@
#include "TexturedVertexShader.glsl"
VisualiserComponent::VisualiserComponent(
juce::File& lastOpenedDirectory,
CommonAudioProcessor& processor,
#if SOSCI_FEATURES
SharedTextureManager& sharedTextureManager,
#endif
juce::File ffmpegFile,
std::function<void()>& haltRecording,
AudioBackgroundThreadManager& threadManager,
VisualiserSettings& settings,
RecordingParameters& recordingParameters,
RecordingSettings& recordingSettings,
VisualiserComponent* parent,
bool visualiserOnly
) : lastOpenedDirectory(lastOpenedDirectory),
) : audioProcessor(processor),
ffmpegFile(ffmpegFile),
#if SOSCI_FEATURES
sharedTextureManager(sharedTextureManager),
#endif
haltRecording(haltRecording),
settings(settings),
recordingParameters(recordingParameters),
threadManager(threadManager),
recordingSettings(recordingSettings),
visualiserOnly(visualiserOnly),
AudioBackgroundThread("VisualiserComponent" + juce::String(parent != nullptr ? " Child" : ""), threadManager),
AudioBackgroundThread("VisualiserComponent" + juce::String(parent != nullptr ? " Child" : ""), processor.threadManager),
parent(parent) {
#if SOSCI_FEATURES
addAndMakeVisible(ffmpegDownloader);
@ -45,11 +43,18 @@ VisualiserComponent::VisualiserComponent(
ffmpegDownloader.onSuccessfulDownload = [this] {
juce::MessageManager::callAsync([this] {
record.setEnabled(true);
juce::Timer::callAfterDelay(3000, [this] {
juce::MessageManager::callAsync([this] {
ffmpegDownloader.setVisible(false);
downloading = false;
resized();
});
});
});
};
#endif
haltRecording = [this] {
audioProcessor.haltRecording = [this] {
setRecording(false);
};
@ -110,6 +115,8 @@ VisualiserComponent::VisualiserComponent(
popoutWindow();
};
addAndMakeVisible(audioPlayer);
setFullScreen(false);
openGLContext.setRenderer(this);
@ -121,7 +128,7 @@ VisualiserComponent::VisualiserComponent(
VisualiserComponent::~VisualiserComponent() {
setRecording(false);
if (parent == nullptr) {
haltRecording = nullptr;
audioProcessor.haltRecording = nullptr;
}
openGLContext.detach();
setShouldBeRunning(false, [this] {
@ -255,6 +262,7 @@ void VisualiserComponent::setPaused(bool paused) {
active = !paused;
renderingSemaphore.release();
setShouldBeRunning(active);
audioPlayer.setPaused(paused);
repaint();
}
@ -289,8 +297,8 @@ void VisualiserComponent::setRecording(bool recording) {
if (recording) {
#if SOSCI_FEATURES
recordingVideo = recordingParameters.recordingVideo();
recordingAudio = recordingParameters.recordingAudio();
recordingVideo = recordingSettings.recordingVideo();
recordingAudio = recordingSettings.recordingAudio();
if (!recordingVideo && !recordingAudio) {
record.setToggleState(false, juce::NotificationType::dontSendNotification);
return;
@ -311,6 +319,9 @@ void VisualiserComponent::setRecording(bool recording) {
if (result == 1) {
record.setEnabled(false);
ffmpegDownloader.download();
ffmpegDownloader.setVisible(true);
downloading = true;
resized();
}
});
record.setToggleState(false, juce::NotificationType::dontSendNotification);
@ -325,10 +336,10 @@ void VisualiserComponent::setRecording(bool recording) {
" -s " + resolution +
" -i -" +
" -threads 4" +
" -preset " + recordingParameters.getCompressionPreset() +
" -preset " + recordingSettings.getCompressionPreset() +
" -y" +
" -pix_fmt yuv420p" +
" -crf " + juce::String(recordingParameters.getCRF()) +
" -crf " + juce::String(recordingSettings.getCRF()) +
" -vf vflip" +
" \"" + tempVideoFile->getFile().getFullPathName() + "\"";
@ -366,7 +377,7 @@ void VisualiserComponent::setRecording(bool recording) {
audioRecorder.stop();
juce::String extension = "wav";
#endif
chooser = std::make_unique<juce::FileChooser>("Save recording", lastOpenedDirectory, "*." + extension);
chooser = std::make_unique<juce::FileChooser>("Save recording", audioProcessor.lastOpenedDirectory, "*." + extension);
auto flags = juce::FileBrowserComponent::saveMode | juce::FileBrowserComponent::canSelectFiles | juce::FileBrowserComponent::warnAboutOverwriting;
#if SOSCI_FEATURES
@ -381,7 +392,7 @@ void VisualiserComponent::setRecording(bool recording) {
} else if (wasRecordingVideo) {
tempVideoFile->getFile().copyFileTo(file);
}
lastOpenedDirectory = file.getParentDirectory();
audioProcessor.lastOpenedDirectory = file.getParentDirectory();
}
});
#else
@ -389,7 +400,7 @@ void VisualiserComponent::setRecording(bool recording) {
auto file = chooser.getResult();
if (file != juce::File()) {
tempAudioFile->getFile().copyFileTo(file);
lastOpenedDirectory = file.getParentDirectory();
audioProcessor.lastOpenedDirectory = file.getParentDirectory();
}
});
#endif
@ -430,11 +441,13 @@ void VisualiserComponent::resized() {
stopwatch.setVisible(false);
}
#if SOSCI_FEATURES
if (child == nullptr) {
if (child == nullptr && downloading) {
auto bounds = buttons.removeFromRight(160);
ffmpegDownloader.setBounds(bounds.withSizeKeepingCentre(bounds.getWidth() - 10, bounds.getHeight() - 10));
}
#endif
buttons.removeFromRight(10); // padding
audioPlayer.setBounds(buttons);
viewportArea = area;
viewportChanged(viewportArea);
}
@ -447,15 +460,13 @@ void VisualiserComponent::popoutWindow() {
#endif
setRecording(false);
auto visualiser = new VisualiserComponent(
lastOpenedDirectory,
audioProcessor,
#if SOSCI_FEATURES
sharedTextureManager,
#endif
ffmpegFile,
haltRecording,
threadManager,
settings,
recordingParameters,
recordingSettings,
this
);
visualiser->settings.setLookAndFeel(&getLookAndFeel());
@ -481,12 +492,12 @@ void VisualiserComponent::childUpdated() {
#endif
record.setVisible(child == nullptr);
if (child != nullptr) {
haltRecording = [this] {
audioProcessor.haltRecording = [this] {
setRecording(false);
child->setRecording(false);
};
} else {
haltRecording = [this] {
audioProcessor.haltRecording = [this] {
setRecording(false);
};
}
@ -660,7 +671,7 @@ void VisualiserComponent::renderOpenGL() {
void VisualiserComponent::viewportChanged(juce::Rectangle<int> area) {
using namespace juce::gl;
if (openGLContext.isAttached()) {
float realWidth = area.getWidth() * renderScale;
float realHeight = area.getHeight() * renderScale;
@ -1175,11 +1186,7 @@ void VisualiserComponent::checkGLErrors(const juce::String& location) {
void VisualiserComponent::paint(juce::Graphics& g) {
#if SOSCI_FEATURES
g.setColour(settings.getScreenType() == ScreenType::Real ? Colours::dark : Colours::veryDark);
#else
g.setColour(Colours::veryDark);
#endif
g.fillRect(buttonRow);
if (!active) {
// draw a translucent overlay

Wyświetl plik

@ -12,6 +12,8 @@
#include "../components/DownloaderComponent.h"
#include "../concurrency/WriteProcess.h"
#include "../audio/AudioRecorder.h"
#include "../wav/WavParser.h"
#include "../components/AudioPlayerComponent.h"
#define FILE_RENDER_DUMMY 0
#define FILE_RENDER_PNG 1
@ -29,19 +31,18 @@ struct Texture {
int height;
};
class CommonAudioProcessor;
class VisualiserWindow;
class VisualiserComponent : public juce::Component, public AudioBackgroundThread, public juce::MouseListener, public juce::OpenGLRenderer, public juce::AsyncUpdater {
public:
VisualiserComponent(
juce::File& lastOpenedDirectory,
CommonAudioProcessor& processor,
#if SOSCI_FEATURES
SharedTextureManager& sharedTextureManager,
#endif
juce::File ffmpegFile,
std::function<void()>& haltRecording,
AudioBackgroundThreadManager& threadManager,
VisualiserSettings& settings,
RecordingParameters& recordingParameters,
RecordingSettings& recordingSettings,
VisualiserComponent* parent = nullptr,
bool visualiserOnly = false
);
@ -76,13 +77,13 @@ public:
std::atomic<bool> active = true;
private:
CommonAudioProcessor& audioProcessor;
float intensity;
const double FRAME_RATE = 60.0;
bool visualiserOnly;
std::function<void()>& haltRecording;
AudioBackgroundThreadManager& threadManager;
AudioPlayerComponent audioPlayer{audioProcessor};
SvgButton fullScreenButton{ "fullScreen", BinaryData::fullscreen_svg, juce::Colours::white, juce::Colours::white };
SvgButton popOutButton{ "popOut", BinaryData::open_in_new_svg, juce::Colours::white, juce::Colours::white };
@ -97,12 +98,13 @@ private:
std::function<void(FullScreenMode)> fullScreenCallback;
VisualiserSettings& settings;
RecordingParameters& recordingParameters;
RecordingSettings& recordingSettings;
juce::File ffmpegFile;
bool recordingAudio = true;
#if SOSCI_FEATURES
bool recordingVideo = true;
bool downloading = false;
long numFrames = 0;
std::vector<unsigned char> framePixels;
@ -144,7 +146,6 @@ private:
StopwatchComponent stopwatch;
SvgButton record{"Record", BinaryData::record_svg, juce::Colours::red, juce::Colours::red.withAlpha(0.01f)};
juce::File& lastOpenedDirectory;
std::unique_ptr<juce::FileChooser> chooser;
std::unique_ptr<juce::TemporaryFile> tempAudioFile;
AudioRecorder audioRecorder;
@ -260,7 +261,6 @@ private:
void viewportChanged(juce::Rectangle<int> area);
void renderScope(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints);
int renderAudioFile(juce::File& sourceAudio, int method = 1, int width = 1024, int height = 1024);
double getSweepIncrement();

Wyświetl plik

@ -307,11 +307,12 @@ private:
juce::Component& component;
};
class SettingsWindow : public juce::DocumentWindow {
class SettingsWindow : public juce::DialogWindow {
public:
SettingsWindow(juce::String name, juce::Component& component) : juce::DocumentWindow(name, Colours::darker, juce::DocumentWindow::TitleBarButtons::closeButton), component(component) {
juce::Component::addAndMakeVisible(viewport);
SettingsWindow(juce::String name, juce::Component& component) : juce::DialogWindow(name, Colours::darker, true, true), component(component) {
setContentComponent(&viewport);
setResizable(false, false);
viewport.setColour(juce::ScrollBar::trackColourId, juce::Colours::white);
viewport.setViewedComponent(&component, false);
viewport.setScrollBarsShown(true, false, true, false);
setAlwaysOnTop(true);
@ -321,10 +322,6 @@ public:
setVisible(false);
}
void resized() override {
viewport.setBounds(getLocalBounds());
}
private:
juce::Viewport viewport;
juce::Component& component;

Wyświetl plik

@ -9,7 +9,8 @@ WavParser::WavParser(CommonAudioProcessor& p, std::unique_ptr<juce::InputStream>
if (reader == nullptr) {
return;
}
auto* afSource = new juce::AudioFormatReaderSource(reader, true);
afSource = new juce::AudioFormatReaderSource(reader, true);
totalSamples = afSource->getTotalLength();
afSource->setLooping(true);
source = std::make_unique<juce::ResamplingAudioSource>(afSource, true);
fileSampleRate = reader->sampleRate;
@ -32,11 +33,23 @@ OsciPoint WavParser::getSample() {
if (currentSampleRate != audioProcessor.currentSampleRate) {
setSampleRate(audioProcessor.currentSampleRate);
}
if (source == nullptr) {
if (source == nullptr || paused) {
return OsciPoint();
}
source->getNextAudioBlock(juce::AudioSourceChannelInfo(audioBuffer));
currentSample += source->getResamplingRatio();
counter++;
if (currentSample >= totalSamples && afSource->isLooping()) {
currentSample = 0;
counter = 0;
afSource->setNextReadPosition(0);
}
if (counter % currentSampleRate == 0) {
if (onProgress != nullptr) {
onProgress((double)currentSample / totalSamples);
}
}
if (audioBuffer.getNumChannels() == 1) {
return OsciPoint(audioBuffer.getSample(0, 0), audioBuffer.getSample(0, 0));
@ -44,3 +57,29 @@ OsciPoint WavParser::getSample() {
return OsciPoint(audioBuffer.getSample(0, 0), audioBuffer.getSample(1, 0));
}
}
void WavParser::setProgress(double progress) {
if (source == nullptr) {
return;
}
afSource->setNextReadPosition(progress * totalSamples);
currentSample = progress * totalSamples;
}
void WavParser::setLooping(bool looping) {
afSource->setLooping(looping);
this->looping = looping;
}
bool WavParser::isLooping() {
return looping;
}
void WavParser::setPaused(bool paused) {
this->paused = paused;
counter = 0;
}
bool WavParser::isPaused() {
return paused;
}

Wyświetl plik

@ -10,12 +10,25 @@ public:
OsciPoint getSample();
void setProgress(double progress);
void setPaused(bool paused);
bool isPaused();
void setLooping(bool looping);
bool isLooping();
std::function<void(double)> onProgress;
private:
void setSampleRate(double sampleRate);
juce::AudioFormatReaderSource* afSource;
bool looping = true;
std::unique_ptr<juce::ResamplingAudioSource> source;
juce::AudioBuffer<float> audioBuffer;
int currentSample = 0;
long totalSamples;
long counter = 0;
std::atomic<double> currentSample = 0;
std::atomic<bool> paused = false;
int fileSampleRate;
int currentSampleRate;
CommonAudioProcessor& audioProcessor;

Wyświetl plik

@ -58,11 +58,14 @@
<FILE id="pSc1mq" name="osci.svg" compile="0" resource="1" file="Resources/svg/osci.svg"/>
<FILE id="f2D5tv" name="pause.svg" compile="0" resource="1" file="Resources/svg/pause.svg"/>
<FILE id="D2AI1b" name="pencil.svg" compile="0" resource="1" file="Resources/svg/pencil.svg"/>
<FILE id="sfWuFd" name="play.svg" compile="0" resource="1" file="Resources/svg/play.svg"/>
<FILE id="PFc2q2" name="random.svg" compile="0" resource="1" file="Resources/svg/random.svg"/>
<FILE id="CE6di2" name="range.svg" compile="0" resource="1" file="Resources/svg/range.svg"/>
<FILE id="n79IAy" name="record.svg" compile="0" resource="1" file="Resources/svg/record.svg"/>
<FILE id="vOGcgi" name="repeat.svg" compile="0" resource="1" file="Resources/svg/repeat.svg"/>
<FILE id="OaqZb1" name="right_arrow.svg" compile="0" resource="1" file="Resources/svg/right_arrow.svg"/>
<FILE id="yiDo4s" name="spout.svg" compile="0" resource="1" file="Resources/svg/spout.svg"/>
<FILE id="EOWhGi" name="stop.svg" compile="0" resource="1" file="Resources/svg/stop.svg"/>
<FILE id="rXjNlx" name="threshold.svg" compile="0" resource="1" file="Resources/svg/threshold.svg"/>
<FILE id="rFYmV8" name="timer.svg" compile="0" resource="1" file="Resources/svg/timer.svg"/>
<FILE id="qC6QiP" name="volume.svg" compile="0" resource="1" file="Resources/svg/volume.svg"/>
@ -148,6 +151,10 @@
file="Source/components/AboutComponent.cpp"/>
<FILE id="vDlOTn" name="AboutComponent.h" compile="0" resource="0"
file="Source/components/AboutComponent.h"/>
<FILE id="xxiMAy" name="AudioPlayerComponent.cpp" compile="1" resource="0"
file="Source/components/AudioPlayerComponent.cpp"/>
<FILE id="DSvDMv" name="AudioPlayerComponent.h" compile="0" resource="0"
file="Source/components/AudioPlayerComponent.h"/>
<FILE id="kUinTt" name="ComponentList.cpp" compile="1" resource="0"
file="Source/components/ComponentList.cpp"/>
<FILE id="HGTPEW" name="ComponentList.h" compile="0" resource="0" file="Source/components/ComponentList.h"/>

Wyświetl plik

@ -53,11 +53,14 @@
<FILE id="pSc1mq" name="osci.svg" compile="0" resource="1" file="Resources/svg/osci.svg"/>
<FILE id="f2D5tv" name="pause.svg" compile="0" resource="1" file="Resources/svg/pause.svg"/>
<FILE id="D2AI1b" name="pencil.svg" compile="0" resource="1" file="Resources/svg/pencil.svg"/>
<FILE id="Tf7MT4" name="play.svg" compile="0" resource="1" file="Resources/svg/play.svg"/>
<FILE id="PFc2q2" name="random.svg" compile="0" resource="1" file="Resources/svg/random.svg"/>
<FILE id="CE6di2" name="range.svg" compile="0" resource="1" file="Resources/svg/range.svg"/>
<FILE id="n79IAy" name="record.svg" compile="0" resource="1" file="Resources/svg/record.svg"/>
<FILE id="WDxwSi" name="repeat.svg" compile="0" resource="1" file="Resources/svg/repeat.svg"/>
<FILE id="OaqZb1" name="right_arrow.svg" compile="0" resource="1" file="Resources/svg/right_arrow.svg"/>
<FILE id="z3A7FT" name="spout.svg" compile="0" resource="1" file="Resources/svg/spout.svg"/>
<FILE id="wD6mre" name="stop.svg" compile="0" resource="1" file="Resources/svg/stop.svg"/>
<FILE id="rXjNlx" name="threshold.svg" compile="0" resource="1" file="Resources/svg/threshold.svg"/>
<FILE id="rFYmV8" name="timer.svg" compile="0" resource="1" file="Resources/svg/timer.svg"/>
<FILE id="qC6QiP" name="volume.svg" compile="0" resource="1" file="Resources/svg/volume.svg"/>
@ -102,6 +105,10 @@
file="Source/components/AboutComponent.cpp"/>
<FILE id="FtOv3F" name="AboutComponent.h" compile="0" resource="0"
file="Source/components/AboutComponent.h"/>
<FILE id="bbqJIb" name="AudioPlayerComponent.cpp" compile="1" resource="0"
file="Source/components/AudioPlayerComponent.cpp"/>
<FILE id="UJoX67" name="AudioPlayerComponent.h" compile="0" resource="0"
file="Source/components/AudioPlayerComponent.h"/>
<FILE id="aihIvJ" name="DownloaderComponent.cpp" compile="1" resource="0"
file="Source/components/DownloaderComponent.cpp"/>
<FILE id="wBQEfb" name="DownloaderComponent.h" compile="0" resource="0"
@ -198,7 +205,7 @@
</GROUP>
</MAINGROUP>
<JUCEOPTIONS JUCE_STRICT_REFCOUNTEDPOINTER="1" JUCE_VST3_CAN_REPLACE_VST2="0"
JUCE_WIN_PER_MONITOR_DPI_AWARE="0" JUCE_WEB_BROWSER="0"/>
JUCE_WIN_PER_MONITOR_DPI_AWARE="0" JUCE_WEB_BROWSER="0" JUCE_USE_MP3AUDIOFORMAT="1"/>
<EXPORTFORMATS>
<LINUX_MAKE targetFolder="Builds/sosci/LinuxMakefile" smallIcon="KigaoN"
bigIcon="KigaoN">