Skip to content

Commit

Permalink
move envelope inside each module
Browse files Browse the repository at this point in the history
  • Loading branch information
iljarotar committed Jan 11, 2025
1 parent 1446e35 commit 4f296d2
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 95 deletions.
108 changes: 53 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,18 @@ Run `synth -h` to see all configuration options.
| noises | Noise [0..*] | all noise generators |
| wavetables | Wavetables [0..*] | all wavetables |
| samplers | Sampler[0..*] | all samplers |
| envelopes | Envelope [0..*] | all envelopes |

| Oscillator | | |
| ---------- | -------------- | ----------------------------------------- |
| **Field** | **Type** | **Description** |
| name | String | should be unique in the scope of the file |
| type | OscillatorType | wave form |
| freq | Input | frequency in range [0,20000] |
| amp | Input | amplitude in range [0,2] |
| phase | Float | phase in range [-1,1] |
| pan | Input | stereo balance in range [-1,1] |
| filters | String[0..*] | names of the filters to apply |
| envelope | String | name of the envelope to apply |

| Oscillator | | |
| ---------- | -------------- | --------------------------------------------------------------- |
| **Field** | **Type** | **Description** |
| name | String | should be unique in the scope of the file |
| type | OscillatorType | wave form |
| freq | Input | frequency in range [0,20000] |
| amp | Input | amplitude in range [0,2] |
| phase | Float | phase in range [-1,1] |
| pan | Input | stereo balance in range [-1,1] |
| filters | String[0..*] | names of the filters to apply |
| envelope | Envelope | envelope to apply; if omitted, oscillator will constantly sound |

| OscillatorType |
| --------------- |
Expand All @@ -82,36 +81,36 @@ Run `synth -h` to see all configuration options.
| Sawtooth |
| ReverseSawtooth |

| Noise | | |
| --------- | ------------ | ----------------------------------------- |
| **Field** | **Type** | **Description** |
| name | String | should be unique in the scope of the file |
| amp | Input | amplitude in range [0,2] |
| pan | Input | stereo balance in range [-1,1] |
| filters | String[0..*] | names of the filters to apply |
| envelope | String | name of the envelope to apply |

| Wavetable | | |
| --------- | ------------ | ----------------------------------------- |
| **Field** | **Type** | **Description** |
| name | String | should be unique in the scope of the file |
| amp | Input | amplitude in range [0,2] |
| pan | Input | stereo balance in range [-1,1] |
| freq | Input | periods per second [0,20000] |
| table | Float [0..*] | output values |
| filters | String[0..*] | names of the filters to apply |
| envelope | String | name of the envelope to apply |

| Sampler | | |
| --------- | ------------ | -------------------------------------------------- |
| **Field** | **Type** | **Description** |
| name | String | should be unique in the scope of the file |
| amp | Input | amplitude in range [0,2] |
| pan | Input | stereo balance in range [-1,1] |
| freq | Input | frequency in range [0,SAMPLE_RATE (default 44100)] |
| filters | String[0..*] | names of the filters to apply |
| inputs | String[0..*] | names of the modules that will be sampled |
| envelope | String | name of the envelope to apply |
| Noise | | |
| --------- | ------------ | ---------------------------------------------------------- |
| **Field** | **Type** | **Description** |
| name | String | should be unique in the scope of the file |
| amp | Input | amplitude in range [0,2] |
| pan | Input | stereo balance in range [-1,1] |
| filters | String[0..*] | names of the filters to apply |
| envelope | Envelope | envelope to apply; if omitted, noise will constantly sound |

| Wavetable | | |
| --------- | ------------ | -------------------------------------------------------------- |
| **Field** | **Type** | **Description** |
| name | String | should be unique in the scope of the file |
| amp | Input | amplitude in range [0,2] |
| pan | Input | stereo balance in range [-1,1] |
| freq | Input | periods per second [0,20000] |
| table | Float [0..*] | output values |
| filters | String[0..*] | names of the filters to apply |
| envelope | Envelope | envelope to apply; if omitted, wavetable will constantly sound |

| Sampler | | |
| --------- | ------------ | ------------------------------------------------------------ |
| **Field** | **Type** | **Description** |
| name | String | should be unique in the scope of the file |
| amp | Input | amplitude in range [0,2] |
| pan | Input | stereo balance in range [-1,1] |
| freq | Input | frequency in range [0,SAMPLE_RATE (default 44100)] |
| filters | String[0..*] | names of the filters to apply |
| inputs | String[0..*] | names of the modules that will be sampled |
| envelope | Envelope | envelope to apply; if omitted, sampler will constantly sound |

A sampler periodically samples the output values of the given inputs and outputs their sum.

Expand All @@ -126,18 +125,17 @@ If both `low-cutoff` and `high-cutoff` are omitted, the filter is disabled. If
`high-cutoff` frequency. If `high-cutoff` is omitted, the filter is a highpass filter
transitioning at the `low-cutoff` frequency. If both cutoff frequencies are defined, it becomes a bandpass filter.

