forked from maximecb/uvm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsequencer.c
176 lines (136 loc) · 3.93 KB
/
sequencer.c
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
169
170
171
172
173
174
175
176
#include <uvm/syscalls.h>
#include <uvm/utils.h>
#include <uvm/graphics.h>
#include <stdlib.h>
#include <stdint.h>
#define FRAME_WIDTH 435
#define FRAME_HEIGHT 185
#define NUM_STEPS 16
#define NUM_ROWS 6
#define CELL_SIZE 20
#define PAD_SIZE 5
#define BORDER_SIZE 20
// Frame buffer to draw into
u32 frame_buffer[FRAME_HEIGHT][FRAME_WIDTH];
// Sequencer grid
bool grid[NUM_ROWS][NUM_STEPS];
// Buffer used for audio output
u16 audio_buffer[1024];
// Index of the current step
u32 step_idx = 0;
// Sample index in the current step
u32 sample_idx = 0;
// Frequencies for musical notes on the pentatonic scale
float NOTE_FREQS[6] = {
330.0f,
294.0f,
261.0f,
220.0f,
196.0f,
147.0f,
};
void redraw()
{
// Clear the frame buffer, set all pixels to black
memset32(frame_buffer, 0, sizeof(frame_buffer) / sizeof(u32));
for (int j = 0; j < NUM_ROWS; ++j)
{
for (int i = 0; i < NUM_STEPS; ++i)
{
bool cell_on = grid[j][i];
u32 color = 0x22_22_22;
if (cell_on)
{
color = (i == step_idx)? COLOR_WHITE:COLOR_RED;
}
fill_rect(
(u32*)frame_buffer,
FRAME_WIDTH,
FRAME_HEIGHT,
BORDER_SIZE + i * (CELL_SIZE + PAD_SIZE),
BORDER_SIZE + j * (CELL_SIZE + PAD_SIZE),
CELL_SIZE,
CELL_SIZE,
color
);
}
}
window_draw_frame(0, frame_buffer);
}
u16* audio_cb(u16 num_channels, u32 num_samples)
{
assert(num_channels == 1);
assert(num_samples <= 1024);
memset(audio_buffer, 0, sizeof(audio_buffer));
u64 bpm = 120;
u64 beats_per_sec = 2;
u64 steps_per_beat = 4;
u64 steps_per_sec = beats_per_sec * steps_per_beat;
u64 samples_per_step = 44100 / steps_per_sec;
// For each sample to generate
for (int i = 0; i < num_samples; ++i)
{
float out = 0.0f;
for (int j = 0; j < NUM_ROWS; ++j)
{
if (!grid[j][step_idx])
continue;
float freq = NOTE_FREQS[j];
float phase = freq * (float)(i32)sample_idx / 44100.0f;
float cycle_pos = phase - (float)(int)phase;
// Here we assume that cycle_pos is in [0, 1[
// Use a square wave for a retro sound
float osc_val = (cycle_pos < 0.5f)? 1.0f:-1.0f;
// Convert the output to signed 16-bit i16 samples
out = out + osc_val * 0.3f;
}
float env = 1.0f - (float)(i32)sample_idx / 12000.0f;
if (env < 0.0f)
env = 0.0f;
audio_buffer[i] = (i16)(5000.0f * out * env);
sample_idx = sample_idx + 1;
// If it's time to move to the next step
if (sample_idx >= samples_per_step)
{
// Move to the next step
step_idx = (step_idx + 1) % NUM_STEPS;
sample_idx = 0;
// Schedule redrawing as soon as we are done generating audio
time_delay_cb(0, redraw);
}
}
return audio_buffer;
}
void mousedown(u64 window_id, u8 btn_id, i32 x, i32 y)
{
if (btn_id != 0)
{
return;
}
u32 step_idx = (x - BORDER_SIZE) / (CELL_SIZE + PAD_SIZE);
u32 cell_x = (x - BORDER_SIZE) % (CELL_SIZE + PAD_SIZE);
u32 row_idx = (y - BORDER_SIZE) / (CELL_SIZE + PAD_SIZE);
u32 cell_y = (y - BORDER_SIZE) % (CELL_SIZE + PAD_SIZE);
if (row_idx >= NUM_ROWS || step_idx >= NUM_STEPS)
{
return;
}
grid[row_idx][step_idx] = !grid[row_idx][step_idx];
redraw();
}
void keydown(u64 window_id, u16 keycode)
{
if (keycode == KEY_ESCAPE)
{
exit(0);
}
}
void main()
{
window_create(FRAME_WIDTH, FRAME_HEIGHT, "Pentatonic Sequencer", 0);
window_on_keydown(0, keydown);
window_on_mousedown(0, mousedown);
enable_event_loop();
redraw();
audio_open_output(44100, 1, AUDIO_FORMAT_I16, audio_cb);
}