1
0
Fork 0
forked from a/yue

handle clipping

move osc&gain to the user side
This commit is contained in:
Locria Cyber 2023-03-12 17:08:21 +00:00
parent 7a508ad33d
commit ba6e3c3757
Signed by: iacore
GPG key ID: ED0D424AE4406330
2 changed files with 48 additions and 31 deletions

View file

@ -1,7 +1,7 @@
import musiclib, std/math
import musiclib, std/[math, sugar]
# Number of times to sample each second
let bitrate = 44100
const bitrate = 44100
func osc_piano*(f, t: float): float =
## Returns the intensity of a tone of frequency f sampled at time t
@ -20,15 +20,28 @@ func osc_piano*(f, t: float): float =
Y *= 1 + 16 * t * math.exp(-6 * t)
Y
func osc_pulse*(f,t:float):float =
let width = (1.0 / f) / 2
let phase: float = t mod (width * 2)
if phase < width: 1.0 else: -1.0
func freq*(octave, step: float): float =
## Returns the frequency of a note
55 * pow(2, (octave + step / 12 - 1))
func p*(len, octave, step, vol: float = 1): Note =
var osc: OscFn = (f, t: float) => 0.0
proc p*(len, octave, step, vol: float = 1): Note =
## Note helper constructor
let osc: OscFn = osc_piano
(len, freq(octave, step), vol, osc)
#------- song region -------
const GAIN_NORMAL = 0.24
osc = (f, t: float) => osc_piano(f, t) * GAIN_NORMAL
let intro = [
p(1,3,3),
p(1,3,7),
@ -51,6 +64,8 @@ let intro = [
p(8,3,7),
]
# osc = osc_pulse
let melody = [
p(1,3,3),
p(1,3,7),
@ -119,17 +134,6 @@ let melody = [
p(2,4,10),
]
let bass = [
p(1,1,3),
p(1,1,10),
p(1,1,1),
p(1,1,8),
p(1,1,3),
p(1,2,3),
p(1,1,1),
p(1,1,10),
]
let melody2 = [
p(1,0,0),
p(1,5,10),
@ -273,6 +277,19 @@ let outro = [
p(16,3,7,2),
]
osc = (f, t: float) => osc_piano(f, t) * GAIN_NORMAL * 1.5
let bass = [
p(1,1,3),
p(1,1,10),
p(1,1,1),
p(1,1,8),
p(1,1,3),
p(1,2,3),
p(1,1,1),
p(1,1,10),
]
from std/algorithm import sort
# Process all lists of notes
@ -280,16 +297,16 @@ var music: seq[ProcessedNote] = @[]
music.process(intro, 0, 4)
music.process(melody, 8, 4)
music.process(melody, 24, 4)
music.process(bass, 24, gain=1.5)
music.process(bass, 32, gain=1.5)
music.process(bass, 24)
music.process(bass, 32)
music.process(melody, 40, 4)
music.process(melody2, 40, 4)
music.process(bass, 40, gain=1.5)
music.process(bass, 48, gain=1.5)
music.process(bass, 40)
music.process(bass, 48)
music.process(melody, 56, 4)
music.process(melody3, 56, 4)
music.process(bass, 56, gain=1.5)
music.process(bass, 64, gain=1.5)
music.process(bass, 56)
music.process(bass, 64)
music.process(outro, 72, 4)
music.sortByStart()

View file

@ -16,12 +16,9 @@ type
vol: float
osc: OscFn
## give some seconds for the note to fade out
const HACK_DROPOFF_DELAY = 2.0
const HACK_LONGEST_NOTE = 16.0
func process*(music: var seq[ProcessedNote], notes: openArray[Note]; start_init: float, speed: float=1, gain:float = 1) =
func process*(music: var seq[ProcessedNote], notes: openArray[Note]; start_init: float, speed: float=1) =
## Adds a list of notes to the music list
##
## `notes` sequence of notes with no rests in between
@ -32,7 +29,7 @@ func process*(music: var seq[ProcessedNote], notes: openArray[Note]; start_init:
assert note.len <= HACK_LONGEST_NOTE, &"note too long: {note.len}"
start = t
let stop = t + note.len / speed
music &= (start, stop, note.freq, note.vol * gain, note.osc)
music &= (start, stop, note.freq, note.vol, note.osc)
t = stop
func sortByStart*(music: var seq[ProcessedNote]) =
@ -45,7 +42,7 @@ func bisect(music: openArray[ProcessedNote], x: float): int =
music.lowerBound(x, (m, key) => cmp(m.start, key))
const GAIN_BIAS: float = pow(2.0, 28.0)
const GAIN_BIAS: float = pow(2.0, 31.0)
proc at*(music: openArray[ProcessedNote], t: float): int32 =
## Returns the total intensity of music sampled at time t
@ -59,10 +56,13 @@ proc at*(music: openArray[ProcessedNote], t: float): int32 =
while i >= 0:
let m = music[i]
assert m.start <= t
if m.stop + HACK_DROPOFF_DELAY >= t:
ret += m.vol * m.osc(m.freq, t - m.start)
if m.start + HACK_LONGEST_NOTE + HACK_DROPOFF_DELAY < t:
if m.start + HACK_LONGEST_NOTE < t:
break
else:
ret += m.vol * m.osc(m.freq, t - m.start)
i -= 1
int32(ret * GAIN_BIAS)
ret *= GAIN_BIAS
# clip sample
clamp(ret, int32.low.float..int32.high.float).int32