Merge pull request #277 from DJLevel3/trace

Change trace functionality from Min/Max to Start/Length
custom-resolution-framerate
James H Ball 2025-01-19 18:40:34 +00:00 zatwierdzone przez GitHub
commit ec3838ba5d
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
9 zmienionych plików z 107 dodań i 62 usunięć

Wyświetl plik

@ -121,8 +121,8 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
} }
)); ));
toggleableEffects.push_back(custom); toggleableEffects.push_back(custom);
toggleableEffects.push_back(traceMax); toggleableEffects.push_back(trace);
toggleableEffects.push_back(traceMin); trace->getParameter("traceLength")->lfo->setUnnormalisedValueNotifyingHost((int) LfoType::Sawtooth);
for (int i = 0; i < toggleableEffects.size(); i++) { for (int i = 0; i < toggleableEffects.size(); i++) {
auto effect = toggleableEffects[i]; auto effect = toggleableEffects[i];

Wyświetl plik

@ -68,25 +68,25 @@ public:
"Frequency", "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.", "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", "frequency",
VERSION_HINT, 220.0, 0.0, 12000.0 VERSION_HINT, 220.0, 0.0, 4200.0
) )
); );
std::shared_ptr<Effect> traceMax = std::make_shared<Effect>( std::shared_ptr<Effect> trace = std::make_shared<Effect>(
new EffectParameter( std::vector<EffectParameter*>{
"Trace max", new EffectParameter(
"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.", "Trace Start",
"traceMax", "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.",
VERSION_HINT, 0.75, 0.0, 1.0 "traceStart",
) VERSION_HINT, 0.0, 0.0, 1.0, 0.001, 0.001
); ),
std::shared_ptr<Effect> traceMin = std::make_shared<Effect>( new EffectParameter(
new EffectParameter( "Trace Length",
"Trace min", "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.",
"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.", "traceLength",
"traceMin", VERSION_HINT, 1.0, 0.0, 1.0, 0.001, 0.001
VERSION_HINT, 0.25, 0.0, 1.0 ),
) }
); );
std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>(); std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>();

Wyświetl plik

