kopia lustrzana https://github.com/jameshball/osci-render
Add sweep / V-T functionality and update mac icon
rodzic
587e82e7a6
commit
bfe497ca72
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 45 KiB |
|
@ -47,8 +47,8 @@ CommonPluginEditor::CommonPluginEditor(CommonAudioProcessor& p, juce::String app
|
|||
};
|
||||
|
||||
visualiserSettings.setLookAndFeel(&getLookAndFeel());
|
||||
visualiserSettings.setSize(550, 600);
|
||||
visualiserSettingsWindow.centreWithSize(550, 450);
|
||||
visualiserSettings.setSize(550, 550);
|
||||
visualiserSettingsWindow.centreWithSize(550, 400);
|
||||
#if JUCE_WINDOWS
|
||||
// if not standalone, use native title bar for compatibility with DAWs
|
||||
visualiserSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
|
||||
|
|
|
@ -25,7 +25,6 @@ CommonAudioProcessor::CommonAudioProcessor()
|
|||
}
|
||||
|
||||
effects.push_back(visualiserParameters.smoothEffect);
|
||||
permanentEffects.push_back(visualiserParameters.smoothEffect);
|
||||
|
||||
for (auto parameter : visualiserParameters.booleans) {
|
||||
booleanParameters.push_back(parameter);
|
||||
|
|
|
@ -328,7 +328,7 @@ class EffectParameter : public FloatParameter {
|
|||
public:
|
||||
std::atomic<bool> smoothValueChange = true;
|
||||
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.");
|
||||
std::atomic<float> phase = 0.0f;
|
||||
juce::String description;
|
||||
|
|
|
@ -25,6 +25,7 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
|
|||
"A huge thank you to:\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"
|
||||
"Jean Perbet (@jeanprbt) for the osci-render macOS icon\n"
|
||||
"All the community, for suggesting features and reporting issues!\n\n"
|
||||
"I am open for commissions! Email me at james@ball.sh."
|
||||
);
|
||||
|
|
|
@ -120,11 +120,42 @@ void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
|
|||
xSamples.clear();
|
||||
ySamples.clear();
|
||||
zSamples.clear();
|
||||
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);
|
||||
|
||||
if (settings.isSweepEnabled()) {
|
||||
double sweepIncrement = getSweepIncrement();
|
||||
long samplesPerSweep = sampleRate * settings.getSweepSeconds();
|
||||
|
||||
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++;
|
||||
|
@ -137,7 +168,25 @@ void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
|
|||
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());
|
||||
zResampler.process(zSamples.data(), smoothedZSamples.data(), zSamples.size());
|
||||
}
|
||||
|
@ -167,6 +216,10 @@ void VisualiserComponent::stopTask() {
|
|||
renderingSemaphore.release();
|
||||
}
|
||||
|
||||
double VisualiserComponent::getSweepIncrement() {
|
||||
return 1.0 / (sampleRate * settings.getSweepSeconds());
|
||||
}
|
||||
|
||||
void VisualiserComponent::setPaused(bool paused) {
|
||||
active = !paused;
|
||||
renderingSemaphore.release();
|
||||
|
@ -506,7 +559,11 @@ void VisualiserComponent::renderOpenGL() {
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
// Without this, there's an access violation that seems to occur only on some systems
|
||||
|
|
|
@ -152,6 +152,7 @@ private:
|
|||
int nEdges = 0;
|
||||
|
||||
juce::CriticalSection samplesLock;
|
||||
long sampleCount = 0;
|
||||
std::vector<float> xSamples{2};
|
||||
std::vector<float> ySamples{2};
|
||||
std::vector<float> zSamples{2};
|
||||
|
@ -160,6 +161,7 @@ private:
|
|||
std::vector<float> smoothedZSamples;
|
||||
int sampleBufferCount = 0;
|
||||
int prevSampleBufferCount = 0;
|
||||
long lastTriggerPosition = 0;
|
||||
|
||||
std::vector<float> scratchVertices;
|
||||
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);
|
||||
int renderAudioFile(juce::File& sourceAudio, int method = 1, int width = 1024, int height = 1024);
|
||||
|
||||
double getSweepIncrement();
|
||||
|
||||
Texture createScreenTexture();
|
||||
Texture createReflectionTexture();
|
||||
|
|
|
@ -13,7 +13,8 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
|
|||
addAndMakeVisible(glow);
|
||||
addAndMakeVisible(ambient);
|
||||
addAndMakeVisible(smooth);
|
||||
addChildComponent(sweepMs);
|
||||
addAndMakeVisible(sweepMs);
|
||||
addAndMakeVisible(triggerValue);
|
||||
addAndMakeVisible(upsamplingToggle);
|
||||
addAndMakeVisible(sweepToggle);
|
||||
addAndMakeVisible(screenTypeLabel);
|
||||
|
@ -37,9 +38,16 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
|
|||
ambient.setSliderOnValueChange();
|
||||
smooth.setSliderOnValueChange();
|
||||
sweepMs.setSliderOnValueChange();
|
||||
triggerValue.setSliderOnValueChange();
|
||||
|
||||
sweepMs.setEnabled(sweepToggle.getToggleState());
|
||||
triggerValue.setEnabled(sweepToggle.getToggleState());
|
||||
|
||||
sweepMs.slider.setSkewFactorFromMidPoint(100);
|
||||
|
||||
sweepToggle.onClick = [this] {
|
||||
sweepMs.setVisible(sweepToggle.getToggleState());
|
||||
sweepMs.setEnabled(sweepToggle.getToggleState());
|
||||
triggerValue.setEnabled(sweepToggle.getToggleState());
|
||||
resized();
|
||||
};
|
||||
}
|
||||
|
@ -68,7 +76,6 @@ void VisualiserSettings::resized() {
|
|||
upsamplingToggle.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));
|
||||
}
|
||||
|
|
|
@ -171,11 +171,19 @@ public:
|
|||
"Sweep (ms)",
|
||||
"The number of milliseconds it takes for the oscilloscope to sweep from left to right.",
|
||||
"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<IntParameter*> integers = {screenType};
|
||||
};
|
||||
|
@ -226,6 +234,18 @@ public:
|
|||
bool getUpsamplingEnabled() {
|
||||
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;
|
||||
int numChannels;
|
||||
|
@ -241,6 +261,7 @@ private:
|
|||
EffectComponent ambient{*parameters.ambientEffect};
|
||||
EffectComponent smooth{*parameters.smoothEffect};
|
||||
EffectComponent sweepMs{*parameters.sweepMsEffect};
|
||||
EffectComponent triggerValue{*parameters.triggerValueEffect};
|
||||
|
||||
juce::Label screenTypeLabel{"Screen Type", "Screen Type"};
|
||||
juce::ComboBox screenType;
|
||||
|
@ -257,7 +278,7 @@ public:
|
|||
juce::Component::addAndMakeVisible(viewport);
|
||||
setResizable(false, false);
|
||||
viewport.setViewedComponent(&component, false);
|
||||
viewport.setScrollBarsShown(false, false, true, false);
|
||||
viewport.setScrollBarsShown(true, false, true, false);
|
||||
setAlwaysOnTop(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,15 @@ else
|
|||
PROJUCER_PATH="$ROOT/ci/bin/JUCE/Projucer.exe"
|
||||
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
|
||||
GLOBAL_PATH_COMMAND="$PROJUCER_PATH --set-global-search-path $PROJUCER_OS 'defaultJuceModulePath' '$ROOT/ci/bin/JUCE/modules'"
|
||||
eval "$GLOBAL_PATH_COMMAND"
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
</GROUP>
|
||||
<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="aZXbCi" name="osci_mac.png" compile="0" resource="1" file="Resources/images/osci_mac.png"/>
|
||||
</GROUP>
|
||||
<GROUP id="{C2609827-4F4A-1ADA-8BA1-A40C1D92649C}" name="lua">
|
||||
<FILE id="xANsA8" name="demo.lua" compile="0" resource="1" file="Resources/lua/demo.lua"/>
|
||||
|
@ -775,8 +776,8 @@
|
|||
</MODULEPATHS>
|
||||
</VS2022>
|
||||
<XCODE_MAC targetFolder="Builds/osci-render/MacOSX" extraLinkerFlags="-Wl,-weak_reference_mismatches,weak"
|
||||
extraDefs="JUCE_SILENCE_XCODE_15_LINKER_WARNING=1" smallIcon="pSc1mq"
|
||||
bigIcon="pSc1mq" applicationCategory="public.app-category.music"
|
||||
extraDefs="JUCE_SILENCE_XCODE_15_LINKER_WARNING=1" smallIcon="aZXbCi"
|
||||
bigIcon="aZXbCi" applicationCategory="public.app-category.music"
|
||||
microphonePermissionNeeded="1" frameworkSearchPaths="../../../External/syphon"
|
||||
embeddedFrameworks="../../../External/syphon/Syphon.framework"
|
||||
extraCustomFrameworks="../../../External/syphon/Syphon.framework"
|
||||
|
@ -787,6 +788,8 @@
|
|||
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"
|
||||
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>
|
||||
<MODULEPATHS>
|
||||
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>
|
||||
|
|
|
@ -263,6 +263,8 @@
|
|||
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"
|
||||
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>
|
||||
<MODULEPATHS>
|
||||
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>
|
||||
|
|
Ładowanie…
Reference in New Issue