void * dumba5_update_thread(ALLEGRO_THREAD * thread, void * arg) { DUMBA5_PLAYER * dp = (DUMBA5_PLAYER *)arg; ALLEGRO_EVENT_QUEUE * queue; unsigned short *fragment; long n; long size; int n_channels; queue = al_create_event_queue(); al_register_event_source(queue, al_get_audio_stream_event_source(dp->stream)); while(1) { ALLEGRO_EVENT event; al_wait_for_event(queue, &event); if(event.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT) { fragment = (unsigned short *)al_get_audio_stream_fragment(dp->stream); if(!fragment) { return NULL; } n = duh_render(dp->sigrenderer, 16, 0, dp->volume, 65536.0 / dp->freq, dp->bufsize, fragment); if (n == 0) { if (++dp->silentcount >= 2) { duh_end_sigrenderer(dp->sigrenderer); if(!al_set_audio_stream_fragment(dp->stream, fragment)) { } al_destroy_audio_stream(dp->stream); dp->sigrenderer = NULL; return NULL; } } n_channels = duh_sigrenderer_get_n_channels(dp->sigrenderer); n *= n_channels; size = dp->bufsize * n_channels; for (; n < size; n++) { fragment[n] = 0x8000; } if(!al_set_audio_stream_fragment(dp->stream, fragment)) { } } if(al_get_thread_should_stop(thread)) { break; } } al_destroy_event_queue(queue); return NULL; }
int main(int argc, char **argv) { ALLEGRO_AUDIO_RECORDER *r; ALLEGRO_AUDIO_STREAM *s; ALLEGRO_EVENT_QUEUE *q; ALLEGRO_DISPLAY *d; ALLEGRO_FILE *fp = NULL; ALLEGRO_PATH *tmp_path = NULL; int prev = 0; bool is_recording = false; int n = 0; /* number of samples written to disk */ (void) argc; (void) argv; if (!al_init()) { abort_example("Could not init Allegro.\n"); } if (!al_init_primitives_addon()) { abort_example("Unable to initialize primitives addon"); } if (!al_install_keyboard()) { abort_example("Unable to install keyboard"); } if (!al_install_audio()) { abort_example("Unable to initialize audio addon"); } if (!al_init_acodec_addon()) { abort_example("Unable to initialize acodec addon"); } /* Note: increasing the number of channels will break this demo. Other * settings can be changed by modifying the constants at the top of the * file. */ r = al_create_audio_recorder(1000, samples_per_fragment, frequency, audio_depth, ALLEGRO_CHANNEL_CONF_1); if (!r) { abort_example("Unable to create audio recorder"); } s = al_create_audio_stream(playback_fragment_count, playback_samples_per_fragment, frequency, audio_depth, ALLEGRO_CHANNEL_CONF_1); if (!s) { abort_example("Unable to create audio stream"); } al_reserve_samples(0); al_set_audio_stream_playing(s, false); al_attach_audio_stream_to_mixer(s, al_get_default_mixer()); q = al_create_event_queue(); /* Note: the following two options are referring to pixel samples, and have * nothing to do with audio samples. */ al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST); al_set_new_display_option(ALLEGRO_SAMPLES, 8, ALLEGRO_SUGGEST); d = al_create_display(320, 256); if (!d) { abort_example("Error creating display\n"); } al_set_window_title(d, "SPACE to record. P to playback."); al_register_event_source(q, al_get_audio_recorder_event_source(r)); al_register_event_source(q, al_get_audio_stream_event_source(s)); al_register_event_source(q, al_get_display_event_source(d)); al_register_event_source(q, al_get_keyboard_event_source()); al_start_audio_recorder(r); while (true) { ALLEGRO_EVENT e; al_wait_for_event(q, &e); if (e.type == ALLEGRO_EVENT_AUDIO_RECORDER_FRAGMENT) { /* We received an incoming fragment from the microphone. In this * example, the recorder is constantly recording even when we aren't * saving to disk. The display is updated every time a new fragment * comes in, because it makes things more simple. If the fragments * are coming in faster than we can update the screen, then it will be * a problem. */ ALLEGRO_AUDIO_RECORDER_EVENT *re = al_get_audio_recorder_event(&e); audio_buffer_t input = (audio_buffer_t) re->buffer; int sample_count = re->samples; const int R = sample_count / 320; int i, gain = 0; /* Calculate the volume, and display it regardless if we are actively * recording to disk. */ for (i = 0; i < sample_count; ++i) { if (gain < abs(input[i] - sample_center)) gain = abs(input[i] - sample_center); } al_clear_to_color(al_map_rgb(0,0,0)); if (is_recording) { /* Save raw bytes to disk. Assumes everything is written * succesfully. */ if (fp && n < frequency / (float) samples_per_fragment * max_seconds_to_record) { al_fwrite(fp, input, sample_count * sample_size); ++n; } /* Draw a pathetic visualization. It draws exactly one fragment * per frame. This means the visualization is dependent on the * various parameters. A more thorough implementation would use this * event to copy the new data into a circular buffer that holds a * few seconds of audio. The graphics routine could then always * draw that last second of audio, which would cause the * visualization to appear constant across all different settings. */ for (i = 0; i < 320; ++i) { int j, c = 0; /* Take the average of R samples so it fits on the screen */ for (j = i * R; j < i * R + R && j < sample_count; ++j) { c += input[j] - sample_center; } c /= R; /* Draws a line from the previous sample point to the next */ al_draw_line(i - 1, 128 + ((prev - min_sample_val) / (float) sample_range) * 256 - 128, i, 128 + ((c - min_sample_val) / (float) sample_range) * 256 - 128, al_map_rgb(255,255,255), 1.2); prev = c; } } /* draw volume bar */ al_draw_filled_rectangle((gain / (float) max_sample_val) * 320, 251, 0, 256, al_map_rgba(0, 255, 0, 128)); al_flip_display(); } else if (e.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT) { /* This event is received when we are playing back the audio clip. * See ex_saw.c for an example dedicated to playing streams. */ if (fp) { audio_buffer_t output = al_get_audio_stream_fragment(s); if (output) { /* Fill the buffer from the data we have recorded into the file. * If an error occurs (or end of file) then silence out the * remainder of the buffer and stop the playback. */ const size_t bytes_to_read = playback_samples_per_fragment * sample_size; size_t bytes_read = 0, i; do { bytes_read += al_fread(fp, (uint8_t *)output + bytes_read, bytes_to_read - bytes_read); } while (bytes_read < bytes_to_read && !al_feof(fp) && !al_ferror(fp)); /* silence out unused part of buffer (end of file) */ for (i = bytes_read / sample_size; i < bytes_to_read / sample_size; ++i) { output[i] = sample_center; } al_set_audio_stream_fragment(s, output); if (al_ferror(fp) || al_feof(fp)) { al_drain_audio_stream(s); al_fclose(fp); fp = NULL; } } } } else if (e.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { break; } else if (e.type == ALLEGRO_EVENT_KEY_CHAR) { if (e.keyboard.unichar == 27) { /* pressed ESC */ break; } else if (e.keyboard.unichar == ' ') { if (!is_recording) { /* Start the recording */ is_recording = true; if (al_get_audio_stream_playing(s)) { al_drain_audio_stream(s); } /* Reuse the same temp file for all recordings */ if (!tmp_path) { fp = al_make_temp_file("alrecXXX.raw", &tmp_path); } else { if (fp) al_fclose(fp); fp = al_fopen(al_path_cstr(tmp_path, '/'), "w"); } n = 0; } else { is_recording = false; if (fp) { al_fclose(fp); fp = NULL; } } } else if (e.keyboard.unichar == 'p') { /* Play the previously recorded wav file */ if (!is_recording) { if (tmp_path) { fp = al_fopen(al_path_cstr(tmp_path, '/'), "r"); if (fp) { al_set_audio_stream_playing(s, true); } } } } } } /* clean up */ al_destroy_audio_recorder(r); al_destroy_audio_stream(s); if (fp) al_fclose(fp); if (tmp_path) { al_remove_filename(al_path_cstr(tmp_path, '/')); al_destroy_path(tmp_path); } return 0; }
static void mainloop(void) { ALLEGRO_EVENT_QUEUE *queue; ALLEGRO_TIMER *timer; float *buf; double pitch = 440; int i, si; int n = 0; bool redraw = false; for (i = 0; i < N; i++) { frequency[i] = 22050 * pow(2, i / (double)N); stream[i] = al_create_audio_stream(4, SAMPLES_PER_BUFFER, frequency[i], ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_1); if (!stream[i]) { abort_example("Could not create stream.\n"); return; } if (!al_attach_audio_stream_to_mixer(stream[i], al_get_default_mixer())) { abort_example("Could not attach stream to mixer.\n"); return; } } queue = al_create_event_queue(); al_register_event_source(queue, al_get_keyboard_event_source()); for (i = 0; i < N; i++) { al_register_event_source(queue, al_get_audio_stream_event_source(stream[i])); } #ifdef ALLEGRO_POPUP_EXAMPLES if (textlog) { al_register_event_source(queue, al_get_native_text_log_event_source(textlog)); } #endif log_printf("Generating %d sine waves of different sampling quality\n", N); log_printf("If Allegro's resampling is correct there should be little variation\n", N); timer = al_create_timer(1.0 / 60); al_register_event_source(queue, al_get_timer_event_source(timer)); al_register_event_source(queue, al_get_display_event_source(display)); al_start_timer(timer); while (n < 60 * frequency[0] / SAMPLES_PER_BUFFER * N) { ALLEGRO_EVENT event; al_wait_for_event(queue, &event); if (event.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT) { for (si = 0; si < N; si++) { buf = al_get_audio_stream_fragment(stream[si]); if (!buf) { continue; } for (i = 0; i < SAMPLES_PER_BUFFER; i++) { double t = samplepos[si]++ / (double)frequency[si]; buf[i] = sin(t * pitch * ALLEGRO_PI * 2) / N; } if (!al_set_audio_stream_fragment(stream[si], buf)) { log_printf("Error setting stream fragment.\n"); } n++; log_printf("%d", si); if ((n % 60) == 0) log_printf("\n"); } } if (event.type == ALLEGRO_EVENT_TIMER) { redraw = true; } if (event.type == ALLEGRO_EVENT_KEY_DOWN && event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) { break; } if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { break; } #ifdef ALLEGRO_POPUP_EXAMPLES if (event.type == ALLEGRO_EVENT_NATIVE_DIALOG_CLOSE) { break; } #endif if (redraw &&al_is_event_queue_empty(queue)) { ALLEGRO_COLOR c = al_map_rgb(0, 0, 0); int i; al_clear_to_color(al_map_rgb_f(1, 1, 1)); for (i = 0; i < 640; i++) { al_draw_pixel(i, 50 + waveform[i] * 50, c); } al_flip_display(); redraw = false; } } for (si = 0; si < N; si++) { al_drain_audio_stream(stream[si]); } log_printf("\n"); al_destroy_event_queue(queue); }
/* _al_kcm_feed_stream: * A routine running in another thread that feeds the stream buffers as * neccesary, usually getting data from some file reader backend. */ void *_al_kcm_feed_stream(ALLEGRO_THREAD *self, void *vstream) { ALLEGRO_AUDIO_STREAM *stream = vstream; ALLEGRO_EVENT_QUEUE *queue; ALLEGRO_EVENT event; (void)self; ALLEGRO_DEBUG("Stream feeder thread started.\n"); queue = al_create_event_queue(); al_register_event_source(queue, &stream->spl.es); al_lock_mutex(stream->feed_thread_started_mutex); stream->feed_thread_started = true; al_broadcast_cond(stream->feed_thread_started_cond); al_unlock_mutex(stream->feed_thread_started_mutex); stream->quit_feed_thread = false; while (!stream->quit_feed_thread) { char *fragment; ALLEGRO_EVENT event; al_wait_for_event(queue, &event); if (event.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT && !stream->is_draining) { unsigned long bytes; unsigned long bytes_written; ALLEGRO_MUTEX *stream_mutex; fragment = al_get_audio_stream_fragment(stream); if (!fragment) { /* This is not an error. */ continue; } bytes = (stream->spl.spl_data.len) * al_get_channel_count(stream->spl.spl_data.chan_conf) * al_get_audio_depth_size(stream->spl.spl_data.depth); stream_mutex = maybe_lock_mutex(stream->spl.mutex); bytes_written = stream->feeder(stream, fragment, bytes); maybe_unlock_mutex(stream_mutex); if (stream->spl.loop == _ALLEGRO_PLAYMODE_STREAM_ONEDIR) { /* Keep rewinding until the fragment is filled. */ while (bytes_written < bytes && stream->spl.loop == _ALLEGRO_PLAYMODE_STREAM_ONEDIR) { size_t bw; al_rewind_audio_stream(stream); stream_mutex = maybe_lock_mutex(stream->spl.mutex); bw = stream->feeder(stream, fragment + bytes_written, bytes - bytes_written); bytes_written += bw; maybe_unlock_mutex(stream_mutex); } } else if (bytes_written < bytes) { /* Fill the rest of the fragment with silence. */ int silence_samples = (bytes - bytes_written) / (al_get_channel_count(stream->spl.spl_data.chan_conf) * al_get_audio_depth_size(stream->spl.spl_data.depth)); al_fill_silence(fragment + bytes_written, silence_samples, stream->spl.spl_data.depth, stream->spl.spl_data.chan_conf); } if (!al_set_audio_stream_fragment(stream, fragment)) { ALLEGRO_ERROR("Error setting stream buffer.\n"); continue; } /* The streaming source doesn't feed any more, drain buffers and quit. */ if (bytes_written != bytes && stream->spl.loop == _ALLEGRO_PLAYMODE_STREAM_ONCE) { al_drain_audio_stream(stream); stream->quit_feed_thread = true; } } else if (event.type == _KCM_STREAM_FEEDER_QUIT_EVENT_TYPE) { stream->quit_feed_thread = true; } } event.user.type = ALLEGRO_EVENT_AUDIO_STREAM_FINISHED; event.user.timestamp = al_get_time(); al_emit_user_event(&stream->spl.es, &event, NULL); al_destroy_event_queue(queue); ALLEGRO_DEBUG("Stream feeder thread finished.\n"); return NULL; }