Chapter 12. Putting all together

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 sine 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 AgsFxFactory.

The example creates 2 different AgsAudio objects. One called master which does the actual playback and a second called slave doing the sequencer work. Since the slave is linked to the master, we only have to start slave, which initializes the audio tree for playback.

Note, here thread-safety doesn't matter. If you need to do more complex work-flows, you have to care about it. In practice you wouldn't make direct use of any struct fields. Rather use the appropriate getter/setter functions and take care of owner ship.

Usually, you wouldn't call directly void ags_channel_set_link(AgsChannel*, AgsChannel*, GError**), but rather use the AgsLinkChannel task and add it to the AgsTaskLauncher. Else, everything is fine.

Example 12.1. Simple pattern sequencer with master playback

#include <glib.h>
#include <glib-object.h>

#include <ags/libags.h>
#include <ags/libags-audio.h>

AgsAudio* setup_master(AgsApplicationContext *application_context);
AgsAudio* setup_slave(AgsApplicationContext *application_context);

AgsAudio*
setup_master(AgsApplicationContext *application_context)
{
  AgsAudio *audio;
  AgsChannel *channel;
  AgsChannel *start_output;
  AgsRecallContainer *playback_play_container;
  AgsRecallContainer *playback_recall_container;
  
  GObject *soundcard;

  GList *start_list;
  GList *start_recall;

  guint n_audio_channels, n_output_pads, n_input_pads;
  gint position;
  
  /* get soundcard */
  start_list = ags_sound_provider_get_soundcard(AGS_SOUND_PROVIDER(application_context));

  soundcard = start_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);

  /* create recall container */
  position = 0;
  
  playback_play_container = ags_recall_container_new();
  playback_recall_container = ags_recall_container_new();
  
  start_recall = ags_fx_factory_create(audio,
				       playback_play_container, playback_recall_container,
				       "ags-fx-playback",
				       NULL,
				       NULL,
				       0, n_audio_channels,
				       0, n_output_pads,
				       position,
				       (AGS_FX_FACTORY_ADD |
					AGS_FX_FACTORY_INPUT),
				       0);

  g_list_free_full(start_recall,
		   (GDestroyNotify) g_object_unref);
  
  /* set output soundcard channel on ags-fx-playback */
  start_output = NULL;
  
  g_object_get(audio,
	       "output", &start_output,
	       NULL);
  
  channel = start_output;

  if(channel != NULL){
    g_object_ref(channel);
  }
  
  while(channel != NULL){
    AgsChannel *next;

    GList *start_play, *play;

    start_play = NULL;
    
    g_object_get(channel,
		 "play", &start_play,
		 NULL);
    
    play = start_play;

    while((play = ags_play_template_find_type(play,
					      AGS_TYPE_FX_PLAYBACK_CHANNEL)) != NULL){
      g_object_set(play->data,
		   "output-soundcard-channel", channel->audio_channel,
		   NULL);

      /* iterate */
      play = play->next;
    }

    g_list_free_full(start_play,
		     (GDestroyNotify) g_object_unref);
  

    /* iterate */
    next = ags_channel_next(channel);

    g_object_unref(channel);
    
    channel = next;
  }

  /* unref */
  g_list_free_full(start_list,
		   (GDestroyNotify) g_object_unref);

  if(start_output != NULL){
    g_object_unref(start_output);
  }

  return(audio);
}

