Add sweep / V-T functionality and update mac icon

pre-release-3
James H Ball 2025-01-03 16:37:36 +00:00
rodzic 587e82e7a6
commit bfe497ca72
12 zmienionych plików z 126 dodań i 22 usunięć

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 45 KiB

Wyświetl plik

@ -47,8 +47,8 @@ CommonPluginEditor::CommonPluginEditor(CommonAudioProcessor& p, juce::String app
}; };
visualiserSettings.setLookAndFeel(&getLookAndFeel()); visualiserSettings.setLookAndFeel(&getLookAndFeel());
visualiserSettings.setSize(550, 600); visualiserSettings.setSize(550, 550);
visualiserSettingsWindow.centreWithSize(550, 450); visualiserSettingsWindow.centreWithSize(550, 400);
#if JUCE_WINDOWS #if JUCE_WINDOWS
// if not standalone, use native title bar for compatibility with DAWs // if not standalone, use native title bar for compatibility with DAWs
visualiserSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone); visualiserSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);

Wyświetl plik

@ -25,7 +25,6 @@ CommonAudioProcessor::CommonAudioProcessor()
} }
effects.push_back(visualiserParameters.smoothEffect); effects.push_back(visualiserParameters.smoothEffect);
permanentEffects.push_back(visualiserParameters.smoothEffect);
for (auto parameter : visualiserParameters.booleans) { for (auto parameter : visualiserParameters.booleans) {
booleanParameters.push_back(parameter); booleanParameters.push_back(parameter);

Wyświetl plik

@ -328,7 +328,7 @@ class EffectParameter : public FloatParameter {
public: public:
std::atomic<bool> smoothValueChange = true; std::atomic<bool> smoothValueChange = true;
LfoTypeParameter* lfo = new LfoTypeParameter(name + " LFO", paramID + "Lfo", getVersionHint(), 1); LfoTypeParameter* lfo = new LfoTypeParameter(name + " LFO", paramID + "Lfo", getVersionHint(), 1);
FloatParameter* lfoRate = new FloatParameter(name + " LFO Rate", paramID + "LfoRate", getVersionHint(), 1.0f, 0.0f, 1000.0f, 0.01f, "Hz"); FloatParameter* lfoRate = new FloatParameter(name + " LFO Rate", paramID + "LfoRate", getVersionHint(), 1.0f, 0.0f, 10000.0f, 0.01f, "Hz");
BooleanParameter* sidechain = new BooleanParameter(name + " Sidechain Enabled", paramID + "Sidechain", getVersionHint(), false, "Toggles " + name + " Sidechain."); BooleanParameter* sidechain = new BooleanParameter(name + " Sidechain Enabled", paramID + "Sidechain", getVersionHint(), false, "Toggles " + name + " Sidechain.");
std::atomic<float> phase = 0.0f; std::atomic<float> phase = 0.0f;
juce::String description; juce::String description;

Wyświetl plik

@ -25,6 +25,7 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
"A huge thank you to:\n" "A huge thank you to:\n"
"DJ_Level_3, for contributing several features to osci-render\n" "DJ_Level_3, for contributing several features to osci-render\n"
"BUS ERROR Collective, for providing the source code for the Hilligoss encoder\n" "BUS ERROR Collective, for providing the source code for the Hilligoss encoder\n"
"Jean Perbet (@jeanprbt) for the osci-render macOS icon\n"
"All the community, for suggesting features and reporting issues!\n\n" "All the community, for suggesting features and reporting issues!\n\n"
"I am open for commissions! Email me at james@ball.sh." "I am open for commissions! Email me at james@ball.sh."
); );

Wyświetl plik

@ -120,11 +120,42 @@ void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
xSamples.clear(); xSamples.clear();
ySamples.clear(); ySamples.clear();
zSamples.clear(); zSamples.clear();
for (auto& point : points) {
OsciPoint smoothPoint = settings.parameters.smoothEffect->apply(0, point); if (settings.isSweepEnabled()) {
xSamples.push_back(smoothPoint.x); double sweepIncrement = getSweepIncrement();
ySamples.push_back(smoothPoint.y); long samplesPerSweep = sampleRate * settings.getSweepSeconds();
zSamples.push_back(smoothPoint.z);
double triggerValue = settings.getTriggerValue();
bool belowTrigger = false;
for (auto& point : points) {
OsciPoint smoothPoint = settings.parameters.smoothEffect->apply(0, point);
long samplePosition = sampleCount - lastTriggerPosition;
double startPoint = 1.135;
double sweep = samplePosition * sweepIncrement * 2 * startPoint - startPoint;
double value = smoothPoint.x;
if (sweep > startPoint && belowTrigger && value >= triggerValue) {
lastTriggerPosition = sampleCount;
}
belowTrigger = value < triggerValue;
xSamples.push_back(sweep);
ySamples.push_back(value);
zSamples.push_back(1);
sampleCount++;
}
} else {
for (auto& point : points) {
OsciPoint smoothPoint = settings.parameters.smoothEffect->apply(0, point);
xSamples.push_back(smoothPoint.x);
ySamples.push_back(smoothPoint.y);
zSamples.push_back(smoothPoint.z);
}
} }
sampleBufferCount++; sampleBufferCount++;
@ -137,7 +168,25 @@ void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
smoothedZSamples.resize(newResampledSize); smoothedZSamples.resize(newResampledSize);
smoothedZSamples.resize(newResampledSize); smoothedZSamples.resize(newResampledSize);
xResampler.process(xSamples.data(), smoothedXSamples.data(), xSamples.size()); if (settings.isSweepEnabled()) {
// interpolate between sweep values to avoid any artifacts from quickly going from one sweep to the next
for (int i = 0; i < newResampledSize; ++i) {
int index = i / RESAMPLE_RATIO;
if (index < xSamples.size() - 1) {
double thisSample = xSamples[index];
double nextSample = xSamples[index + 1];
if (nextSample > thisSample) {
smoothedXSamples[i] = xSamples[index] + (i % (int) RESAMPLE_RATIO) * (nextSample - thisSample) / RESAMPLE_RATIO;
} else {
smoothedXSamples[i] = xSamples[index];
}
} else {
smoothedXSamples[i] = xSamples[index];
}
}
} else {
xResampler.process(xSamples.data(), smoothedXSamples.data(), xSamples.size());
}
yResampler.process(ySamples.data(), smoothedYSamples.data(), ySamples.size()); yResampler.process(ySamples.data(), smoothedYSamples.data(), ySamples.size());
zResampler.process(zSamples.data(), smoothedZSamples.data(), zSamples.size()); zResampler.process(zSamples.data(), smoothedZSamples.data(), zSamples.size());
} }
@ -167,6 +216,10 @@ void VisualiserComponent::stopTask() {
renderingSemaphore.release(); renderingSemaphore.release();
} }
double VisualiserComponent::getSweepIncrement() {
return 1.0 / (sampleRate * settings.getSweepSeconds());
}
void VisualiserComponent::setPaused(bool paused) { void VisualiserComponent::setPaused(bool paused) {
active = !paused; active = !paused;
renderingSemaphore.release(); renderingSemaphore.release();
@ -506,7 +559,11 @@ void VisualiserComponent::renderOpenGL() {
} }
if (recordingAudio) { if (recordingAudio) {
audioRecorder.audioThreadCallback(xSamples, ySamples); if (settings.isSweepEnabled()) {
audioRecorder.audioThreadCallback(ySamples, ySamples);
} else {
audioRecorder.audioThreadCallback(xSamples, ySamples);
}
} }
} }
@ -753,7 +810,8 @@ void VisualiserComponent::drawLine(const std::vector<float>& xPoints, const std:
using namespace juce::gl; using namespace juce::gl;
setAdditiveBlending(); setAdditiveBlending();
// TODO: need to add the last point from the previous frame to the start of the frame so they connect?
int nPoints = xPoints.size(); int nPoints = xPoints.size();
// Without this, there's an access violation that seems to occur only on some systems // Without this, there's an access violation that seems to occur only on some systems

Wyświetl plik

@ -152,6 +152,7 @@ private:
int nEdges = 0; int nEdges = 0;
juce::CriticalSection samplesLock; juce::CriticalSection samplesLock;
long sampleCount = 0;
std::vector<float> xSamples{2}; std::vector<float> xSamples{2};
std::vector<float> ySamples{2}; std::vector<float> ySamples{2};
std::vector<float> zSamples{2}; std::vector<float> zSamples{2};
@ -160,6 +161,7 @@ private:
std::vector<float> smoothedZSamples; std::vector<float> smoothedZSamples;
int sampleBufferCount = 0; int sampleBufferCount = 0;
int prevSampleBufferCount = 0; int prevSampleBufferCount = 0;
long lastTriggerPosition = 0;
std::vector<float> scratchVertices; std::vector<float> scratchVertices;
std::vector<float> fullScreenQuad; std::vector<float> fullScreenQuad;
@ -242,6 +244,8 @@ private:
void renderScope(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints); 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); int renderAudioFile(juce::File& sourceAudio, int method = 1, int width = 1024, int height = 1024);
double getSweepIncrement();
Texture createScreenTexture(); Texture createScreenTexture();
Texture createReflectionTexture(); Texture createReflectionTexture();