@ -69,7 +69,10 @@ void Effect::animateValues(double volume) {
actualValues[i] = ((float)rand() / RAND_MAX) * (maxValue - minValue) + minValue; actualValues[i] = ((float)rand() / RAND_MAX) * (maxValue - minValue) + minValue;
break; break;
default: 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; double newValue;
if (parameter->sidechain != nullptr && parameter->sidechain->getBoolValue()) { if (parameter->sidechain != nullptr && parameter->sidechain->getBoolValue()) {
newValue = volume * (maxValue - minValue) + minValue; newValue = volume * (maxValue - minValue) + minValue;

Wyświetl plik

@ -3,6 +3,9 @@
#include <JuceHeader.h> #include <JuceHeader.h>
#include "BooleanParameter.h" #include "BooleanParameter.h"
#define SMOOTHING_SPEED_CONSTANT 0.0003
#define SMOOTHING_SPEED_MIN 0.0001
class FloatParameter : public juce::AudioProcessorParameterWithID { class FloatParameter : public juce::AudioProcessorParameterWithID {
public: public:
std::atomic<float> min = 0.0; std::atomic<float> min = 0.0;
@ -23,7 +26,8 @@ public:
return label; return label;
} }
// returns value in range [0, 1] // returns value in
// [0, 1]
float getNormalisedValue(float value) const { float getNormalisedValue(float value) const {
// clip value to valid range // clip value to valid range
auto min = this->min.load(); auto min = this->min.load();
@ -326,7 +330,7 @@ public:
class EffectParameter : public FloatParameter { class EffectParameter : public FloatParameter {
public: public:
std::atomic<bool> smoothValueChange = true; std::atomic<double> smoothValueChange = SMOOTHING_SPEED_CONSTANT;
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, 10000.0f, 0.001f, "Hz"); 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."); 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) {}
}; };

Wyświetl plik

@ -2,8 +2,8 @@
#include "../PluginProcessor.h" #include "../PluginProcessor.h"
ShapeVoice::ShapeVoice(OscirenderAudioProcessor& p) : audioProcessor(p) { ShapeVoice::ShapeVoice(OscirenderAudioProcessor& p) : audioProcessor(p) {
actualTraceMin = audioProcessor.traceMin->getValue(); actualTraceStart = audioProcessor.trace->getValue(0);
actualTraceMax = audioProcessor.traceMax->getValue(); actualTraceLength = audioProcessor.trace->getValue(1);
} }
bool ShapeVoice::canPlaySound(juce::SynthesiserSound* sound) { bool ShapeVoice::canPlaySound(juce::SynthesiserSound* sound) {
@ -55,7 +55,6 @@ void ShapeVoice::incrementShapeDrawing() {
currentShape++; currentShape++;
if (currentShape >= frame.size()) { if (currentShape >= frame.size()) {
currentShape = 0; currentShape = 0;
break;
} }
// POTENTIAL TODO: Think of a way to make this more efficient when iterating // POTENTIAL TODO: Think of a way to make this more efficient when iterating
// this loop many times // this loop many times
@ -87,13 +86,12 @@ void ShapeVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int star
} }
for (auto sample = startSample; sample < startSample + numSamples; ++sample) { for (auto sample = startSample; sample < startSample + numSamples; ++sample) {
bool traceMinEnabled = audioProcessor.traceMin->enabled->getBoolValue(); bool traceEnabled = audioProcessor.trace->enabled->getBoolValue();
bool traceMaxEnabled = audioProcessor.traceMax->enabled->getBoolValue();
// update length increment // update length increment
double traceMax = traceMaxEnabled ? actualTraceMax : 1.0; double traceLen = traceEnabled ? actualTraceLength : 1.0;
double traceMin = traceMinEnabled ? actualTraceMin : 0.0; double traceMin = traceEnabled ? actualTraceStart : 0.0;
double proportionalLength = (traceMax - traceMin) * frameLength; double proportionalLength = std::max(0.001, traceLen) * frameLength;
lengthIncrement = juce::jmax(proportionalLength / (audioProcessor.currentSampleRate / actualFrequency), MIN_LENGTH_INCREMENT); lengthIncrement = juce::jmax(proportionalLength / (audioProcessor.currentSampleRate / actualFrequency), MIN_LENGTH_INCREMENT);
OsciPoint channels; OsciPoint channels;
@ -148,33 +146,43 @@ void ShapeVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int star
outputBuffer.addSample(0, sample, x * gain); outputBuffer.addSample(0, sample, x * gain);
} }
double traceMinValue = audioProcessor.traceMin->getActualValue(); double traceStartValue = audioProcessor.trace->getActualValue(0);
double traceMaxValue = audioProcessor.traceMax->getActualValue(); double traceLengthValue = audioProcessor.trace->getActualValue(1);
traceMaxValue = traceMaxEnabled ? traceMaxValue : 1.0; traceLengthValue = traceEnabled ? traceLengthValue : 1.0;
traceMinValue = traceMinEnabled ? traceMinValue : 0.0; traceStartValue = traceEnabled ? traceStartValue : 0.0;
actualTraceMax = juce::jmax(actualTraceMin, juce::jmin(traceMaxValue, 1.0)); actualTraceLength = std::max(0.01, traceLengthValue);
actualTraceMin = juce::jmax(MIN_TRACE, juce::jmin(traceMinValue, actualTraceMax - MIN_TRACE)); actualTraceStart = traceStartValue;
if (actualTraceStart < 0) {
actualTraceStart = 0;
}
if (!renderingSample) { if (!renderingSample) {
incrementShapeDrawing(); incrementShapeDrawing();
} }
double drawnFrameLength = traceMaxEnabled ? actualTraceMax * frameLength : frameLength; double drawnFrameLength = frameLength;
bool willLoopOver = false;
if (traceEnabled) {
drawnFrameLength *= actualTraceLength + actualTraceStart;
}
if (!renderingSample && frameDrawn >= drawnFrameLength) { if (!renderingSample && frameDrawn >= drawnFrameLength) {
if (sound.load() != nullptr && currentlyPlaying) { if (sound.load() != nullptr && currentlyPlaying) {
frameLength = sound.load()->updateFrame(frame); frameLength = sound.load()->updateFrame(frame);
} }
frameDrawn -= drawnFrameLength; frameDrawn -= drawnFrameLength;
if (traceEnabled) {
shapeDrawn = juce::jlimit(0.0, frame[currentShape]->len, frameDrawn);
}
currentShape = 0; currentShape = 0;
// TODO: updateFrame already iterates over all the shapes, // TODO: updateFrame already iterates over all the shapes,
// so we can improve performance by calculating frameDrawn // 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 // 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. // we need to iterate over all the shapes to calculate it.
if (traceMinEnabled) { if (traceEnabled) {
while (frameDrawn < actualTraceMin * frameLength) { while (frameDrawn < actualTraceStart * frameLength) {
incrementShapeDrawing(); incrementShapeDrawing();
} }
} }

Wyświetl plik

@ -27,8 +27,8 @@ private:
OscirenderAudioProcessor& audioProcessor; OscirenderAudioProcessor& audioProcessor;
std::vector<std::unique_ptr<Shape>> frame; std::vector<std::unique_ptr<Shape>> frame;
std::atomic<ShapeSound*> sound = nullptr; std::atomic<ShapeSound*> sound = nullptr;
double actualTraceMin; double actualTraceStart;
double actualTraceMax; double actualTraceLength;
double frameLength = 0.0; double frameLength = 0.0;
int currentShape = 0; int currentShape = 0;

Wyświetl plik

@ -1,7 +1,7 @@
bl_info = { bl_info = {
"name": "osci-render", "name": "osci-render",
"author": "James Ball", "author": "James Ball",
"version": (1, 0, 2), "version": (1, 0, 3),
"blender": (3, 1, 2), "blender": (3, 1, 2),
"location": "View3D", "location": "View3D",
"description": "Addon to send gpencil frames over to osci-render", "description": "Addon to send gpencil frames over to osci-render",
@ -126,21 +126,33 @@ def append_matrix(object_info, obj):
return object_info return object_info
def get_frame_info(): def get_frame_info():
frame_info = {"objects": []} 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: for obj in bpy.data.objects:
if obj.visible_get() and obj.type == 'GPENCIL': if obj.visible_get() and obj.type == 'GREASEPENCIL':
object_info = {"name": obj.name} object_info = {"name": obj.name}
strokes = obj.data.layers.active.frames.data.active_frame.strokes strokes = obj.data.layers.active.frames.data.current_frame().drawing.strokes
object_info["vertices"] = [] object_info["vertices"] = []
for stroke in strokes: for stroke in strokes:
object_info["vertices"].append([{ object_info["vertices"].append([{
"x": vert.co[0], "x": vert.position.x,
"y": vert.co[1], "y": vert.position.y,
"z": vert.co[2], "z": vert.position.z,
} for vert in stroke.points]) } for vert in stroke.points])
frame_info["objects"].append(append_matrix(object_info, obj))
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 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' json_str = json.dumps(frame_info, separators=(',', ':')) + '\n'
try: try:
print(json_str)
sock.sendall(json_str.encode('utf-8')) sock.sendall(json_str.encode('utf-8'))
except socket.error as exp: except socket.error as exp:
sock = None sock = None

Wyświetl plik

@ -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 <https://github.com/jameshball>"
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",
]

Wyświetl plik

@ -751,11 +751,12 @@
</MODULEPATHS> </MODULEPATHS>
</LINUX_MAKE> </LINUX_MAKE>
<VS2022 targetFolder="Builds/osci-render/VisualStudio2022" smallIcon="pSc1mq" <VS2022 targetFolder="Builds/osci-render/VisualStudio2022" smallIcon="pSc1mq"
bigIcon="pSc1mq"> bigIcon="pSc1mq" extraCompilerFlags="/wd4005 /wd4244 /wd4305 /wd4584"
extraLinkerFlags="/IGNORE:4006">
<CONFIGURATIONS> <CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" targetName="osci-render"/> <CONFIGURATION isDebug="1" name="Debug" targetName="osci-render" winWarningLevel="2"/>
<CONFIGURATION isDebug="0" name="Release" targetName="osci-render" alwaysGenerateDebugSymbols="1" <CONFIGURATION isDebug="0" name="Release" targetName="osci-render" alwaysGenerateDebugSymbols="1"
debugInformationFormat="ProgramDatabase"/> debugInformationFormat="ProgramDatabase" winWarningLevel="2"/>
</CONFIGURATIONS> </CONFIGURATIONS>
<MODULEPATHS> <MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/> <MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>