| Envelope | | |
| ------------- | -------- | ----------------------------------------- |
| **Field** | **Type** | **Description** |
| name | String | should be unique in the scope of the file |
| attack | Input | attack time in seconds [0,10000] |
| decay | Input | decay time in seconds [0,10000] |
| sustain | Input | sustain time in seconds [0,10000] |
| release | Input | release time in seconds [0,10000] |
| peak | Input | peak amplitude [0,1] |
| sustain-level | Input | sustain amplitude [0,1] |
| bpm | Input | triggers per minute [0,600000] |
| time-shift | Float | initial time shift |
| Envelope | | |
| ------------- | -------- | --------------------------------- |
| **Field** | **Type** | **Description** |
| attack | Input | attack time in seconds [0,10000] |
| decay | Input | decay time in seconds [0,10000] |
| sustain | Input | sustain time in seconds [0,10000] |
| release | Input | release time in seconds [0,10000] |
| peak | Input | peak amplitude [0,1] |
| sustain-level | Input | sustain amplitude [0,1] |
| bpm | Input | triggers per minute [0,600000] |
| time-shift | Float | initial time shift |

| Input | | |
| --------- | ------------- | ----------------------------------------------------------------------- |
Expand Down
21 changes: 9 additions & 12 deletions examples/envelope.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,17 @@ oscillators:
type: Sine
freq: {val: 200}
amp: {val: 0.9, mod: [lfo1], mod-amp: 0.1}
envelope: e1
envelope:
attack: {val: 0.05}
decay: {val: 0.1}
sustain: {val: 0.5}
release: {val: 1}
peak: {val: 1}
sustain-level: {val: 0.1}
time-shift: 3
bpm: {val: 40}

- name: lfo1
type: Sine
freq: {val: 8}
amp: {val: 1}

envelopes:
- name: e1
attack: {val: 0.05}
decay: {val: 0.1}
sustain: {val: 0.5}
release: {val: 1}
peak: {val: 1}
sustain-level: {val: 0.1}
time-shift: 3
bpm: {val: 40}
3 changes: 0 additions & 3 deletions module/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import (
"github.com/iljarotar/synth/utils"
)

type EnvelopesMap map[string]*Envelope