Wyświetl plik

@ -13,7 +13,8 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
addAndMakeVisible(glow); addAndMakeVisible(glow);
addAndMakeVisible(ambient); addAndMakeVisible(ambient);
addAndMakeVisible(smooth); addAndMakeVisible(smooth);
addChildComponent(sweepMs); addAndMakeVisible(sweepMs);
addAndMakeVisible(triggerValue);
addAndMakeVisible(upsamplingToggle); addAndMakeVisible(upsamplingToggle);
addAndMakeVisible(sweepToggle); addAndMakeVisible(sweepToggle);
addAndMakeVisible(screenTypeLabel); addAndMakeVisible(screenTypeLabel);
@ -37,9 +38,16 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
ambient.setSliderOnValueChange(); ambient.setSliderOnValueChange();
smooth.setSliderOnValueChange(); smooth.setSliderOnValueChange();
sweepMs.setSliderOnValueChange(); sweepMs.setSliderOnValueChange();
triggerValue.setSliderOnValueChange();
sweepMs.setEnabled(sweepToggle.getToggleState());
triggerValue.setEnabled(sweepToggle.getToggleState());
sweepMs.slider.setSkewFactorFromMidPoint(100);
sweepToggle.onClick = [this] { sweepToggle.onClick = [this] {
sweepMs.setVisible(sweepToggle.getToggleState()); sweepMs.setEnabled(sweepToggle.getToggleState());
triggerValue.setEnabled(sweepToggle.getToggleState());
resized(); resized();
}; };
} }
@ -68,7 +76,6 @@ void VisualiserSettings::resized() {
upsamplingToggle.setBounds(area.removeFromTop(rowHeight)); upsamplingToggle.setBounds(area.removeFromTop(rowHeight));
sweepToggle.setBounds(area.removeFromTop(rowHeight)); sweepToggle.setBounds(area.removeFromTop(rowHeight));
if (sweepToggle.getToggleState()) { sweepMs.setBounds(area.removeFromTop(rowHeight));
sweepMs.setBounds(area.removeFromTop(rowHeight)); triggerValue.setBounds(area.removeFromTop(rowHeight));
}
} }

