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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
ardour {
["type"] = "dsp",
name = "a-Pong",
category = "Visualization",
license = "MIT",
author = "Ardour Lua Task Force",
description = [[classic game of mixer pong]]
}
-- return possible i/o configurations
function dsp_ioconfig ()
-- -1, -1 = any number of channels as long as input and output count matches
return { [1] = { audio_in = -1, audio_out = -1}, }
end
function dsp_params ()
return
{
{ ["type"] = "input", name = "Bar", min = 0, max = 1, default = 0.5 },
}
end
local fps -- audio samples per game-step
local game_time -- counts up to fps
local ball_x, ball_y -- ball position [0..1]
local dx, dy -- current ball speed
local lost_sound -- audio-sample counter for game-over [0..3*fps]
local ping_sound -- audio-sample counter for ping-sound [0..fps]
local ping_phase -- ping note phase
local ping_pitch
function dsp_init (rate)
self:shmem ():allocate (4)
self:shmem ():clear ()
fps = rate / 25
ping_pitch = 752 / rate
ball_x = 0.5
ball_y = 0
dx = 0.011
dy = 0.0237
end
function dsp_configure (ins, outs)
game_time = fps -- start the ball immediately (notfiy GUI)
ping_sound = fps -- set to end of synth cycle
lost_sound = 3 * fps
end
function dsp_run (ins, outs, n_samples)
local ctrl = CtrlPorts:array () -- get control port array (read/write)
local shmem = self:shmem ()
local state = shmem:to_float (0):array () -- "cast" into lua-table
local changed = false -- flag to notify GUI on every game-step
game_time = game_time + n_samples
-- simple game engine
while game_time > fps do
changed = true
game_time = game_time - fps
-- move the ball
ball_x = ball_x + dx
ball_y = ball_y + dy
-- reflect left/right
if ball_x >= 1 or ball_x <= 0 then dx = -dx end
-- single player (reflect top) -- TODO "stereo" version, 2 ctrls
if ball_y <= 0 then dy = - dy end
if ball_y > 1 then
local bar = ctrl[1] -- get bar position
if math.abs (bar - ball_x) < 0.1 then
dy = - dy
ball_y = 1.0
dx = dx + 0.1 * (bar - ball_x)
-- queue sound (unless it's playing)
if (ping_sound >= fps) then
ping_sound = 0
ping_phase = 0
end
else
-- game over
lost_sound = 0
ball_y = 0
dx = 0.011
end
end
end
-- simple synth -- TODO Optimize
if ping_sound < fps then
local abufs = {}
for c = 1,#outs do
abufs[c] = outs[c]:array();
end
for s = 1, n_samples do
ping_sound = ping_sound + 1
ping_phase = ping_phase + ping_pitch
local snd = 0.7 * math.sin(6.283185307 * ping_phase) * math.sin (3.141592 * ping_sound / fps)
for c = 1,#outs do
abufs[c][s] = abufs[c][s] + snd
end
if ping_sound >= fps then goto ping_end end
end
::ping_end::
end
if lost_sound < 3 * fps then
local abufs = {}
for c = 1,#outs do
abufs[c] = outs[c]:array();
end
for s = 1, n_samples do
lost_sound = lost_sound + 1
-- -12dBFS white noise
local snd = 0.5 * (math.random () - 0.5)
for c = 1,#outs do
abufs[c][s] = abufs[c][s] + snd
end
if lost_sound >= 3 * fps then goto noise_end end
end
::noise_end::
end
if changed then
state[1] = ball_x
state[2] = ball_y
self:queue_draw ()
end
end
function render_inline (ctx, w, max_h)
local ctrl = CtrlPorts:array () -- get control port array (read/write)
local shmem = self:shmem () -- get shared memory region
local state = shmem:to_float (0):array () -- "cast" into lua-table
if (w > max_h) then
h = max_h
else
h = w
end
-- clear background
ctx:rectangle (0, 0, w, h)
ctx:set_source_rgba (.2, .2, .2, 1.0)
ctx:fill ()
-- display bar
local bar_width = w * .1
local bar_space = w - bar_width
ctx:set_line_cap (Cairo.LineCap.Round)
ctx:set_source_rgba (.8, .8, .8, 1.0)
ctx:set_line_width (3.0)
ctx:move_to (bar_space * ctrl[1], h - 3)
ctx:rel_line_to (bar_width, 0)
ctx:stroke ()
-- display ball
ctx:move_to (state[1] * w, state[2] * (h - 5))
ctx:close_path ()
ctx:stroke ()
return {w, h}
end
|