So far we have seen the most important objects involved doing an audio processing tree. Now we want to do complete example putting all together. In this example we instantiate AgsAudioThread and AgsChannelThread to play a simple pattern. The sound we use is generated using a sinus wave.
In order that the threads are used we provide an appropriate AgsConfig. Further we define an AgsPattern and add the needed recalls to do playback using the AgsRecallFactory.
Example 11.1. Simple pattern sequencer with master playback
#include <ags/object/ags_config.h> #include <ags/object/ags_application_context.h> #include <ags/thread/ags_thread-posix.h> #include <ags/audio/ags_audio_application_context.h> #include <ags/audio/ags_sound_provider.h> #include <ags/audio/ags_audio.h> #include <ags/audio/ags_channel.h> #include <ags/audio/ags_audio_signal.h> #include <ags/audio/ags_output.h> #include <ags/audio/ags_input.h> #include <ags/audio/ags_playback_domain.h> #include <ags/audio/ags_playback.h> #include <ags/audio/ags_pattern.h> #include <ags/audio/ags_recall_audio_run.h> #include <ags/audio/thread/ags_audio_thread.h> #include <ags/audio/thread/ags_channel_thread.h> #include <ags/audio/recall/ags_delay_audio.h> #include <ags/audio/recall/ags_delay_audio_run.h> #include <ags/audio/recall/ags_count_beats_audio.h> #include <ags/audio/recall/ags_count_beats_audio_run.h> #include <ags/audio/recall/ags_copy_pattern_audio.h> #include <ags/audio/recall/ags_copy_pattern_audio_run.h> #include <ags/audio/recall/ags_copy_pattern_channel.h> #include <ags/audio/recall/ags_copy_pattern_channel_run.h> #include <ags/audio/recall/ags_play_channel.h> AgsAudio* setup_master(AgsApplicationContext *application_context); AgsAudio* setup_slave(AgsApplicationContext *application_context); AgsAudio* setup_master(AgsApplicationContext *application_context) { AgsAudio *audio; AgsChannel *channel; GObject *soundcard; GList *list; GList *recall; guint n_audio_channels, n_output_pads, n_input_pads; /* get soundcard */ list = ags_sound_provider_get_soundcard(AGS_SOUND_PROVIDER(application_context)); soundcard = list->data; /* create master playback */ audio = ags_audio_new(soundcard); n_audio_channels = 2; n_output_pads = 1; n_input_pads = 1; ags_audio_set_audio_channels(audio, n_audio_channels); ags_audio_set_pads(audio, AGS_TYPE_OUTPUT, n_output_pads); ags_audio_set_pads(audio, AGS_TYPE_INPUT, n_input_pads); /* add ags-play-master recall */ ags_recall_factory_create(audio, NULL, NULL, "ags-play-master\0", 0, n_audio_channels, 0, n_output_pads, (AGS_RECALL_FACTORY_INPUT, AGS_RECALL_FACTORY_PLAY | AGS_RECALL_FACTORY_ADD), 0); /* set audio channel on play channel */ channel = audio->output; while(channel != NULL){ recall = channel->play; while((recall = ags_recall_template_find_type(recall, AGS_TYPE_PLAY_CHANNEL)) != NULL){ GValue audio_channel_value = {0,}; play_channel = AGS_PLAY_CHANNEL(recall->data); g_value_init(&audio_channel_value, G_TYPE_UINT64); g_value_set_uint64(&audio_channel_value, channel->audio_channel); ags_port_safe_write(play_channel->audio_channel, &audio_channel_value); recall = recall->next; } channel = channel->next; } return(audio); } AgsAudio* setup_slave(AgsApplicationContext *application_context) { AgsAudio *audio; AgsChannel *channel; AgsAudioSignal *audio_signal; AgsPattern *pattern; AgsDelayAudioRun *play_delay_audio_run; AgsCountBeatsAudioRun *play_count_beats_audio_run; GObject *soundcard; GList *list; GList *recall; guint n_audio_channels, n_output_pads, n_input_pads; gdouble volume; guint current_phase, prev_phase; guint i, j, k; GValue value; /* get soundcard */ list = ags_sound_provider_get_soundcard(AGS_SOUND_PROVIDER(application_context)); soundcard = list->data; /* create master playback */ audio = ags_audio_new(soundcard); audio->flags |= (AGS_AUDIO_OUTPUT_HAS_RECYCLING | AGS_AUDIO_INPUT_HAS_RECYCLING); n_audio_channels = 2; n_output_pads = 1; n_input_pads = 1; ags_audio_set_audio_channels(audio, n_audio_channels); ags_audio_set_pads(audio, AGS_TYPE_OUTPUT, n_output_pads); ags_audio_set_pads(audio, AGS_TYPE_INPUT, n_input_pads); /* add pattern and generate sound */ channel = audio->input; for(i = 0; i < n_input_pads; i++){ for(j = 0; j < n_audio_channels; j++){ /* pattern */ pattern = ags_pattern_new(); ags_pattern_set_dim(pattern, 1, 1, 16); ags_channel_add_pattern(channel, pattern); for(k = 0; k < 16;){ ags_pattern_toggle_bit(pattern, 0, 0, k); k += 4; } /* sound */ audio_signal = ags_audio_signal_new(); ags_audio_signal_stream_resize(audio_signal, 5); stream = audio_signal->stream; current_phase = 0; volume = 1.0; k = 0; while(stream != NULL){ ags_synth_sin(soundcard, (signed short *) stream->data, 0, 440.0, current_phase, audio_signal->buffer_size, volume); prev_phase = current_phase; current_phase = (prev_phase + (audio_signal->buffer_size) + k * audio_signal->buffer_size) % 440.0; stream = stream->next; k++; } channel = channel->next; } } /* add ags-delay recall */ ags_recall_factory_create(audio, NULL, NULL, "ags-delay\0", 0, 0, 0, 0, (AGS_RECALL_FACTORY_OUTPUT | AGS_RECALL_FACTORY_ADD | AGS_RECALL_FACTORY_PLAY), 0); recall = ags_recall_find_type(audio->play, AGS_TYPE_DELAY_AUDIO_RUN); if(recall != NULL){ play_delay_audio_run = AGS_DELAY_AUDIO_RUN(recall->data); } /* ags-count-beats */ ags_recall_factory_create(audio, NULL, NULL, "ags-count-beats\0", 0, 0, 0, 0, (AGS_RECALL_FACTORY_OUTPUT | AGS_RECALL_FACTORY_ADD | AGS_RECALL_FACTORY_PLAY), 0); recall = ags_recall_find_type(audio->play, AGS_TYPE_COUNT_BEATS_AUDIO_RUN); if(recall != NULL){ play_count_beats_audio_run = AGS_COUNT_BEATS_AUDIO_RUN(recall->data); /* set dependency */ g_object_set(G_OBJECT(play_count_beats_audio_run), "delay-audio-run\0", play_delay_audio_run, NULL); /* make it loop */ g_value_init(&value, G_TYPE_BOOLEAN); g_value_set_boolean(&value, gtk_toggle_button_get_active(window->navigation->loop)); ags_port_safe_write(AGS_COUNT_BEATS_AUDIO(AGS_RECALL_AUDIO_RUN(play_count_beats_audio_run)->recall_audio)->notation_loop, &value); } /* ags-copy-pattern */ ags_recall_factory_create(audio, NULL, NULL, "ags-copy-pattern\0", 0, n_audio_channels, 0, n_input_pads, (AGS_RECALL_FACTORY_INPUT | AGS_RECALL_FACTORY_ADD | AGS_RECALL_FACTORY_RECALL), 0); recall = ags_recall_find_type(audio->recall, AGS_TYPE_COPY_PATTERN_AUDIO_RUN); if(recall != NULL){ recall_copy_pattern_audio_run = AGS_COPY_PATTERN_AUDIO_RUN(recall->data); /* set dependency */ g_object_set(G_OBJECT(recall_copy_pattern_audio_run), "delay-audio-run\0", play_delay_audio_run, "count-beats-audio-run\0", play_count_beats_audio_run, NULL); } /* set pattern object on port */ channel = ags_channel_pad_nth(audio->input, 0); for(i = 0; i < n_input_pads; i++){ for(j = 0; j < n_audio_channels; j++){ GList *list; recall = ags_recall_template_find_type(channel->recall, AGS_TYPE_COPY_PATTERN_CHANNEL); copy_pattern_channel = AGS_COPY_PATTERN_CHANNEL(recall->data); list = channel->pattern; pattern = AGS_PATTERN(list->data); copy_pattern_channel->pattern->port_value.ags_port_object = (GObject *) pattern; ags_portlet_set_port(AGS_PORTLET(pattern), copy_pattern_channel->pattern); channel = channel->next; } } return(audio); } int main(int argc, char **argv) { AgsAudio *master, *slave; AgsChannel *output, *input; AgsThread *main_loop; AgsApplicationContext *application_context; AgsConfig *config; GList *playback; GList *start_queue; GError *error; pthread_mutex_t *application_mutex; pthread_mutex_t *main_loop_mutex; pthread_mutex_t *audio_thread_mutex; /* create application context */ application_context = ags_audio_application_context_new(); /* set config */ config = application_context->config; ags_config_set_value(config, AGS_CONFIG_THREAD, "model\0", "super-threaded\0"); ags_config_set_value(config, AGS_CONFIG_THREAD, "super-threaded-scope\0", "channel\0"); /* get mutex manager and application mutex */ mutex_manager = ags_mutex_manager_get_instance(); application_mutex = ags_mutex_manager_get_application_mutex(mutex_manager); /* main loop */ main_loop = application_context->main_loop; pthread_mutex_lock(application_mutex); main_loop_mutex = ags_mutex_manager_lookup(mutex_manager, main_loop); pthread_mutex_unlock(application_mutex); /* setup audio tree */ master = setup_master(application_context); slave = setup_slave(application_context); /* set link */ input = master->input; output = slave->output; while(input != NULL && output != NULL){ error = NULL; ags_channel_set_link(input, output, &error); if(error != NULL){ g_message("%s\0", error->message); } input = input->next; output = output->next; } /* initialize tree */ g_atomic_int_or(&(AGS_PLAYBACK_DOMAIN(audio->playback_domain)->flags), AGS_PLAYBACK_DOMAIN_SEQUENCER); playback = AGS_PLAYBACK_DOMAIN(audio->playback_domain)->playback; list = ags_audio_recursive_play_init(audio, FALSE, TRUE, FALSE); while(playback != NULL){ g_atomic_int_or(&(AGS_PLAYBACK(playback->data)->flags), AGS_PLAYBACK_SEQUENCER); playback = playback->next; } /* parent mutex */ pthread_mutex_lock(application_mutex); audio_thread_mutex = ags_mutex_manager_lookup(mutex_manager, AGS_PLAYBACK_DOMAIN(slave->playback_domain)->audio_thread[1]); pthread_mutex_unlock(application_mutex); /* append to AgsAudioLoop */ ags_audio_loop_add_audio(main_loop, slave); /* add audio and channel threads */ output = slave->output; start_queue = NULL; while(output != NULL){ g_atomic_int_or(&(AGS_CHANNEL_THREAD(AGS_PLAYBACK(output->playback)->channel_thread[1])->flags), (AGS_CHANNEL_THREAD_WAIT | AGS_CHANNEL_THREAD_DONE | AGS_CHANNEL_THREAD_WAIT_SYNC | AGS_CHANNEL_THREAD_DONE_SYNC)); start_queue = g_list_prepend(start_queue, AGS_PLAYBACK(output->playback)->channel_thread[1]); if(AGS_PLAYBACK(output->playback)->channel_thread[1]->parent == NULL){ ags_thread_add_child_extended(AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1], AGS_PLAYBACK(output->playback)->channel_thread[1], TRUE, TRUE); ags_connectable_connect(AGS_CONNECTABLE(AGS_PLAYBACK(output->playback)->channel_thread[1])); } output = output->next; } /* start queue */ start_queue = g_list_reverse(start_queue); pthread_mutex_lock(audio_thread_mutex); if(start_queue != NULL){ if(g_atomic_pointer_get(&(AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1]->start_queue)) != NULL){ g_atomic_pointer_set(&(AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1]->start_queue), g_list_concat(start_queue, g_atomic_pointer_get(&(AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1]->start_queue)))); }else{ g_atomic_pointer_set(&(AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1]->start_queue), start_queue); } } pthread_mutex_unlock(audio_thread_mutex); /* super threaded setup - audio */ start_queue = NULL; if(append_audio->do_sequencer){ g_atomic_int_or(&(AGS_AUDIO_THREAD(AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1])->flags), (AGS_AUDIO_THREAD_WAIT | AGS_AUDIO_THREAD_DONE | AGS_AUDIO_THREAD_WAIT_SYNC | AGS_AUDIO_THREAD_DONE_SYNC)); start_queue = g_list_prepend(start_queue, AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1]); if(AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1]->parent == NULL){ ags_thread_add_child_extended(main_loop, AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1], TRUE, TRUE); ags_connectable_connect(AGS_CONNECTABLE(AGS_PLAYBACK_DOMAIN(audio->playback_domain)->audio_thread[1])); } } /* start queue */ start_queue = g_list_reverse(start_queue); pthread_mutex_lock(main_loop_mutex); if(start_queue != NULL){ if(g_atomic_pointer_get(&(AGS_THREAD(main_loop)->start_queue)) != NULL){ g_atomic_pointer_set(&(AGS_THREAD(main_loop)->start_queue), g_list_concat(start_queue, g_atomic_pointer_get(&(AGS_THREAD(main_loop)->start_queue)))); }else{ g_atomic_pointer_set(&(AGS_THREAD(main_loop)->start_queue), start_queue); } } pthread_mutex_unlock(main_loop_mutex); /* start threads and join main loop */ ags_thread_start(main_loop); pthread_join(*(main_loop->thread), NULL); return(0); }