Wyświetl plik

@ -171,11 +171,19 @@ public:
"Sweep (ms)", "Sweep (ms)",
"The number of milliseconds it takes for the oscilloscope to sweep from left to right.", "The number of milliseconds it takes for the oscilloscope to sweep from left to right.",
"sweepMs", "sweepMs",
VERSION_HINT, 0.3, 0.0, 1.0 VERSION_HINT, 30.0, 0.0, 1000.0
)
);
std::shared_ptr<Effect> triggerValueEffect = std::make_shared<Effect>(
new EffectParameter(
"Trigger Value",
"The trigger value sets the signal level that starts waveform capture to display a stable waveform.",
"triggerValue",
VERSION_HINT, 0.0, -1.0, 1.0
) )
); );
std::vector<std::shared_ptr<Effect>> effects = {persistenceEffect, hueEffect, intensityEffect, saturationEffect, focusEffect, noiseEffect, glowEffect, ambientEffect, sweepMsEffect}; std::vector<std::shared_ptr<Effect>> effects = {persistenceEffect, hueEffect, intensityEffect, saturationEffect, focusEffect, noiseEffect, glowEffect, ambientEffect, sweepMsEffect, triggerValueEffect};
std::vector<BooleanParameter*> booleans = {upsamplingEnabled, visualiserFullScreen, sweepEnabled}; std::vector<BooleanParameter*> booleans = {upsamplingEnabled, visualiserFullScreen, sweepEnabled};
std::vector<IntParameter*> integers = {screenType}; std::vector<IntParameter*> integers = {screenType};
}; };
@ -226,6 +234,18 @@ public:
bool getUpsamplingEnabled() { bool getUpsamplingEnabled() {
return parameters.upsamplingEnabled->getBoolValue(); return parameters.upsamplingEnabled->getBoolValue();
} }
bool isSweepEnabled() {
return parameters.sweepEnabled->getBoolValue();
}
double getSweepSeconds() {
return parameters.sweepMsEffect->getActualValue() / 1000.0;
}
double getTriggerValue() {
return parameters.triggerValueEffect->getActualValue();
}
VisualiserParameters& parameters; VisualiserParameters& parameters;
int numChannels; int numChannels;
@ -241,6 +261,7 @@ private:
EffectComponent ambient{*parameters.ambientEffect}; EffectComponent ambient{*parameters.ambientEffect};
EffectComponent smooth{*parameters.smoothEffect}; EffectComponent smooth{*parameters.smoothEffect};
EffectComponent sweepMs{*parameters.sweepMsEffect}; EffectComponent sweepMs{*parameters.sweepMsEffect};
EffectComponent triggerValue{*parameters.triggerValueEffect};
juce::Label screenTypeLabel{"Screen Type", "Screen Type"}; juce::Label screenTypeLabel{"Screen Type", "Screen Type"};
juce::ComboBox screenType; juce::ComboBox screenType;
@ -257,7 +278,7 @@ public:
juce::Component::addAndMakeVisible(viewport); juce::Component::addAndMakeVisible(viewport);
setResizable(false, false); setResizable(false, false);
viewport.setViewedComponent(&component, false); viewport.setViewedComponent(&component, false);
viewport.setScrollBarsShown(false, false, true, false); viewport.setScrollBarsShown(true, false, true, false);
setAlwaysOnTop(true); setAlwaysOnTop(true);
} }

