mirror of
https://github.com/SleepingInsomniac/pixelfaucet
synced 2025-02-02 20:45:54 +01:00
Improve piano, add harmonica
This commit is contained in:
parent
55f9ce35f4
commit
2c22abf2ea
4 changed files with 49 additions and 9 deletions
|
@ -18,7 +18,7 @@ module PF
|
||||||
@white_keys = [] of Tuple(Vector2(Int32), Vector2(Int32), String)
|
@white_keys = [] of Tuple(Vector2(Int32), Vector2(Int32), String)
|
||||||
@black_keys = [] of Tuple(Vector2(Int32), Vector2(Int32), String)
|
@black_keys = [] of Tuple(Vector2(Int32), Vector2(Int32), String)
|
||||||
|
|
||||||
@instruments : Array(Instrument) = [RetroVoice.new, PianoVoice.new, Flute.new, KickDrum.new, SnareDrum.new]
|
@instruments : Array(Instrument) = [RetroVoice.new, PianoVoice.new, Flute.new, KickDrum.new, SnareDrum.new, Harmonica.new]
|
||||||
|
|
||||||
def initialize(*args, **kwargs)
|
def initialize(*args, **kwargs)
|
||||||
super
|
super
|
||||||
|
|
|
@ -6,7 +6,7 @@ module PF
|
||||||
@device_id : LibSDL::AudioDeviceID
|
@device_id : LibSDL::AudioDeviceID
|
||||||
property volume = 0.5
|
property volume = 0.5
|
||||||
# https://dsp.stackexchange.com/questions/3581/algorithms-to-mix-audio-signals-without-clipping
|
# https://dsp.stackexchange.com/questions/3581/algorithms-to-mix-audio-signals-without-clipping
|
||||||
property headroom = 0.4
|
property headroom = 0.3
|
||||||
delegate :freq, to: @spec
|
delegate :freq, to: @spec
|
||||||
@playing : Bool = false
|
@playing : Bool = false
|
||||||
getter time : Float64 = 0.0
|
getter time : Float64 = 0.0
|
||||||
|
|
|
@ -3,6 +3,7 @@ module PF
|
||||||
property name : String = "Unnamed Instrument"
|
property name : String = "Unnamed Instrument"
|
||||||
property envelope : Envelope
|
property envelope : Envelope
|
||||||
property wave : Sound::Wave
|
property wave : Sound::Wave
|
||||||
|
property volume : Float64 = 1.0
|
||||||
|
|
||||||
getter sounds : Array(Sound) = [] of Sound
|
getter sounds : Array(Sound) = [] of Sound
|
||||||
@notes : Hash(UInt32, Sound) = {} of UInt32 => Sound
|
@notes : Hash(UInt32, Sound) = {} of UInt32 => Sound
|
||||||
|
@ -13,7 +14,7 @@ module PF
|
||||||
|
|
||||||
def on(hertz : Float64, time : Float64)
|
def on(hertz : Float64, time : Float64)
|
||||||
@note_id += 1_u32
|
@note_id += 1_u32
|
||||||
sound = Sound.new(hertz, @envelope, time, @wave)
|
sound = Sound.new(hertz, @envelope, time, @volume, @wave)
|
||||||
@notes[@note_id] = sound
|
@notes[@note_id] = sound
|
||||||
@sounds << sound
|
@sounds << sound
|
||||||
@note_id
|
@note_id
|
||||||
|
@ -47,13 +48,32 @@ module PF
|
||||||
class PianoVoice < Instrument
|
class PianoVoice < Instrument
|
||||||
def initialize
|
def initialize
|
||||||
@name = "Piano"
|
@name = "Piano"
|
||||||
|
|
||||||
|
exp_interpolation = ->(time : Float64, duration : Float64, initial : Float64, level : Float64) do
|
||||||
|
# https://www.desmos.com/calculator/r2jn9wurwv
|
||||||
|
curve = 1000
|
||||||
|
(initial - level) * ((curve ** -(time / duration)) * (1 + (1 / curve)) - (1 / curve)) + level
|
||||||
|
end
|
||||||
|
|
||||||
@envelope = Envelope.new(
|
@envelope = Envelope.new(
|
||||||
attack: Envelope::Stage.new(0.001, 0.0, 1.0),
|
attack: Envelope::Stage.new(0.001, 0.0, 1.0, exp_interpolation),
|
||||||
decay: Envelope::Stage.new(0.7, 1.0, 0.0),
|
decay: Envelope::Stage.new(3.0, 1.0, 0.0, exp_interpolation),
|
||||||
sustain: Envelope::Stage.new(0.0, 0.0, 0.0),
|
sustain: Envelope::Stage.new(0.0, 0.0, 0.0),
|
||||||
release: Envelope::Stage.new(0.5, 1.0, 0.0)
|
release: Envelope::Stage.new(0.3, 1.0, 0.0)
|
||||||
)
|
)
|
||||||
@wave = Sound.triangle_wave(6.0, 0.0005)
|
|
||||||
|
@wave = ->(time : Float64, hertz : Float64) do
|
||||||
|
# https://www.desmos.com/calculator/mnxargxllk
|
||||||
|
av = 2 * Math::PI * hertz * time
|
||||||
|
y = (Math.sin(Math::PI * (av / Math::PI)) ** 3) + Math.sin(Math::PI * ((av / Math::PI) + (2 / 3)))
|
||||||
|
y = (Math.sin(av) ** 3) + Math.sin(av + 0.6666)
|
||||||
|
y += y / 2
|
||||||
|
y += y / 4
|
||||||
|
y += y / 8
|
||||||
|
y += y / 16
|
||||||
|
y += y / 32
|
||||||
|
y /= 5
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -106,4 +126,23 @@ module PF
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Harmonica < Instrument
|
||||||
|
def initialize
|
||||||
|
@name = "Harmonica"
|
||||||
|
@envelope = Envelope.new(
|
||||||
|
attack: Envelope::Stage.new(0.1, 0.0, 1.0),
|
||||||
|
decay: Envelope::Stage.new(0.3, 1.0, 0.8),
|
||||||
|
sustain: Envelope::Stage.new(Float64::INFINITY, 0.8, 0.8),
|
||||||
|
release: Envelope::Stage.new(0.3, 1.0, 0.0)
|
||||||
|
)
|
||||||
|
@volume = 0.5
|
||||||
|
wave = Sound.square_wave
|
||||||
|
@wave = ->(time : Float64, hertz : Float64) do
|
||||||
|
0.5 * wave.call(time + 0.1, hertz * 2) +
|
||||||
|
wave.call(time, hertz) +
|
||||||
|
rand(-0.05..0.05)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
module PF
|
module PF
|
||||||
struct Note
|
struct Note
|
||||||
NAMES = %w[C C#/Db D D#/Eb E F F#/Gb G G#/Ab A A#/Bb B]
|
TWELFTH_ROOT = 2 ** (1 / 12)
|
||||||
ACCIDENTALS = StaticArray[1u8, 3u8, 6u8, 8u8, 10u8]
|
NAMES = %w[C C#/Db D D#/Eb E F F#/Gb G G#/Ab A A#/Bb B]
|
||||||
|
ACCIDENTALS = StaticArray[1u8, 3u8, 6u8, 8u8, 10u8]
|
||||||
|
|
||||||
getter tuning : Float64 = 440.0
|
getter tuning : Float64 = 440.0
|
||||||
getter number : Float64
|
getter number : Float64
|
||||||
|
|
Loading…
Add table
Reference in a new issue