kopia lustrzana https://github.com/jameshball/osci-render
Merge pull request #277 from DJLevel3/trace
Change trace functionality from Min/Max to Start/Lengthcustom-resolution-framerate
commit
ec3838ba5d
|
@ -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];
|
||||||
|
|
|
@ -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>();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
]
|
|
@ -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"/>
|
||||||
|
|
Ładowanie…
Reference in New Issue