handle clipping
move osc&gain to the user side
This commit is contained in:
parent
7a508ad33d
commit
ba6e3c3757
59
music.nim
59
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()
|
||||
|
||||
|
|
20
musiclib.nim
20
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
|
||||
|
|
Loading…
Reference in a new issue