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;
}
|