diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index e1b63a7..54a20b9 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -121,8 +121,8 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse } )); toggleableEffects.push_back(custom); - toggleableEffects.push_back(traceMax); - toggleableEffects.push_back(traceMin); + toggleableEffects.push_back(trace); + trace->getParameter("traceLength")->lfo->setUnnormalisedValueNotifyingHost((int) LfoType::Sawtooth); for (int i = 0; i < toggleableEffects.size(); i++) { auto effect = toggleableEffects[i]; diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 0689ed0..f5f658f 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -68,25 +68,25 @@ public: "Frequency", "Controls how many times per second the image is drawn, thereby controlling the pitch of the sound. Lower frequencies result in more-accurately drawn images, but more flickering, and vice versa.", "frequency", - VERSION_HINT, 220.0, 0.0, 12000.0 + VERSION_HINT, 220.0, 0.0, 4200.0 ) ); - std::shared_ptr traceMax = std::make_shared( - new EffectParameter( - "Trace max", - "Defines the maximum proportion of the image that is drawn before skipping to the next frame. This has the effect of 'tracing' out the image from a single dot when animated. By default, we draw until the end of the frame, so this value is 1.0.", - "traceMax", - VERSION_HINT, 0.75, 0.0, 1.0 - ) - ); - std::shared_ptr traceMin = std::make_shared( - new EffectParameter( - "Trace min", - "Defines the proportion of the image that drawing starts from. This has the effect of 'tracing' out the image from a single dot when animated. By default, we start drawing from the beginning of the frame, so this value is 0.0.", - "traceMin", - VERSION_HINT, 0.25, 0.0, 1.0 - ) + std::shared_ptr trace = std::make_shared( + std::vector{ + new EffectParameter( + "Trace Start", + "Defines how far into the frame the drawing is started at. This has the effect of 'tracing' out the image from a single dot when animated. By default, we start drawing from the beginning of the frame, so this value is 0.0.", + "traceStart", + VERSION_HINT, 0.0, 0.0, 1.0, 0.001, 0.001 + ), + new EffectParameter( + "Trace Length", + "Defines how much of the frame is drawn per cycle. This has the effect of 'tracing' out the image from a single dot when animated. By default, we draw the whole frame, corresponding to a value of 1.0.", + "traceLength", + VERSION_HINT, 1.0, 0.0, 1.0, 0.001, 0.001 + ), + } ); std::shared_ptr delayEffect = std::make_shared(); diff --git a/Source/audio/Effect.cpp b/Source/audio/Effect.cpp index 4fa2c78..bc30819 100644 --- a/Source/audio/Effect.cpp +++ b/Source/audio/Effect.cpp @@ -69,7 +69,10 @@ void Effect::animateValues(double volume) { actualValues[i] = ((float)rand() / RAND_MAX) * (maxValue - minValue) + minValue; break; default: - double weight = parameter->smoothValueChange ? 0.0005 : 1.0; + double weight = 1.0; + if (parameter->smoothValueChange < 1.0 && parameter->smoothValueChange > SMOOTHING_SPEED_MIN) { + weight = parameter->smoothValueChange.load() * 192000 / sampleRate; + } double newValue; if (parameter->sidechain != nullptr && parameter->sidechain->getBoolValue()) { newValue = volume * (maxValue - minValue) + minValue; diff --git a/Source/audio/EffectParameter.h b/Source/audio/EffectParameter.h index f76d9ff..3bc6a91 100644 --- a/Source/audio/EffectParameter.h +++ b/Source/audio/EffectParameter.h @@ -3,6 +3,9 @@ #include #include "BooleanParameter.h" +#define SMOOTHING_SPEED_CONSTANT 0.0003 +#define SMOOTHING_SPEED_MIN 0.0001 + class FloatParameter : public juce::AudioProcessorParameterWithID { public: std::atomic min = 0.0; @@ -23,7 +26,8 @@ public: return label; } - // returns value in range [0, 1] + // returns value in + // [0, 1] float getNormalisedValue(float value) const { // clip value to valid range auto min = this->min.load(); @@ -326,7 +330,7 @@ public: class EffectParameter : public FloatParameter { public: - std::atomic smoothValueChange = true; + std::atomic smoothValueChange = SMOOTHING_SPEED_CONSTANT; LfoTypeParameter* lfo = new LfoTypeParameter(name + " LFO", paramID + "Lfo", getVersionHint(), 1); FloatParameter* lfoRate = new FloatParameter(name + " LFO Rate", paramID + "LfoRate", getVersionHint(), 1.0f, 0.0f, 10000.0f, 0.001f, "Hz"); BooleanParameter* sidechain = new BooleanParameter(name + " Sidechain Enabled", paramID + "Sidechain", getVersionHint(), false, "Toggles " + name + " Sidechain."); @@ -401,5 +405,5 @@ public: } } - EffectParameter(juce::String name, juce::String description, juce::String id, int versionHint, float value, float min, float max, float step = 0.0001, bool smoothValueChange = true) : FloatParameter(name, id, versionHint, value, min, max, step), smoothValueChange(smoothValueChange), description(description), defaultValue(value) {} + EffectParameter(juce::String name, juce::String description, juce::String id, int versionHint, float value, float min, float max, float step = 0.0001, double smoothValueChange = SMOOTHING_SPEED_CONSTANT) : FloatParameter(name, id, versionHint, value, min, max, step), smoothValueChange(smoothValueChange), description(description) {} }; diff --git a/Source/audio/ShapeVoice.cpp b/Source/audio/ShapeVoice.cpp index a587662..083d796 100644 --- a/Source/audio/ShapeVoice.cpp +++ b/Source/audio/ShapeVoice.cpp @@ -2,8 +2,8 @@ #include "../PluginProcessor.h" ShapeVoice::ShapeVoice(OscirenderAudioProcessor& p) : audioProcessor(p) { - actualTraceMin = audioProcessor.traceMin->getValue(); - actualTraceMax = audioProcessor.traceMax->getValue(); + actualTraceStart = audioProcessor.trace->getValue(0); + actualTraceLength = audioProcessor.trace->getValue(1); } bool ShapeVoice::canPlaySound(juce::SynthesiserSound* sound) { @@ -55,7 +55,6 @@ void ShapeVoice::incrementShapeDrawing() { currentShape++; if (currentShape >= frame.size()) { currentShape = 0; - break; } // POTENTIAL TODO: Think of a way to make this more efficient when iterating // this loop many times @@ -87,13 +86,12 @@ void ShapeVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int star } for (auto sample = startSample; sample < startSample + numSamples; ++sample) { - bool traceMinEnabled = audioProcessor.traceMin->enabled->getBoolValue(); - bool traceMaxEnabled = audioProcessor.traceMax->enabled->getBoolValue(); + bool traceEnabled = audioProcessor.trace->enabled->getBoolValue(); // update length increment - double traceMax = traceMaxEnabled ? actualTraceMax : 1.0; - double traceMin = traceMinEnabled ? actualTraceMin : 0.0; - double proportionalLength = (traceMax - traceMin) * frameLength; + double traceLen = traceEnabled ? actualTraceLength : 1.0; + double traceMin = traceEnabled ? actualTraceStart : 0.0; + double proportionalLength = std::max(0.001, traceLen) * frameLength; lengthIncrement = juce::jmax(proportionalLength / (audioProcessor.currentSampleRate / actualFrequency), MIN_LENGTH_INCREMENT); OsciPoint channels; @@ -148,33 +146,43 @@ void ShapeVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int star outputBuffer.addSample(0, sample, x * gain); } - double traceMinValue = audioProcessor.traceMin->getActualValue(); - double traceMaxValue = audioProcessor.traceMax->getActualValue(); - traceMaxValue = traceMaxEnabled ? traceMaxValue : 1.0; - traceMinValue = traceMinEnabled ? traceMinValue : 0.0; - actualTraceMax = juce::jmax(actualTraceMin, juce::jmin(traceMaxValue, 1.0)); - actualTraceMin = juce::jmax(MIN_TRACE, juce::jmin(traceMinValue, actualTraceMax - MIN_TRACE)); + double traceStartValue = audioProcessor.trace->getActualValue(0); + double traceLengthValue = audioProcessor.trace->getActualValue(1); + traceLengthValue = traceEnabled ? traceLengthValue : 1.0; + traceStartValue = traceEnabled ? traceStartValue : 0.0; + actualTraceLength = std::max(0.01, traceLengthValue); + actualTraceStart = traceStartValue; + if (actualTraceStart < 0) { + actualTraceStart = 0; + } if (!renderingSample) { incrementShapeDrawing(); } - double drawnFrameLength = traceMaxEnabled ? actualTraceMax * frameLength : frameLength; + double drawnFrameLength = frameLength; + bool willLoopOver = false; + if (traceEnabled) { + drawnFrameLength *= actualTraceLength + actualTraceStart; + } if (!renderingSample && frameDrawn >= drawnFrameLength) { if (sound.load() != nullptr && currentlyPlaying) { frameLength = sound.load()->updateFrame(frame); } frameDrawn -= drawnFrameLength; + if (traceEnabled) { + shapeDrawn = juce::jlimit(0.0, frame[currentShape]->len, frameDrawn); + } currentShape = 0; // TODO: updateFrame already iterates over all the shapes, // so we can improve performance by calculating frameDrawn - // and shapeDrawn directly. frameDrawn is simply actualTraceMin * frameLength + // and shapeDrawn directly. frameDrawn is simply actualTraceStart * frameLength // but shapeDrawn is the amount of the current shape that has been drawn so // we need to iterate over all the shapes to calculate it. - if (traceMinEnabled) { - while (frameDrawn < actualTraceMin * frameLength) { + if (traceEnabled) { + while (frameDrawn < actualTraceStart * frameLength) { incrementShapeDrawing(); } } diff --git a/Source/audio/ShapeVoice.h b/Source/audio/ShapeVoice.h index 76a878f..95b3888 100644 --- a/Source/audio/ShapeVoice.h +++ b/Source/audio/ShapeVoice.h @@ -27,8 +27,8 @@ private: OscirenderAudioProcessor& audioProcessor; std::vector> frame; std::atomic sound = nullptr; - double actualTraceMin; - double actualTraceMax; + double actualTraceStart; + double actualTraceLength; double frameLength = 0.0; int currentShape = 0; diff --git a/blender/osci_render/__init__.py b/blender/osci_render/__init__.py index cb68c52..723ca7b 100644 --- a/blender/osci_render/__init__.py +++ b/blender/osci_render/__init__.py @@ -1,7 +1,7 @@ bl_info = { "name": "osci-render", "author": "James Ball", - "version": (1, 0, 2), + "version": (1, 0, 3), "blender": (3, 1, 2), "location": "View3D", "description": "Addon to send gpencil frames over to osci-render", @@ -126,21 +126,33 @@ def append_matrix(object_info, obj): return object_info def get_frame_info(): - frame_info = {"objects": []} - - for obj in bpy.data.objects: - if obj.visible_get() and obj.type == 'GPENCIL': - object_info = {"name": obj.name} - strokes = obj.data.layers.active.frames.data.active_frame.strokes - object_info["vertices"] = [] - for stroke in strokes: - object_info["vertices"].append([{ - "x": vert.co[0], - "y": vert.co[1], - "z": vert.co[2], - } for vert in stroke.points]) - - frame_info["objects"].append(append_matrix(object_info, obj)) + frame_info = {"objects": []} + if (bpy.app.version[0] > 4) or (bpy.app.version[0] == 4 and bpy.app.version[1] >= 3): + for obj in bpy.data.objects: + if obj.visible_get() and obj.type == 'GREASEPENCIL': + object_info = {"name": obj.name} + strokes = obj.data.layers.active.frames.data.current_frame().drawing.strokes + object_info["vertices"] = [] + for stroke in strokes: + object_info["vertices"].append([{ + "x": vert.position.x, + "y": vert.position.y, + "z": vert.position.z, + } for vert in stroke.points]) + frame_info["objects"].append(append_matrix(object_info, obj)) + else: + for obj in bpy.data.objects: + if obj.visible_get() and obj.type == 'GPENCIL': + object_info = {"name": obj.name} + strokes = obj.data.layers.active.frames.data.active_frame.strokes + object_info["vertices"] = [] + for stroke in strokes: + object_info["vertices"].append([{ + "x": vert.co[0], + "y": vert.co[1], + "z": vert.co[2], + } for vert in stroke.points]) + frame_info["objects"].append(append_matrix(object_info, obj)) frame_info["focalLength"] = -0.05 * bpy.data.cameras[0].lens @@ -177,7 +189,6 @@ def send_scene_to_osci_render(scene): json_str = json.dumps(frame_info, separators=(',', ':')) + '\n' try: - print(json_str) sock.sendall(json_str.encode('utf-8')) except socket.error as exp: sock = None diff --git a/blender/osci_render/blender_manifest.toml b/blender/osci_render/blender_manifest.toml new file mode 100644 index 0000000..ffc917e --- /dev/null +++ b/blender/osci_render/blender_manifest.toml @@ -0,0 +1,18 @@ +schema_version = "1.0.0" + +id = "osci_render" +version = "1.0.3" +name = "osci-render" +tagline = "Addon to send gpencil frames over to osci-render" +maintainer = "James H. Ball " +type = "add-on" + +website = "https://github.com/jameshball/osci-render" + +tags = ["Animation", "Bake", "Grease Pencil", "Import-Export", "Render"] + +blender_version_min = "4.2.0" + +license = [ + "SPDX:GPL-3.0-or-later", +] \ No newline at end of file diff --git a/osci-render.jucer b/osci-render.jucer index 146dc76..36f3f2c 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -751,11 +751,12 @@ + bigIcon="pSc1mq" extraCompilerFlags="/wd4005 /wd4244 /wd4305 /wd4584" + extraLinkerFlags="/IGNORE:4006"> - + + debugInformationFormat="ProgramDatabase" winWarningLevel="2"/>