-
Notifications
You must be signed in to change notification settings - Fork 1
/
music_server.rb
112 lines (90 loc) · 2.51 KB
/
music_server.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
require 'drb/drb'
srand
SINE = -> { Math.sin(2 * Math::PI * @step) }
SQUARE = -> { @step < 0.5 ? -1 : 1 }
TRIANGLE = -> { 1 - 4 * (@step.round - @step).abs }
SAW = -> { 2 * (@step - @step.round) }
NOISE = -> { rand - 0.5 }
EXP_FALLOFF = -> { [(1 / (@note_frame * 0.002)), 1.0].min }
EXP_FALLOFF2 = -> { [(1 / (@note_frame * 0.005)), 1.0].min }
LIN_FALLOFF = -> { (50000.0 - @note_frame) / 50000.0 }
SNARE = -> { e(EXP_FALLOFF) * e(NOISE) }
BASSDRUM = -> { e(EXP_FALLOFF2) * e(NOISE) * 0.1 + e(SINE) * 0.9 * e(EXP_FALLOFF) }
OCTAVE = -> { e(SINE) * 0.5 + f(SINE,note+12) * 0.5 }
TRIAD = -> { e(SINE) * 0.3 + f(SINE,note+3) * 0.3 + f(SINE,note+5) * 0.3 }
PHASED = -> { fr(SINE, Math.sin(@step / 4.0) * (freq / 2.0)) }
class MusicServer
def initialize
@file = $DEBUG ? File.open("out.log", "w") : nil
@note_on = false
@sample_rate = 48000
@freq = 0
@amp = 0.6
@fn = SINE
@step = 0
@frame = 0
@note_frame = 0
@note = 0
end
attr_accessor :fn, :freq, :amp, :sample_rate, :step, :note_frame, :note
def log(*args)
return unless @file
@file.puts "* #{Time.now}: #{args.join(" ")}"
end
def midi_note(status, note, velocity)
log __method__, status, note, velocity
@freq = freq_for_note(note)
@note = note
@note_frame = 0
@note_on = status == 0x90 && velocity > 0
end
def freq_for_note(note)
2.0 ** ((note-49.0)/12.0) * 440.0
end
def midi_all_off
log __method__
@note_on = false
end
def process(samples)
return if !@fn || !@note_on
result = Array.new(samples)
samples.times do |i|
calculate_step
result[i] = @amp.to_f * instance_exec(&@fn)
@frame += 1
@note_frame += 1
end
log __method__, samples, result
result
end
def calculate_step
@step = (@frame * @freq / @sample_rate.to_f) % 1.0
end
def map(samples, &block)
Array.new(samples).map(&block)
end
end
def e(fn = nil, &block)
$music.send(:instance_exec, &(fn || block))
end
def f(fn = nil, note, &block)
orig_freq, orig_step = $music.freq, $music.step
$music.freq = freq_for_note(note)
$music.calculate_step
result = e(fn || block)
$music.freq = orig_freq
$music.step = orig_step
result
end
def fr(fn = nil, freq, &block)
orig_freq, orig_step = $music.freq, $music.step
$music.freq = freq
$music.calculate_step
result = e(fn || block)
$music.freq = orig_freq
$music.step = orig_step
result
end
$music = MusicServer.new
DRb.start_service("druby://localhost:9090", $music)
#DRb.thread.join