From ba6e3c375728eff3510e877f594653a5e373fc5c Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sun, 12 Mar 2023 17:08:21 +0000 Subject: [PATCH] handle clipping move osc&gain to the user side --- music.nim | 59 +++++++++++++++++++++++++++++++++------------------- musiclib.nim | 20 +++++++++--------- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/music.nim b/music.nim index 15a2fc3..24c01ca 100644 --- a/music.nim +++ b/music.nim @@ -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() diff --git a/musiclib.nim b/musiclib.nim index 4984596..91e63c4 100644 --- a/musiclib.nim +++ b/musiclib.nim @@ -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