Wyświetl plik

@ -41,6 +41,15 @@ else
PROJUCER_PATH="$ROOT/ci/bin/JUCE/Projucer.exe" PROJUCER_PATH="$ROOT/ci/bin/JUCE/Projucer.exe"
fi fi
# Get the Spout SDK and SpoutLibrary.dll
if [ "$OS" = "win" ]; then
curl -s -S -L https://github.com/leadedge/Spout2/releases/download/2.007.015/Spout-SDK-binaries_2-007-015.zip -o Spout.zip
unzip Spout.zip
ls -R
# move to system32
cp Spout-SDK-binaries/2-007-015/Libs/MD/bin/SpoutLibrary.dll /c/Windows/System32
fi
# Set global path # Set global path
GLOBAL_PATH_COMMAND="$PROJUCER_PATH --set-global-search-path $PROJUCER_OS 'defaultJuceModulePath' '$ROOT/ci/bin/JUCE/modules'" GLOBAL_PATH_COMMAND="$PROJUCER_PATH --set-global-search-path $PROJUCER_OS 'defaultJuceModulePath' '$ROOT/ci/bin/JUCE/modules'"
eval "$GLOBAL_PATH_COMMAND" eval "$GLOBAL_PATH_COMMAND"

Wyświetl plik

@ -24,6 +24,7 @@
</GROUP> </GROUP>
<GROUP id="{525C568C-29E9-D0A2-9773-8A04981C5575}" name="images"> <GROUP id="{525C568C-29E9-D0A2-9773-8A04981C5575}" name="images">
<FILE id="jI9VSZ" name="logo.png" compile="0" resource="1" file="Resources/images/logo.png"/> <FILE id="jI9VSZ" name="logo.png" compile="0" resource="1" file="Resources/images/logo.png"/>
<FILE id="aZXbCi" name="osci_mac.png" compile="0" resource="1" file="Resources/images/osci_mac.png"/>
</GROUP> </GROUP>
<GROUP id="{C2609827-4F4A-1ADA-8BA1-A40C1D92649C}" name="lua"> <GROUP id="{C2609827-4F4A-1ADA-8BA1-A40C1D92649C}" name="lua">
<FILE id="xANsA8" name="demo.lua" compile="0" resource="1" file="Resources/lua/demo.lua"/> <FILE id="xANsA8" name="demo.lua" compile="0" resource="1" file="Resources/lua/demo.lua"/>
@ -775,8 +776,8 @@
</MODULEPATHS> </MODULEPATHS>
</VS2022> </VS2022>
<XCODE_MAC targetFolder="Builds/osci-render/MacOSX" extraLinkerFlags="-Wl,-weak_reference_mismatches,weak" <XCODE_MAC targetFolder="Builds/osci-render/MacOSX" extraLinkerFlags="-Wl,-weak_reference_mismatches,weak"
extraDefs="JUCE_SILENCE_XCODE_15_LINKER_WARNING=1" smallIcon="pSc1mq" extraDefs="JUCE_SILENCE_XCODE_15_LINKER_WARNING=1" smallIcon="aZXbCi"
bigIcon="pSc1mq" applicationCategory="public.app-category.music" bigIcon="aZXbCi" applicationCategory="public.app-category.music"
microphonePermissionNeeded="1" frameworkSearchPaths="../../../External/syphon" microphonePermissionNeeded="1" frameworkSearchPaths="../../../External/syphon"
embeddedFrameworks="../../../External/syphon/Syphon.framework" embeddedFrameworks="../../../External/syphon/Syphon.framework"
extraCustomFrameworks="../../../External/syphon/Syphon.framework" extraCustomFrameworks="../../../External/syphon/Syphon.framework"
@ -787,6 +788,8 @@
codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/> codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/>
<CONFIGURATION name="Release" targetName="osci-render" customXcodeFlags="LD_RUNPATH_SEARCH_PATHS = '@executable_path/../Frameworks' '@executable_path/',CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO,OTHER_CODE_SIGN_FLAGS = --timestamp --force --deep" <CONFIGURATION name="Release" targetName="osci-render" customXcodeFlags="LD_RUNPATH_SEARCH_PATHS = '@executable_path/../Frameworks' '@executable_path/',CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO,OTHER_CODE_SIGN_FLAGS = --timestamp --force --deep"
codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/> codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/>
<CONFIGURATION name="Release (Development)" targetName="osci-render" customXcodeFlags="LD_RUNPATH_SEARCH_PATHS = '@executable_path/../Frameworks' '@executable_path/',OTHER_CODE_SIGN_FLAGS = --timestamp --force --deep"
codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/>
</CONFIGURATIONS> </CONFIGURATIONS>
<MODULEPATHS> <MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/> <MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>

Wyświetl plik

@ -263,6 +263,8 @@
codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/> codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/>
<CONFIGURATION name="Release" targetName="sosci" customXcodeFlags="LD_RUNPATH_SEARCH_PATHS = '@executable_path/../Frameworks' '@executable_path/',CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO,OTHER_CODE_SIGN_FLAGS = --timestamp --force --deep" <CONFIGURATION name="Release" targetName="sosci" customXcodeFlags="LD_RUNPATH_SEARCH_PATHS = '@executable_path/../Frameworks' '@executable_path/',CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO,OTHER_CODE_SIGN_FLAGS = --timestamp --force --deep"
codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/> codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/>
<CONFIGURATION name="Release (Development)" targetName="sosci" customXcodeFlags="LD_RUNPATH_SEARCH_PATHS = '@executable_path/../Frameworks' '@executable_path/',OTHER_CODE_SIGN_FLAGS = --timestamp --force --deep"
codeSigningIdentity="Developer ID Application: James Ball (D86A3M3H2L)"/>
</CONFIGURATIONS> </CONFIGURATIONS>
<MODULEPATHS> <MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/> <MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>