static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg){ DATA *data = (DATA*) arg; float num = 0.1; al_lock_mutex(data->mutex); bool modi_X = data->modi_X; data->ready = true; al_broadcast_cond(data->cond); al_unlock_mutex(data->mutex); while(!al_get_thread_should_stop(thr)){ al_lock_mutex(data->mutex); if(modi_X) data->posiX += num; else data->posiY += num; al_unlock_mutex(data->mutex); al_rest(0.01); } return NULL; }
static void *android_app_trampoline(ALLEGRO_THREAD *thr, void *arg) { const int argc = 1; const char *argv[2] = {system_data.user_lib, NULL}; int ret; (void)thr; (void)arg; ALLEGRO_DEBUG("signaling running"); al_lock_mutex(system_data.mutex); system_data.trampoline_running = true; al_broadcast_cond(system_data.cond); al_unlock_mutex(system_data.mutex); ALLEGRO_DEBUG("entering main function %p", system_data.user_main); ret = (system_data.user_main)(argc, (char **)argv); /* Can we do anything with this exit code? */ ALLEGRO_DEBUG("returned from main function, exit code = %d", ret); /* NOTE: don't put any ALLEGRO_DEBUG in here after running main! */ android_cleanup(true); return NULL; }
static void toggle_pausedness(int n) { ThreadInfo *info = &thread_info[n]; al_lock_mutex(info->mutex); info->is_paused = !info->is_paused; al_broadcast_cond(info->cond); al_unlock_mutex(info->mutex); }
void *CreateDisplay(ALLEGRO_THREAD *thr, void *arg) { DISPLAY_DATA *data = (DISPLAY_DATA*) arg; ALLEGRO_DISPLAY *display; ALLEGRO_EVENT_QUEUE *queue = al_create_event_queue(); if(!queue) { fprintf(stderr, "failed to create event queue"); return NULL; } al_lock_mutex(data->mutex); display = AllegroNewDisplay(data->sizeX, data->sizeY, queue); al_broadcast_cond(data->cond); // Define the size of the image Window<int> scr(0, data->sizeX, 0, data->sizeY); // Define the domain in which we test for points Window<double> fract(data->zoomXmin, data->zoomXmax, data->zoomYmin, data->zoomYmax); al_unlock_mutex(data->mutex); // Generate the fractal Mandelbrot(scr, fract); while(!al_get_thread_should_stop(thr)) { ALLEGRO_EVENT ev; al_wait_for_event(queue, &ev); if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { al_set_thread_should_stop(thr); al_lock_mutex(data->mutex); al_broadcast_cond(data->cond); al_unlock_mutex(data->mutex); break; } } al_destroy_display(display); al_destroy_event_queue(queue); }
static int pulseaudio_stop_voice(ALLEGRO_VOICE *voice) { PULSEAUDIO_VOICE *pv = voice->extra; /* We hold the voice->mutex already. */ if (pv->status == PV_PLAYING) { pv->status = PV_STOPPING; al_broadcast_cond(pv->status_cond); } while (pv->status != PV_IDLE) { al_wait_cond(pv->status_cond, voice->mutex); } return 0; }
static int pulseaudio_start_voice(ALLEGRO_VOICE *voice) { PULSEAUDIO_VOICE *pv = voice->extra; int ret; /* We hold the voice->mutex already. */ if (pv->status == PV_IDLE) { pv->status = PV_PLAYING; al_broadcast_cond(pv->status_cond); ret = 0; } else { ret = 1; } return ret; }
static void pulseaudio_deallocate_voice(ALLEGRO_VOICE *voice) { PULSEAUDIO_VOICE *pv = voice->extra; al_lock_mutex(voice->mutex); pv->status = PV_JOIN; al_broadcast_cond(pv->status_cond); al_unlock_mutex(voice->mutex); /* We do NOT hold the voice mutex here, so this does NOT result in a * deadlock when the thread calls _al_voice_update. */ al_join_thread(pv->poll_thread, NULL); al_destroy_thread(pv->poll_thread); al_destroy_cond(pv->status_cond); al_destroy_mutex(pv->buffer_mutex); pa_simple_free(pv->s); al_free(pv); }
/* Custom routine which runs in another thread to periodically check if DirectSound wants more data for a stream */ static void* _dsound_update(ALLEGRO_THREAD *self, void *arg) { ALLEGRO_VOICE *voice = (ALLEGRO_VOICE *)arg; ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA*)voice->extra; const int bytes_per_sample = ex_data->bits_per_sample / 8; DWORD play_cursor = 0; DWORD write_cursor; DWORD saved_play_cursor = 0; unsigned int samples; LPVOID ptr1, ptr2; DWORD block1_bytes, block2_bytes; unsigned char *data; HRESULT hr; (void)self; unsigned char *silence = (unsigned char *)al_malloc(buffer_size); int silence_value = _al_kcm_get_silence(voice->depth); memset(silence, silence_value, buffer_size); /* Fill buffer */ hr = ex_data->ds8_buffer->Lock(0, buffer_size, &ptr1, &block1_bytes, &ptr2, &block2_bytes, DSBLOCK_ENTIREBUFFER); if (!FAILED(hr)) { samples = buffer_size / bytes_per_sample / ex_data->channels; data = (unsigned char *) _al_voice_update(voice, &samples); memcpy(ptr1, data, block1_bytes); memcpy(ptr2, data + block1_bytes, block2_bytes); ex_data->ds8_buffer->Unlock(ptr1, block1_bytes, ptr2, block2_bytes); } ex_data->ds8_buffer->Play(0, 0, DSBPLAY_LOOPING); do { if (!_dsound_voice_is_playing(voice)) { ex_data->ds8_buffer->Play(0, 0, DSBPLAY_LOOPING); } ex_data->ds8_buffer->GetCurrentPosition(&play_cursor, &write_cursor); /* We try to fill the gap between the saved_play_cursor and the * play_cursor. */ int d = play_cursor - saved_play_cursor; if (d < 0) d += buffer_size; /* Don't fill small gaps. Let it accumulate to amortise the cost of * mixing the samples and locking/unlocking the buffer. */ if (d < MIN_FILL) { al_rest(0.005); continue; } /* Don't generate too many samples at once. The buffer may underrun * while we wait for _al_voice_update to complete. */ samples = d / bytes_per_sample / ex_data->channels; if (samples > MAX_FILL) { samples = MAX_FILL; } /* Generate the samples. */ data = (unsigned char *) _al_voice_update(voice, &samples); if (data == NULL) { data = silence; } hr = ex_data->ds8_buffer->Lock(saved_play_cursor, samples * bytes_per_sample * ex_data->channels, &ptr1, &block1_bytes, &ptr2, &block2_bytes, 0); if (!FAILED(hr)) { memcpy(ptr1, data, block1_bytes); memcpy(ptr2, data + block1_bytes, block2_bytes); hr = ex_data->ds8_buffer->Unlock(ptr1, block1_bytes, ptr2, block2_bytes); if (FAILED(hr)) { ALLEGRO_ERROR("Unlock failed: %s\n", ds_get_error(hr)); } } saved_play_cursor += block1_bytes + block2_bytes; saved_play_cursor %= buffer_size; } while (!ex_data->stop_voice); ex_data->ds8_buffer->Stop(); al_free(silence); ex_data->stop_voice = 0; al_broadcast_cond(voice->cond); return NULL; }
int main(void) { ALLEGRO_THREAD *thread[NUM_THREADS]; ALLEGRO_DISPLAY *display; ALLEGRO_TIMER *timer; ALLEGRO_EVENT_QUEUE *queue; ALLEGRO_EVENT event; bool need_draw; int i; for (i = 0; i < 256; i++) { sin_lut[i] = 128 + (int) (127.0 * sin(i / 8.0)); } if (!al_init()) { abort_example("Could not init Allegro.\n"); return 1; } al_install_keyboard(); al_install_mouse(); display = al_create_display(W * IMAGES_PER_ROW, H * NUM_THREADS / IMAGES_PER_ROW); if (!display) { abort_example("Error creating display\n"); return 1; } timer = al_install_timer(1.0/3); if (!timer) { abort_example("Error creating timer\n"); return 1; } queue = al_create_event_queue(); if (!queue) { abort_example("Error creating event queue\n"); return 1; } al_register_event_source(queue, al_get_display_event_source(display)); al_register_event_source(queue, al_get_keyboard_event_source()); al_register_event_source(queue, al_get_mouse_event_source()); al_register_event_source(queue, al_get_timer_event_source(timer)); /* Note: * Right now, A5 video displays can only be accessed from the thread which * created them (at lesat for OpenGL). To lift this restriction, we could * keep track of the current OpenGL context for each thread and make all * functions accessing the display check for it.. not sure it's worth the * additional complexity though. */ al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_RGB_888); al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP); for (i = 0; i < NUM_THREADS; i++) { thread_info[i].bitmap = al_create_bitmap(W, H); if (!thread_info[i].bitmap) { goto Error; } thread_info[i].mutex = al_create_mutex(); if (!thread_info[i].mutex) { goto Error; } thread_info[i].cond = al_create_cond(); if (!thread_info[i].cond) { goto Error; } thread_info[i].is_paused = false; thread_info[i].random_seed = i; thread[i] = al_create_thread(thread_func, &thread_info[i]); if (!thread[i]) { goto Error; } } set_target(0, -0.56062033041600878303, -0.56064322926933807256); set_target(1, -0.57798076669230014080, -0.63449861991138123418); set_target(2, 0.36676836392830602929, -0.59081385302214906030); set_target(3, -1.48319283039401317303, -0.00000000200514696273); set_target(4, -0.74052910500707636032, 0.18340899525730713915); set_target(5, 0.25437906525768350097, -0.00046678223345789554); set_target(6, -0.56062033041600878303, 0.56064322926933807256); set_target(7, -0.57798076669230014080, 0.63449861991138123418); set_target(8, 0.36676836392830602929, 0.59081385302214906030); for (i = 0; i < NUM_THREADS; i++) { al_start_thread(thread[i]); } al_start_timer(timer); need_draw = true; while (true) { if (need_draw && al_event_queue_is_empty(queue)) { show_images(); need_draw = false; } al_wait_for_event(queue, &event); if (event.type == ALLEGRO_EVENT_TIMER) { need_draw = true; } else if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { int n = (event.mouse.y / H) * IMAGES_PER_ROW + (event.mouse.x / W); if (n < NUM_THREADS) { double x = event.mouse.x - (event.mouse.x / W) * W; double y = event.mouse.y - (event.mouse.y / H) * H; /* Center to the mouse click position. */ if (thread_info[n].is_paused) { thread_info[n].target_x = x / W - 0.5; thread_info[n].target_y = y / H - 0.5; } toggle_pausedness(n); } } else if (event.type == ALLEGRO_EVENT_DISPLAY_EXPOSE) { need_draw = true; } else if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { break; } else if (event.type == ALLEGRO_EVENT_KEY_DOWN) { if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) { break; } need_draw = true; } } for (i = 0; i < NUM_THREADS; i++) { /* Set the flag to stop the thread. The thread might be waiting on a * condition variable, so signal the condition to force it to wake up. */ al_set_thread_should_stop(thread[i]); al_lock_mutex(thread_info[i].mutex); al_broadcast_cond(thread_info[i].cond); al_unlock_mutex(thread_info[i].mutex); /* al_destroy_thread() implicitly joins the thread, so this call is not * strictly necessary. */ al_join_thread(thread[i], NULL); al_destroy_thread(thread[i]); } al_destroy_event_queue(queue); al_uninstall_timer(timer); al_destroy_display(display); return 0; Error: return 1; }
/* TODO: review */ static void *_openal_update(ALLEGRO_THREAD *self, void *arg) { ALLEGRO_VOICE *voice = (ALLEGRO_VOICE*) arg; ALLEGRO_AL_DATA *ex_data = (ALLEGRO_AL_DATA*)voice->extra; unsigned int i, samples_per_update; unsigned int bytes_per_sample; const void *data; void *silence; /* Streams should not be set to looping */ alSourcei(ex_data->source, AL_LOOPING, AL_FALSE); silence = al_calloc(1, ex_data->buffer_size); if (ex_data->format == AL_FORMAT_STEREO8 || ex_data->format == AL_FORMAT_MONO8) { memset(silence, 0x80, ex_data->buffer_size); } for (i = 0; i < ex_data->num_buffers; i++) { alBufferData(ex_data->buffers[i], ex_data->format, silence, ex_data->buffer_size, voice->frequency); } alSourceQueueBuffers(ex_data->source, ex_data->num_buffers, ex_data->buffers); alSourcePlay(ex_data->source); switch (ex_data->format) { case AL_FORMAT_STEREO16: bytes_per_sample = 4; break; case AL_FORMAT_STEREO8: case AL_FORMAT_MONO16: bytes_per_sample = 2; break; default: bytes_per_sample = 1; break; } samples_per_update = ex_data->buffer_size / bytes_per_sample; data = silence; while (!al_get_thread_should_stop(self)) { ALint status = 0; alGetSourcei(ex_data->source, AL_BUFFERS_PROCESSED, &status); if (status <= 0) { /* FIXME what is this for ? */ al_rest(0.001); continue; } while (--status >= 0) { ALuint buffer; data = _al_voice_update(voice, voice->mutex, &samples_per_update); if (data == NULL) data = silence; alSourceUnqueueBuffers(ex_data->source, 1, &buffer); alBufferData(buffer, ex_data->format, data, samples_per_update * bytes_per_sample, voice->frequency); alSourceQueueBuffers(ex_data->source, 1, &buffer); } alGetSourcei(ex_data->source, AL_SOURCE_STATE, &status); if (status == AL_STOPPED) { alSourcePlay(ex_data->source); } } alSourceStop(ex_data->source); al_free(silence); ex_data->stopped = true; al_broadcast_cond(voice->cond); return NULL; }
static void *pulseaudio_update(ALLEGRO_THREAD *self, void *data) { ALLEGRO_VOICE *voice = data; PULSEAUDIO_VOICE *pv = voice->extra; (void)self; for (;;) { enum PULSEAUDIO_VOICE_STATUS status; al_lock_mutex(voice->mutex); while ((status = pv->status) == PV_IDLE) { al_wait_cond(pv->status_cond, voice->mutex); } al_unlock_mutex(voice->mutex); if (status == PV_JOIN) { break; } if (status == PV_PLAYING) { unsigned int frames = pv->buffer_size_in_frames; if (voice->is_streaming) { // streaming audio const void *data = _al_voice_update(voice, voice->mutex, &frames); if (data) { pa_simple_write(pv->s, data, frames * pv->frame_size_in_bytes, NULL); } } else { // direct buffer audio al_lock_mutex(pv->buffer_mutex); const char *data = pv->buffer; unsigned int len = frames * pv->frame_size_in_bytes; pv->buffer += frames * pv->frame_size_in_bytes; if (pv->buffer > pv->buffer_end) { len = pv->buffer_end - data; pv->buffer = voice->attached_stream->spl_data.buffer.ptr; voice->attached_stream->pos = 0; if (voice->attached_stream->loop == ALLEGRO_PLAYMODE_ONCE) { al_lock_mutex(voice->mutex); pv->status = PV_STOPPING; al_broadcast_cond(pv->status_cond); al_unlock_mutex(voice->mutex); } } else { voice->attached_stream->pos += frames; } al_unlock_mutex(pv->buffer_mutex); pa_simple_write(pv->s, data, len, NULL); } } else if (status == PV_STOPPING) { pa_simple_drain(pv->s, NULL); al_lock_mutex(voice->mutex); pv->status = PV_IDLE; al_broadcast_cond(pv->status_cond); al_unlock_mutex(voice->mutex); } } return NULL; }
/* _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; }