Skip to content

Commit

Permalink
Add new Autotuner fx
Browse files Browse the repository at this point in the history
Implements a new autotune, which is closer to the Antares original.

It doesn't use FFTs - instead everything happens in the time domain.
This version of the effect has options for transpose, key and scale
along with note as a target note. Performance should be better than the
other autotuner effect which exists in SP currently.

See @xavriley/qlibugens for the original plugin. Binaries are available
on the Releases page.
  • Loading branch information
xavriley committed May 20, 2022
1 parent 44b97f2 commit 18d21e2
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 1 deletion.
12 changes: 11 additions & 1 deletion BUILD-LINUX.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ few dependencies:
* Ruby + Dev tools (2.5+)
* Erlang + Dev tools (21+)
* SuperCollider + SC3 plugins
*
* (Optional) Additional SuperCollider plugins


### 1.1 Raspberry Pi OS
Expand All @@ -61,6 +61,16 @@ sudo apt-get install -y \
compton pulseaudio-module-jack
```

### 1.2 (Optional) Additional SuperCollider Plugins

Some audio effects and synthesizer are not distributed with the `sc3-plugins-server` package and prebuilt binaries are not provided with the Sonic Pi repo. To use these with your version of Sonic Pi, you can find build instructions at the following repositories:

* https://github.com/xavriley/qlibugens/ - used by the `:autotuner_two` effect.
* (more to follow as we implement other plugins)

After building the plugins, make sure you copy them to your [SuperCollider Extensions folder](https://doc.sccode.org/Guides/UsingExtensions.html) before starting Sonic Pi.

If you don't do this, everything else will still work as normal - you just won't be able to use these particular effects.

## 2. Preparing the Build

Expand Down
144 changes: 144 additions & 0 deletions app/server/ruby/lib/sonicpi/synths/synthinfo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5419,6 +5419,149 @@ def specific_arg_info
end
end

class FXAutotuner2 < FXInfo
def name
"Autotuner2"
end

def introduced
Version.new(4,0,0)
end

def synth_name
"fx_autotuner_two"
end

def doc
"Another autotune effect. Used without any arguments, it tries to detect the pitch and shift it to the nearest exact note. This can help with out of tune singing, but it's also an interesting effect in its own right. When used with the note: arg, it tries to shift the input to match that note instead. This gives that classic \"robot singing\" sound that people associate with vocoders. This can then be changed using the control method to create new melodies.
```
with_fx :autotuner_two do |c|
```
```
sample \"~/Downloads/acappella.wav\" # any sample with a voice is good
```
```
sleep 4
```
```
# listen to standard auto-tune behaviour for 4 seconds
```
```
64.times do
```
```
# now start changing note: to get robot voice behaviour
```
```
control c, note: (scale :a2, :minor_pentatonic, num_octaves: 2).choose
```
```
sleep 0.5
```
```
end
```
```
end
```
"
end

def arg_defaults
super.merge({
:note => -1,
:note_slide => 0,
:note_slide_shape => 1,
:note_slide_curve => 0,
:transpose => 0,
:key => 0,
:scale => 0
})
end

def munge_opts(studio, args_h)
keys = {
nil => nil,
:c => 0,
:cs => 1,
:db => 1,
:d => 2,
:ds => 3,
:eb => 3,
:e => 4,
:f => 5,
:fs => 6,
:gb => 6,
:g => 7,
:gs => 8,
:ab => 8,
:a => 9,
:as => 10,
:bb => 10,
:b => 11
}
args_h[:key] = keys[args_h[:key]]

scales = {
nil => nil,
:none => 0,
:chromatic => 0,
:major => 1,
:major_pentatonic => 2,
:minor_pentatonic => 3
}

args_h[:scale] = scales[args_h[:scale]]

puts args_h
args_h
end

def specific_arg_info
{
:note =>
{
:doc => "Midi note to shift pitch to. The quality of the sound depends on how stable the pitch of the input is. If set, this will override the scale and key options. Set to -1 to turn this off.",
:validations => [v_between_inclusive(:note, -2, 127)],
:modulatable => true
},

:key =>
{
:doc => "Key to lock the output pitches to. One of :c, :cs, :d, :e, :f, :fs, :g, :gs, :a, :as, or :b.",
:validations => [v_one_of(:key, [nil, :c, :cs, :db, :d, :ds, :eb, :e, :f, :fs, :gb, :g, :gs, :ab, :a, :as, :bb, :b,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])],
:modulatable => false
},

:scale =>
{
:doc => "Scale to lock the output pitches to. One of :none, :chromatic, :major, :major_pentatonic or :minor_pentatonic.",
:validations => [v_one_of(:scale, [nil, :none, :chromatic, :major, :major_pentatonic, :minor_pentatonic,
0, 1, 2, 3])],
:modulatable => false
},

:transpose =>
{
:doc => "Number of semitones to shift the output up or down, with a maximum of +24 (two octaves up) and a minimum of -24 (two octaves down).",
:validations => [v_between_inclusive(:transpose, -24, 24)],
:modulatable => true
}
}
end
end

class FXMono < FXInfo
def name
"Mono"
Expand Down Expand Up @@ -8183,6 +8326,7 @@ class BaseInfo
:fx_level => FXLevel.new,
:fx_mono => FXMono.new,
:fx_autotuner => FXAutotuner.new,
:fx_autotuner_two => FXAutotuner2.new,
:fx_replace_level => FXLevel.new,
:fx_echo => FXEcho.new,
:fx_replace_echo => FXEcho.new,
Expand Down
Binary file not shown.
92 changes: 92 additions & 0 deletions etc/synthdefs/designs/supercollider/autotuner_two.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Autotune style plugin based on the orignal Antares patent (US Patent US5973252 - now expired). Uses Cycfi's Bitstream Autocorrelation (BACF) for pitch tracking.
// See https://github.com/xavriley/qlibugens for details of the SC UGens

(
SynthDef('sonic-pi-fx_autotuner_two', {|
//standard args
pre_amp=1,
pre_amp_slide=0,
pre_amp_slide_shape=1,
pre_amp_slide_curve=0,
amp=1,
amp_slide=0,
amp_slide_shape=1,
amp_slide_curve=0,
mix=1,
mix_slide=0,
mix_slide_shape=1,
mix_slide_curve=0,
pre_mix=1,
pre_mix_slide=0,
pre_mix_slide_shape=1,
pre_mix_slide_curve=0
out_bus=0,
in_bus=0,
// args specific to this synth
note = -1,
note_slide=0,
note_slide_shape=1,
note_slide_curve=0,
min_freq = 50,
transpose=0,
key=0,
scale=0|

var pitch_ratio,
in,
snd,
freq,
// args for Sonic Pi plumbing
fxArgMix,
fxArgPreMix,
fxArgAmp,
fxArgPreAmp,
inLeft,
inRight,
fxArgInvPreMix,
fxArgBypassL,
fxArgBypassR,
dryL,
dryR,
wetL,
wetR,
finL,
finR;

// plumbing for Sonic Pi standard args
fxArgMix = VarLag.kr(mix.clip(0,1), mix_slide, mix_slide_curve, mix_slide_shape);
fxArgMix = LinLin.kr(fxArgMix, 0, 1, -1, 1);

fxArgPreMix = VarLag.kr(pre_mix.clip(0,1), pre_mix_slide, pre_mix_slide_curve, pre_mix_slide_shape);

fxArgAmp = VarLag.kr(amp, amp_slide, amp_slide_curve, amp_slide_shape);
fxArgPreAmp = VarLag.kr(pre_amp, pre_amp_slide, pre_amp_slide_curve, pre_amp_slide_shape);

# inLeft, inRight = In.ar(in_bus, 2) * fxArgPreAmp;
fxArgInvPreMix = 1 - fxArgPreMix;

fxArgBypassL = fxArgInvPreMix * inLeft;
fxArgBypassR = fxArgInvPreMix * inRight;

dryL = fxArgPreMix * inLeft;
dryR = fxArgPreMix * inRight;

// note represents a midi "target" pitch to tune to
// without that arg, it works as a normal autotune, locking the
// input to the nearest midi note
note = note.varlag(note_slide, note_slide_curve, note_slide_shape);

wetL = BitstreamPitchCorrection.ar(inLeft, min_freq, transpose: transpose, key: key, scale: scale, target_note: note);
wetR = wetL;

// plumbing for Sonic Pi output
wetL = wetL + fxArgBypassL;
wetR = wetR + fxArgBypassR;

finL = XFade2.ar(inLeft, wetL, fxArgMix, fxArgAmp);
finR = XFade2.ar(inRight, wetR, fxArgMix, fxArgAmp);

Out.ar(out_bus, [finL, finR]);
}
).writeDefFile("/Users/sam/Development/RPi/sonic-pi/etc/synthdefs/compiled/")
)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 18d21e2

Please sign in to comment.