/
ex_record_name.c
257 lines (207 loc) · 9.19 KB
/
ex_record_name.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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/* ex_record_name
*
* This example automatically detects when you say your name and creates a
* sample (maximum of three seconds). Pressing any key (other than ESC) will
* play it back.
*/
#define ALLEGRO_UNSTABLE
#include "allegro5/allegro.h"
#include "allegro5/allegro_audio.h"
#include "allegro5/allegro_acodec.h"
#include "allegro5/allegro_font.h"
#include "allegro5/allegro_image.h"
#include "allegro5/allegro_primitives.h"
#include "common.c"
int main(int argc, char *argv[])
{
ALLEGRO_DISPLAY *display;
ALLEGRO_FONT *font;
ALLEGRO_AUDIO_RECORDER *recorder;
ALLEGRO_EVENT_QUEUE *queue;
ALLEGRO_TIMER *timer;
int font_height;
/* Frequency is the number of samples per second. */
const int frequency = 44100;
const int channels = 2;
/* The latency is used to determine the size of the fragment buffer.
More accurately, it represents approximately how many seconds will
pass between fragment events. (There may be overhead latency from
the OS or driver the adds a fixed amount of latency on top of
Allegro's.)
For this example, the latency should be kept relatively low since
each fragment is processed in its entirety. Increasing the latency
would increase the size of the fragment, which would decrease the
accuracy of the code that processes the fragment.
But if it's too low, then it will cut out too quickly. (If the
example were more thoroughly written, the latency setting wouldn't
actually change how the voice detection worked.)
*/
const float latency = 0.10;
const int max_seconds = 3; /* number of seconds of voice recording */
int16_t *name_buffer; /* stores up to max_seconds of audio */
int16_t *name_buffer_pos; /* points to the current recorded position */
int16_t *name_buffer_end; /* points to the end of the buffer */
float gain = 0.0f; /* 0.0 (quiet) - 1.0 (loud) */
float begin_gain = 0.3f; /* when to begin recording */
bool is_recording = false;
ALLEGRO_SAMPLE *spl = NULL;
(void) argc;
(void) argv;
if (!al_init()) {
abort_example("Could not init Allegro.\n");
}
if (!al_install_audio()) {
abort_example("Unable to initialize audio addon\n");
}
if (!al_init_acodec_addon()) {
abort_example("Unable to initialize acoded addon\n");
}
if (!al_init_image_addon()) {
abort_example("Unable to initialize image addon\n");
}
if (!al_init_primitives_addon()) {
abort_example("Unable to initialize primitives addon\n");
}
al_init_font_addon();
al_install_keyboard();
font = al_load_bitmap_font("data/bmpfont.tga");
if (!font) {
abort_example("Unable to load data/a4_font.tga\n");
}
font_height = al_get_font_line_height(font);
/* WARNING: This demo assumes an audio depth of INT16 and two channels.
Changing those values will break the demo. Nothing here really needs to be
changed. If you want to fiddle with things, adjust the constants at the
beginning of the program.
*/
recorder = al_create_audio_recorder(
5 / latency, /* five seconds of buffer space */
frequency * latency, /* configure the fragment size to give us the given
latency in seconds */
frequency, /* samples per second (higher => better quality) */
ALLEGRO_AUDIO_DEPTH_INT16, /* 2-byte sample size */
ALLEGRO_CHANNEL_CONF_2 /* stereo */
);
if (!recorder) {
abort_example("Unable to create audio recorder\n");
}
display = al_create_display(640, 480);
if (!display) {
abort_example("Unable to create display\n");
}
/* Used to play back the voice recording. */
al_reserve_samples(1);
/* store up to three seconds */
name_buffer = al_calloc(channels * frequency * max_seconds, sizeof(int16_t));
name_buffer_pos = name_buffer;
name_buffer_end = name_buffer + channels * frequency * max_seconds;
queue = al_create_event_queue();
timer = al_create_timer(1 / 60.0);
al_register_event_source(queue, al_get_display_event_source(display));
al_register_event_source(queue, al_get_audio_recorder_event_source(recorder));
al_register_event_source(queue, al_get_timer_event_source(timer));
al_register_event_source(queue, al_get_keyboard_event_source());
al_start_timer(timer);
al_start_audio_recorder(recorder);
while (true) {
ALLEGRO_EVENT event;
bool do_draw = false;
al_wait_for_event(queue, &event);
if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE ||
(event.type == ALLEGRO_EVENT_KEY_UP && event.keyboard.keycode == ALLEGRO_KEY_ESCAPE)) {
break;
}
else if (event.type == ALLEGRO_EVENT_KEY_CHAR) {
if (spl && event.keyboard.unichar != 27) {
al_play_sample(spl, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, NULL);
}
}
else if (event.type == ALLEGRO_EVENT_TIMER) {
do_draw = true;
}
else if (event.type == ALLEGRO_EVENT_AUDIO_RECORDER_FRAGMENT && recorder != NULL) {
/* Because the recording happens in a different thread (and simply because we are
queuing up events), it's quite possible to receive (process) a fragment event
after the recorder has been stopped or destroyed. Thus, it is important to
somehow check that the recorder is still valid, as we are doing above.
*/
ALLEGRO_AUDIO_RECORDER_EVENT *re = al_get_audio_recorder_event(&event);
int16_t *buffer = re->buffer;
int16_t low = 0, high = 0;
unsigned int i;
/* Calculate the volume by comparing the highest and lowest points. This entire
section assumes we are using fairly small fragment size (low latency). If a
large fragment size were used, then we'd have to inspect smaller portions
of it at a time to more accurately deterine when recording started and
stopped. */
for (i = 0; i < channels * re->samples; ++i) {
if (buffer[i] < low)
low = buffer[i];
else if (buffer[i] > high)
high = buffer[i];
}
gain = gain * 0.25 + ((float) (high - low) / 0xffff) * 0.75;
/* Set arbitrary thresholds for beginning and stopping recording. This probably
should be calibrated by determining how loud the ambient noise is.
*/
if (!is_recording && gain >= begin_gain && name_buffer_pos == name_buffer)
is_recording = true;
else if (is_recording && gain <= 0.10)
is_recording = false;
if (is_recording) {
/* Copy out of the fragment buffer into our own buffer that holds the
name. */
int samples_to_copy = channels * re->samples;
/* Don't overfill up our name buffer... */
if (samples_to_copy > name_buffer_end - name_buffer_pos)
samples_to_copy = name_buffer_end - name_buffer_pos;
if (samples_to_copy) {
/* must multiply by two, since we are using 16-bit samples */
memcpy(name_buffer_pos, re->buffer, samples_to_copy * 2);
}
name_buffer_pos += samples_to_copy;
if (name_buffer_pos >= name_buffer_end) {
is_recording = false;
}
}
if (!is_recording && name_buffer_pos != name_buffer && !spl) {
/* finished recording, but haven't created the sample yet */
spl = al_create_sample(name_buffer, name_buffer_pos - name_buffer, frequency,
ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2, false);
/* We no longer need the recorder. Destroying it is the only way to unlock the device. */
al_destroy_audio_recorder(recorder);
recorder = NULL;
}
}
if (do_draw) {
al_clear_to_color(al_map_rgb(0,0,0));
if (!spl) {
const char *msg = "Say Your Name";
int width = al_get_text_width(font, msg);
al_draw_text(font, al_map_rgb(255,255,255),
320, 240 - font_height / 2, ALLEGRO_ALIGN_CENTRE, msg
);
/* draw volume meter */
al_draw_filled_rectangle(320 - width / 2, 242 + font_height / 2,
(320 - width / 2) + (gain * width), 242 + font_height,
al_map_rgb(0,255,0)
);
/* draw target line that triggers recording */
al_draw_line((320 - width / 2) + (begin_gain * width), 242 + font_height / 2,
(320 - width / 2) + (begin_gain * width), 242 + font_height,
al_map_rgb(255,255,0), 1.0
);
}
else {
al_draw_text(font, al_map_rgb(255,255,255), 320, 240 - font_height / 2,
ALLEGRO_ALIGN_CENTRE, "Press Any Key");
}
al_flip_display();
}
}
if (recorder) {
al_destroy_audio_recorder(recorder);
}
al_free(name_buffer);
return 0;
}