greetings folks!
I am wondering whether there is anybody here who
would like to collaborate towards bringing about
a juce-rs translation of the JUCE C++
implementation --?
I have the basics set up already.
The workspace looks like this:
juce-aax
juce-adsr
juce-aiff
juce-alert-window
juce-analytics
juce-android
juce-android-audio
juce-android-camera
juce-android-notifications
juce-android-video
juce-animation
juce-app
juce-array
juce-atomic
juce-au
juce-au-base
juce-au-buffer
juce-au-carbon
juce-au-channel
juce-au-component
juce-au-coreaudio
juce-au-debug
juce-au-device
juce-au-dispatch
juce-au-imports
juce-au-midi
juce-au-parameter
juce-au-streams
juce-au-time
juce-au-types
juce-audio
juce-audio-app-component
juce-audio-block
juce-audio-device-manager
juce-audio-devices
juce-audio-interface
juce-audio-linux-alsa
juce-audio-linux-bela
juce-audio-linux-jack
juce-audio-plugin-client
juce-audio-plugin-format
juce-audio-plugin-host
juce-audio-plugin-instance
juce-audio-processor
juce-audio-processor-editor
juce-audio-processor-graph
juce-audio-processor-value-tree
juce-audio-source
juce-audio-source-player
juce-audio-thumbnail
juce-audio-transport-source
juce-bigint
juce-box2d
juce-bubble
juce-buffers
juce-buttons
juce-callout-box
juce-camera
juce-caret-component
juce-cd
juce-char
juce-chorus
juce-codec
juce-colour
juce-colourselector
juce-combobox
juce-commands
juce-component
juce-concertina
juce-container
juce-content-sharer
juce-convolution
juce-convolution-demo
juce-core
juce-critical-section
juce-crypto
juce-delay
juce-delay-processor
juce-demo
juce-derive
juce-desktop
juce-dialogwindow
juce-distortion
juce-documentwindow
juce-drag-and-drop
juce-drawables
juce-dsp
juce-dynamic-object
juce-dynamics
juce-editor
juce-embedding
juce-events
juce-ex
juce-ex-app
juce-ex-assets
juce-ex-audio
juce-ex-demorunner
juce-ex-gui
juce-ex-plugins
juce-ex-utilities
juce-fft
juce-fft-demo
juce-filebrowser
juce-files
juce-filter
juce-fixed-size-function
juce-flac
juce-flexbox
juce-font
juce-geometry
juce-gif
juce-graphics
juce-grid
juce-identifier
juce-image
juce-imagecomponent
juce-imports
juce-interp
juce-ios-audio
juce-ios-camera
juce-ios-notifications
juce-jpg
juce-keyboard
juce-keymapping-editor
juce-label
juce-ladspa
juce-lasso
juce-level-meter
juce-listbox
juce-live-constant-editor
juce-log
juce-lookandfeel
juce-mac-camera
juce-mac-notifications
juce-mac-video
juce-math
juce-math-expression
juce-memory
juce-menus
juce-midi
juce-mime
juce-mixer
juce-mouse
juce-mp3
juce-mpe
juce-multiband
juce-multidoc
juce-network
juce-notifications
juce-nsview
juce-oboe
juce-oboe-audio-loader
juce-oboe-audioclock
juce-oboe-audioio
juce-oboe-core
juce-oboe-counters
juce-oboe-devicequirks
juce-oboe-extensions
juce-oboe-fifo
juce-oboe-fixedblockrw
juce-oboe-flowgraph
juce-oboe-flowgraph-converter
juce-oboe-flowgraph-filter
juce-oboe-flowgraph-node
juce-oboe-flowgraph-resampler
juce-oboe-flowgraph-sink
juce-oboe-flowgraph-source
juce-oboe-latencytuner
juce-oboe-no-aaudio
juce-oboe-session
juce-oboe-sles
juce-oboe-sourcefloatcaller
juce-oboe-stream
juce-oboe-trace
juce-ogg
juce-opengl
juce-osc
juce-oscillator
juce-oversampling
juce-panner
juce-param
juce-parameter-component
juce-parameter-type
juce-performance-counter
juce-phaser
juce-player
juce-playhead
juce-plugin-demo
juce-positioning
juce-processor-chain
juce-progressbar
juce-properties
juce-random
juce-resizable
juce-reverb
juce-rtas
juce-sample-type
juce-scanning
juce-script
juce-scrollbar
juce-selection
juce-shadow
juce-sidepanel
juce-simd
juce-simd-avx
juce-simd-complex
juce-simd-core
juce-simd-fallback
juce-simd-native
juce-simd-neon
juce-simd-register
juce-simd-register-demo
juce-slider
juce-smoothed-value
juce-splash
juce-standalone
juce-streams
juce-stretchable
juce-string
juce-surround
juce-svg
juce-synthesizer
juce-system
juce-system-tray
juce-tabbed
juce-table
juce-test
juce-text
juce-texteditor
juce-thread-with-progress-window
juce-threads
juce-time
juce-toolbar
juce-tooltip-window
juce-touch
juce-transport
juce-treeview
juce-undo
juce-unity
juce-unlocking
juce-url
juce-uuid
juce-valuetree
juce-variant
juce-video
juce-viewport
juce-visualizer
juce-vst
juce-vst-attributes
juce-vst-buffer
juce-vst-channel
juce-vst-component
juce-vst-debug
juce-vst-edit
juce-vst-events
juce-vst-host
juce-vst-inter-app
juce-vst-message
juce-vst-midi
juce-vst-note-expression
juce-vst-parameters
juce-vst-persist
juce-vst-platform
juce-vst-plugin
juce-vst-plugin-client-vst
juce-vst-plugin-client-vst3
juce-vst-pluginformat
juce-vst-plugview
juce-vst-presetfile
juce-vst-processor
juce-vst-reaper
juce-vst-speaker
juce-vst-stream
juce-vst-string
juce-vst-thread
juce-vst-types
juce-vst-units
juce-vst3
juce-wav
juce-waveshaper
juce-webbrowser
juce-windowing
juce-x11
juce-xembed
juce-xml
juce-zip
In the past, I have done work on compilers and
have written a transpiler which is suitable within
the pipeline for translating C++ codebases into
rust.
Currently, it wraps the function bodies in
todo!() blocks and focuses on translating the
interfaces themselves.
So, we have something like this. Here is an
excerpt from juce-adsr:
// this file is `adsr_envelope.rs` within
// `juce-adsr`
//
// as you can see, we have the whole interface
// translated, but not the implementation.
//
//-------------------------------------------[.cpp/JUCE/modules/juce_audio_basics/utilities/juce_ADSR.h]
// (each translated file has a comment like this^
// saying where the c++ source files came from)
/**
| A very simple ADSR envelope class.
|
| To use it, call setSampleRate() with
| the current sample rate and give it some
| parameters with setParameters() then
| call getNextSample() to get the envelope
| value to be applied to each audio sample
| or applyEnvelopeToBuffer() to apply
| the envelope to a whole buffer.
|
| @tags{Audio}
|
*/
pub struct ADSR {
state: AdsrState, // default = AdsrState::idle
parameters: AdsrParameters,
sample_rate: f64, // default = 44100.0
envelope_val: f32, // default = 0.0f
attack_rate: f32, // default = 0.0f
decay_rate: f32, // default = 0.0f
release_rate: f32, // default = 0.0f
}
impl Default for ADSR {
fn default() -> Self {
todo!();
/*
recalculateRates()
*/
}
}
impl ADSR {
/**
| Sets the parameters that will be used
| by an ADSR object.
|
| You must have called setSampleRate()
| with the correct sample rate before
| this otherwise the values may be incorrect!
|
| @see getParameters
|
*/
pub fn set_parameters(&mut self, new_parameters: &AdsrParameters) {
todo!();
/*
// need to call setSampleRate() first!
jassert (sampleRate > 0.0);
parameters = newParameters;
recalculateRates();
*/
}
/**
| Returns the parameters currently being
| used by an ADSR object.
|
| @see setParameters
|
*/
pub fn get_parameters(&self) -> &AdsrParameters {
todo!();
/*
return parameters;
*/
}
/**
| Returns true if the envelope is in its
| attack, decay, sustain or release stage.
|
*/
pub fn is_active(&self) -> bool {
todo!();
/*
return state != AdsrState::idle;
*/
}
/**
| Sets the sample rate that will be used
| for the envelope.
|
| This must be called before the getNextSample()
| or setParameters() methods.
|
*/
pub fn set_sample_rate(&mut self, new_sample_rate: f64) {
todo!();
/*
jassert (newSampleRate > 0.0);
sampleRate = newSampleRate;
*/
}
/**
| Resets the envelope to an idle state.
|
*/
pub fn reset(&mut self) {
todo!();
/*
envelopeVal = 0.0f;
state = AdsrState::idle;
*/
}
/**
| Starts the attack phase of the envelope.
|
*/
pub fn note_on(&mut self) {
todo!();
/*
if (attackRate > 0.0f)
{
state = AdsrState::attack;
}
else if (decayRate > 0.0f)
{
envelopeVal = 1.0f;
state = AdsrState::decay;
}
else
{
state = AdsrState::sustain;
}
*/
}
/**
| Starts the release phase of the envelope.
|
*/
pub fn note_off(&mut self) {
todo!();
/*
if (state != AdsrState::idle)
{
if (parameters.release > 0.0f)
{
releaseRate = (float) (envelopeVal / (parameters.release * sampleRate));
state = AdsrState::release;
}
else
{
reset();
}
}
*/
}
/**
| Returns the next sample value for an
| ADSR object.
|
| @see applyEnvelopeToBuffer
|
*/
pub fn get_next_sample(&mut self) -> f32 {
todo!();
/*
if (state == AdsrState::idle)
return 0.0f;
if (state == AdsrState::attack)
{
envelopeVal += attackRate;
if (envelopeVal >= 1.0f)
{
envelopeVal = 1.0f;
goToNextState();
}
}
else if (state == AdsrState::decay)
{
envelopeVal -= decayRate;
if (envelopeVal <= parameters.sustain)
{
envelopeVal = parameters.sustain;
goToNextState();
}
}
else if (state == AdsrState::sustain)
{
envelopeVal = parameters.sustain;
}
else if (state == AdsrState::release)
{
envelopeVal -= releaseRate;
if (envelopeVal <= 0.0f)
goToNextState();
}
return envelopeVal;
*/
}
/**
| This method will conveniently apply
| the next numSamples number of envelope
| values to an AudioBuffer.
|
| @see getNextSample
|
*/
pub fn apply_envelope_to_buffer<FloatType>(&mut self,
buffer: &mut AudioBuffer<FloatType>,
start_sample: i32,
num_samples: i32) {
todo!();
/*
jassert (startSample + numSamples <= buffer.getNumSamples());
if (state == AdsrState::idle)
{
buffer.clear (startSample, numSamples);
return;
}
if (state == AdsrState::sustain)
{
buffer.applyGain (startSample, numSamples, parameters.sustain);
return;
}
auto numChannels = buffer.getNumChannels();
while (--numSamples >= 0)
{
auto env = getNextSample();
for (int i = 0; i < numChannels; ++i)
buffer.getWritePointer (i)[startSample] *= env;
++startSample;
}
*/
}
pub fn recalculate_rates(&mut self) {
todo!();
/*
auto getRate = [] (float distance, float timeInSeconds, double sr)
{
return timeInSeconds > 0.0f ? (float) (distance / (timeInSeconds * sr)) : -1.0f;
};
attackRate = getRate (1.0f, parameters.attack, sampleRate);
decayRate = getRate (1.0f - parameters.sustain, parameters.decay, sampleRate);
releaseRate = getRate (parameters.sustain, parameters.release, sampleRate);
if ((state == AdsrState::attack && attackRate <= 0.0f)
|| (state == AdsrState::decay && (decayRate <= 0.0f || envelopeVal <= parameters.sustain))
|| (state == AdsrState::release && releaseRate <= 0.0f))
{
goToNextState();
}
*/
}
pub fn go_to_next_state(&mut self) {
todo!();
/*
if (state == AdsrState::attack)
state = (decayRate > 0.0f ? AdsrState::decay : AdsrState::sustain);
else if (state == AdsrState::decay)
state = AdsrState::sustain;
else if (state == AdsrState::release)
reset();
*/
}
}
As you can see, the code is valid rust, but the
function bodies still need focus.
There is now a full juce-rs workspace of
components such as this, but I am unsure what we
should do with it.
To be clear:
- The whole JUCE C++ interface potentially exists as a valid rust workspace.
- Roughly each C++ module maps to a rust crate.
- The whole workspace compiles.
- The function bodies are still todo.
I want to make sure that this is appropriate in the
context of the JUCE ecosystem and that the current
JUCE developers and owners are happy with it.
I don’t want to do anything which makes their lives
harder! Instead, if possible, I think it would be
wonderful to help them bring JUCE to a new group
of users and collaborators.
Is there anybody here who would like to help?
Is there anybody here from JUCE I ought to speak to?
I have a mountain of other work to do, but after
I got the transpiler up and running I figured
I could try to do something to hopefully help the
JUCE community which had given me an opportunity
to speak a few years ago.
If the existence of juce-rs is inappropriate or
unwelcome for any reason, I totally and
respectfully understand and am happy to work on
other projects. In that case, feel free to
consider the work on it I have done thus far as
a donation, a thank you, and an olive branch.
JUCE helped me get started in the programming
world and, as I grew, I always had it in my head
that if I could, one day, I would try to do
something to help them grow. So, this is
it! I hope you like it!
Best,
-Thomas