type Envelope struct {
Name string `yaml:"name"`
Attack Input `yaml:"attack"`
Decay Input `yaml:"decay"`
Sustain Input `yaml:"sustain"`
Expand Down
6 changes: 3 additions & 3 deletions module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ func modulate(param Input, lim limits, modMap ModulesMap) float64 {
return utils.Limit(y, lim.min, lim.max)
}

func applyEnvelope(x float64, envelopeName string, envelopesMap EnvelopesMap) float64 {
if e, ok := envelopesMap[envelopeName]; ok {
return x * e.current
func applyEnvelope(x float64, envelope *Envelope) float64 {
if envelope != nil {
return x * envelope.current
}
return x
}
Expand Down
21 changes: 14 additions & 7 deletions module/noise.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,30 @@ import (

type Noise struct {
Module
Name string `yaml:"name"`
Amp Input `yaml:"amp"`
Pan Input `yaml:"pan"`
Filters []string `yaml:"filters"`
Envelope string `yaml:"envelope"`
Name string `yaml:"name"`
Amp Input `yaml:"amp"`
Pan Input `yaml:"pan"`
Filters []string `yaml:"filters"`
Envelope *Envelope `yaml:"envelope"`
inputs []filterInputs
sampleRate float64
}

func (n *Noise) Initialize(sampleRate float64) {
if n.Envelope != nil {
n.Envelope.Initialize()
}
n.sampleRate = sampleRate
n.limitParams()
n.inputs = make([]filterInputs, len(n.Filters))
n.current = stereo(noise()*n.Amp.Val, n.Pan.Val)
}

func (n *Noise) Next(modMap ModulesMap, filtersMap FiltersMap, envelopesMap EnvelopesMap) {
func (n *Noise) Next(t float64, modMap ModulesMap, filtersMap FiltersMap) {
if n.Envelope != nil {
n.Envelope.Next(t, modMap)
}

pan := modulate(n.Pan, panLimits, modMap)
amp := modulate(n.Amp, ampLimits, modMap)

Expand All @@ -35,7 +42,7 @@ func (n *Noise) Next(modMap ModulesMap, filtersMap FiltersMap, envelopesMap Enve
}

y, newInputs := cfg.applyFilters(noise())
y = applyEnvelope(y, n.Envelope, envelopesMap)
y = applyEnvelope(y, n.Envelope)
n.integral += y / n.sampleRate
n.inputs = newInputs
n.current = stereo(y*amp, pan)
Expand Down
13 changes: 10 additions & 3 deletions module/oscillator.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ type Oscillator struct {
Phase float64 `yaml:"phase"`
Pan Input `yaml:"pan"`
Filters []string `yaml:"filters"`
Envelope string `yaml:"envelope"`
Envelope *Envelope `yaml:"envelope"`
inputs []filterInputs
signal SignalFunc
sampleRate float64
}

func (o *Oscillator) Initialize(sampleRate float64) {
if o.Envelope != nil {
o.Envelope.Initialize()
}
o.sampleRate = sampleRate
o.signal = newSignalFunc(o.Type)
o.limitParams()
Expand All @@ -45,7 +48,11 @@ func (o *Oscillator) Initialize(sampleRate float64) {
o.current = stereo(y, o.Pan.Val)
}

func (o *Oscillator) Next(t float64, modMap ModulesMap, filtersMap FiltersMap, envelopesMap EnvelopesMap) {
func (o *Oscillator) Next(t float64, modMap ModulesMap, filtersMap FiltersMap) {
if o.Envelope != nil {
o.Envelope.Next(t, modMap)
}

pan := modulate(o.Pan, panLimits, modMap)
amp := modulate(o.Amp, ampLimits, modMap)
offset := o.getOffset(modMap)
Expand All @@ -58,7 +65,7 @@ func (o *Oscillator) Next(t float64, modMap ModulesMap, filtersMap FiltersMap, e

x := o.signalValue(t, amp, offset)
y, newInputs := cfg.applyFilters(x)
y = applyEnvelope(y, o.Envelope, envelopesMap)
y = applyEnvelope(y, o.Envelope)
avg := (y + o.Current().Mono) / 2
o.integral += avg / o.sampleRate
o.inputs = newInputs
Expand Down
25 changes: 16 additions & 9 deletions module/sampler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,35 @@ import (

type Sampler struct {
Module
Name string `yaml:"name"`
Amp Input `yaml:"amp"`
Pan Input `yaml:"pan"`
Freq Input `yaml:"freq"`
Filters []string `yaml:"filters"`
Inputs []string `yaml:"inputs"`
Envelope string `yaml:"envelope"`
Name string `yaml:"name"`
Amp Input `yaml:"amp"`
Pan Input `yaml:"pan"`
Freq Input `yaml:"freq"`
Filters []string `yaml:"filters"`
Inputs []string `yaml:"inputs"`
Envelope *Envelope `yaml:"envelope"`
inputs []filterInputs
lastTriggeredAt float64
limits
sampleRate float64
}

func (s *Sampler) Initialize(sampleRate float64) {
if s.Envelope != nil {
s.Envelope.Initialize()
}
s.sampleRate = sampleRate
s.limits = limits{min: 0, max: sampleRate}
s.limitParams()
s.inputs = make([]filterInputs, len(s.Filters))
s.current = stereo(0, s.Pan.Val)
}

func (s *Sampler) Next(t float64, modMap ModulesMap, filtersMap FiltersMap, envelopesMap EnvelopesMap) {
func (s *Sampler) Next(t float64, modMap ModulesMap, filtersMap FiltersMap) {
if s.Envelope != nil {
s.Envelope.Next(t, modMap)
}

amp := modulate(s.Amp, ampLimits, modMap)
pan := modulate(s.Pan, panLimits, modMap)
freq := modulate(s.Freq, s.limits, modMap)
Expand All @@ -40,7 +47,7 @@ func (s *Sampler) Next(t float64, modMap ModulesMap, filtersMap FiltersMap, enve

x := s.sample(t, freq, amp, modMap)
y, newInputs := cfg.applyFilters(x)
y = applyEnvelope(y, s.Envelope, envelopesMap)
y = applyEnvelope(y, s.Envelope)
s.integral += y / s.sampleRate
s.inputs = newInputs
s.current = stereo(y, pan)
Expand Down
13 changes: 10 additions & 3 deletions module/wavetable.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ type Wavetable struct {
Amp Input `yaml:"amp"`
Pan Input `yaml:"pan"`
Filters []string `yaml:"filters"`
Envelope string `yaml:"envelope"`
Envelope *Envelope `yaml:"envelope"`
inputs []filterInputs
sampleRate float64
}

func (w *Wavetable) Initialize(sampleRate float64) {
if w.Envelope != nil {
w.Envelope.Initialize()
}
w.sampleRate = sampleRate
w.limitParams()
w.Table = utils.Normalize(w.Table, -1, 1)
Expand All @@ -29,7 +32,11 @@ func (w *Wavetable) Initialize(sampleRate float64) {
w.current = stereo(y, w.Pan.Val)
}

func (w *Wavetable) Next(t float64, modMap ModulesMap, filtersMap FiltersMap, envelopesMap EnvelopesMap) {
func (w *Wavetable) Next(t float64, modMap ModulesMap, filtersMap FiltersMap) {
if w.Envelope != nil {
w.Envelope.Next(t, modMap)
}

pan := modulate(w.Pan, panLimits, modMap)
amp := modulate(w.Amp, ampLimits, modMap)
freq := modulate(w.Freq, freqLimits, modMap)
Expand All @@ -42,7 +49,7 @@ func (w *Wavetable) Next(t float64, modMap ModulesMap, filtersMap FiltersMap, en

x := w.signalValue(t, amp, freq)
y, newInputs := cfg.applyFilters(x)
y = applyEnvelope(y, w.Envelope, envelopesMap)
y = applyEnvelope(y, w.Envelope)
w.integral += y / w.sampleRate
w.inputs = newInputs
w.current = stereo(y, pan)
Expand Down

0 comments on commit 4f296d2

Please sign in to comment.