AgsAudio*
setup_slave(AgsApplicationContext *application_context)
{
  AgsAudio *audio;
  AgsChannel *channel;
  AgsChannel *start_input;
  AgsAudioSignal *audio_signal;
  AgsRecallContainer *pattern_play_container;
  AgsRecallContainer *pattern_recall_container;  
  AgsRecallContainer *buffer_play_container;
  AgsRecallContainer *buffer_recall_container;

  AgsDelayAudioRun *play_delay_audio_run;
  AgsCountBeatsAudioRun *play_count_beats_audio_run;

  GObject *soundcard;

  GList *start_list;
  GList *start_pattern;
  GList *start_recall, *recall;

  guint n_audio_channels, n_output_pads, n_input_pads;
  gint position;
  gdouble volume;
  guint current_phase, prev_phase;
  guint i, j, k;
  
  GValue value;
  
  /* get soundcard */
  start_list = ags_sound_provider_get_soundcard(AGS_SOUND_PROVIDER(application_context));

  soundcard = start_list->data;

  /* create master playback */
  audio = ags_audio_new(soundcard);
  ags_audio_set_flags(audio,
		      (AGS_AUDIO_OUTPUT_HAS_RECYCLING |
		       AGS_AUDIO_INPUT_HAS_RECYCLING));
  ags_audio_set_ability_flags(audio, (AGS_SOUND_ABILITY_SEQUENCER));
  ags_audio_set_behaviour_flags(audio, (AGS_SOUND_BEHAVIOUR_PATTERN_MODE |
					AGS_SOUND_BEHAVIOUR_REVERSE_MAPPING |
					AGS_SOUND_BEHAVIOUR_DEFAULTS_TO_INPUT));

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

  while(channel != NULL){
    ags_channel_set_ability_flags(channel, (AGS_SOUND_ABILITY_SEQUENCER));

    channel = channel->next;
  }
  
  /* add pattern and generate sound */
  start_input = NULL;

  g_object_get(audio,
	       "input", &start_input,
	       NULL);
  
  channel = start_input;

  if(channel != NULL){
    g_object_ref(channel);
  }  
  
  for(i = 0; i < n_input_pads; i++){
    for(j = 0; j < n_audio_channels; j++){
      AgsChannel *next;
      
      /* pattern */
      start_pattern = NULL;
      
      g_object_get(channel,
		   "pattern", &start_pattern,
		   NULL);

      for(k = 0; k < 16;){
        ags_pattern_toggle_bit(start_pattern->data,
                               0,
                               0,
                               k);

	/* iterate */
        k += 4;
      }

      g_list_free_full(start_pattern,
		       (GDestroyNotify) g_object_unref);

      /* sound */
      audio_signal = ags_audio_signal_new();
      ags_audio_signal_set_flags(audio_signal,
				 AGS_AUDIO_SIGNAL_TEMPLATE);
      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;

	/* iterate */
        stream = stream->next;
        k++;
      }

      ags_recycling_add_audio_signal(channel->first_recycling,
				     audio_signal);

      /* iterate */
      next = ags_channel_next(channel);

      g_object_unref(channel);
      
      channel = next;
    }
  }
  
  /* create recall container */
  position = 0;

  pattern_play_container = ags_recall_container_new();
  pattern_recall_container = ags_recall_container_new();

  buffer_play_container = ags_recall_container_new();
  buffer_recall_container = ags_recall_container_new();

  /* ags-fx-pattern */
  start_recall = ags_fx_factory_create(audio,
				       pattern_play_container, pattern_recall_container,
				       "ags-fx-pattern",
				       NULL,
				       NULL,
				       0, n_audio_channels,
				       0, n_input_pads,
				       position,
				       (AGS_FX_FACTORY_ADD | AGS_FX_FACTORY_INPUT),
				       0);

  g_list_free_full(start_recall,
		   (GDestroyNotify) g_object_unref);

  /* ags-fx-buffer */
  start_recall = ags_fx_factory_create(audio,
				       buffer_play_container, buffer_recall_container,
				       "ags-fx-buffer",
				       NULL,
				       NULL,
				       0, n_audio_channels,
				       0, n_input_pads,
				       position,
				       (AGS_FX_FACTORY_ADD | AGS_FX_FACTORY_INPUT),
				       0);

  g_list_free_full(start_recall,
		   (GDestroyNotify) g_object_unref);

  /* unref */
  g_list_free_full(start_list,
		   (GDestroyNotify) g_object_unref);

  if(start_input != NULL){
    g_object_unref(start_input);
  }

  return(audio);
}

int
main(int argc, char **argv)
{
  AgsAudio *master, *slave;
  AgsChannel *start_output, *output;
  AgsChannel *start_input, *input;

  AgsStartAudio *start_audio;
  
  AgsThread *main_loop;
  AgsTaskLauncher *task_launcher;
  
  AgsApplicationContext *application_context;
  AgsConfig *config;

  GError *error;

  /* create application context */
  application_context = ags_audio_application_context_new();
  g_object_ref(audio_application_context);

  ags_application_context_prepare(audio_application_context);
  ags_application_context_setup(audio_application_context);

  task_launcher = ags_concurrency_provider_get_task_launcher(AGS_CONCURRENCY_PROVIDER(application_context));
  
  /* set config */
  config = application_context->config;

  ags_config_set_value(config,
                       AGS_CONFIG_THREAD,
                       "model",
                       "super-threaded");
  ags_config_set_value(config,
                       AGS_CONFIG_THREAD,
                       "super-threaded-scope",
                       "channel");

  /* main loop */
  main_loop = application_context->main_loop;

  /* setup audio tree */
  master = setup_master(application_context);
  slave = setup_slave(application_context);

  /* set link */
  start_input = NULL;
  start_output = NULL;
  
  g_object_get(master,
	       "input", &start_input,
	       NULL);

  g_object_get(slave,
	       "input", &start_output,
	       NULL);
  
  input = start_input;

  if(input != NULL){
    g_object_ref(input);
  }  

  output = start_output;

  if(output != NULL){
    g_object_ref(output);
  }  

  while(input != NULL &&
        output != NULL){
    AgsChannel *next;
    
    error = NULL;
    ags_channel_set_link(input,
                         output,
                         &error);

    if(error != NULL){
      g_message("%s", error->message);
    }

    /* iterate output */
    next = ags_channel_next(output);

    g_object_unref(output);
    
    output = next;

    /* iterate input */
    next = ags_channel_next(input);

    g_object_unref(input);
    
    input = next;
  }

  if(start_output != NULL){
    g_object_unref(start_output);
  }

  if(start_input != NULL){
    g_object_unref(start_input);
  }

  start_audio = ags_start_audio_new(slave,
				    AGS_SOUND_SCOPE_SEQUENCER);

  /* launch task */
  sleep(3);

  ags_task_launcher_add_task(task_launcher,
			     start_audio);

  /* GLib's main loop */
  g_main_loop_run(g_main_loop_new(NULL,
				  FALSE));
  
  return(0);
}