summaryrefslogtreecommitdiff
path: root/examples/mixer/mixer.c
blob: ec492cad34f7989deddb071a5229cb8bfed2dd09 (plain)
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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
/*
 * mixer.c - stereo audio mixer - thomas@apestaart.org
 * example based on helloworld
 * demonstrates the adder plugin and the volume envelope plugin 
 * work in progress but do try it out 
 * 
 * Latest change : 	28/08/2001
 * 					trying to adapt to incsched
 * 					delayed start for channels > 1
 *					now works by quickhacking the
 *					adder plugin to set
 * 					GST_ELEMENT_COTHREAD_STOPPING		
 * Version :		0.5.1
 */

#include <stdlib.h>
#include <gst/gst.h>
#include "mixer.h"
#include <unistd.h>

//#define WITH_BUG
//#define WITH_BUG2
//#define DEBUG
//#define AUTOPLUG	/* define if you want autoplugging of input channels */
/* function prototypes */

input_channel_t*	create_input_channel (int id, char* location);
void				destroy_input_channel (input_channel_t *pipe);
void 				env_register_cp (GstElement *volenv, double cp_time, double cp_level);


gboolean playing;


/* eos will be called when the src element has an end of stream */
void eos(GstElement *element) 
{
  g_print("have eos, quitting ?\n");

//  playing = FALSE;
}

static GstCaps*
gst_play_typefind (GstBin *bin, GstElement *element)
{
  GstElement *typefind;
  GstElement *pipeline;
  GstCaps *caps = NULL;

  GST_DEBUG (0,"GstPipeline: typefind for element \"%s\"\n",
             GST_ELEMENT_NAME(element));

  pipeline = gst_pipeline_new ("autoplug_pipeline");
 
  typefind = gst_elementfactory_make ("typefind", "typefind");
  g_return_val_if_fail (typefind != NULL, FALSE);

  gst_pad_connect (gst_element_get_pad (element, "src"),
                   gst_element_get_pad (typefind, "sink"));
  gst_bin_add (bin, typefind);
  gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (bin));
  
  gst_element_set_state (pipeline, GST_STATE_PLAYING);
  
  // push a buffer... the have_type signal handler will set the found flag
  gst_bin_iterate (GST_BIN (pipeline));
  
  gst_element_set_state (pipeline, GST_STATE_NULL);

  caps = gst_pad_get_caps (gst_element_get_pad (element, "src"));

  gst_pad_disconnect (gst_element_get_pad (element, "src"),
                      gst_element_get_pad (typefind, "sink"));
  gst_bin_remove (bin, typefind);
  gst_bin_remove (GST_BIN (pipeline), GST_ELEMENT (bin));
  gst_object_unref (GST_OBJECT (typefind));
  gst_object_unref (GST_OBJECT (pipeline));
                   
  return caps;
}

int main(int argc,char *argv[]) 
{
  int i, j;
  int num_channels;
  gboolean done;
  
  char buffer[20];
  
  GList *input_channels;		/* structure holding all the input channels */
  
  input_channel_t *channel_in;
  
  GstElement *main_bin;
  GstElement *adder;
  GstElement *audiosink;

  GstPad *pad; /* to request pads for the adder */

  gst_init(&argc,&argv);

  if (argc == 1) {
    g_print("usage: %s <filename1> <filename2> <...>\n", argv[0]);
    exit(-1);
  }
  num_channels = argc - 1;
  
  /* set up output channel and main bin */
  
  /* create adder */
  adder = gst_elementfactory_make ("adder", "adderel");

  /* create an audio sink */
  audiosink = gst_elementfactory_make ("esdsink", "play_audio");

  /* create main bin */
  main_bin = gst_pipeline_new("bin");

  /* connect adder and output to bin */
  GST_INFO (0, "main: adding adder to bin");
  gst_bin_add (GST_BIN(main_bin), adder);
  GST_INFO (0, "main: adding audiosink to bin");
  gst_bin_add (GST_BIN(main_bin), audiosink);

  /* connect adder and audiosink */

  gst_pad_connect(gst_element_get_pad(adder,"src"),
                  gst_element_get_pad(audiosink,"sink"));
  
  /* start looping */
  input_channels = NULL;
  
  for (i = 1; i < argc; ++i)
  {
    printf ("Opening channel %d from file %s...\n", i, argv[i]);
    channel_in = create_input_channel (i, argv[i]);
    input_channels = g_list_append (input_channels, channel_in);

    if (i > 1) gst_element_set_state (main_bin, GST_STATE_PAUSED);
    gst_bin_add (GST_BIN(main_bin), channel_in->pipe);

    /* request pads and connect to adder */
    GST_INFO (0, "requesting pad\n");
    pad = gst_element_request_pad_by_name (adder, "sink%d");
    printf ("\tGot new adder sink pad %s\n", gst_pad_get_name (pad));
    sprintf (buffer, "channel%d", i);
    gst_pad_connect (gst_element_get_pad (channel_in->pipe, buffer), pad);

    /* register a volume envelope */
    printf ("\tregistering volume envelope...\n");

    /* 
     * this is the volenv :
     * each song gets a slot of 5 seconds, with a 5 second fadeout
     * at the end of that, all audio streams play simultaneously
     * at a level ensuring no distortion
     * example for three songs :
     * song1 : starts at full level, plays 5 seconds, faded out at 10 seconds,
     * 		   sleep until 25, fade to end level at 30
     * song2 : starts silent, fades in at 5 seconds, full blast at 10 seconds,
     *		   full level until 15, faded out at 20, sleep until 25, fade to end at 30
     * song3 : starts muted, fades in from 15, full at 20, until 25, fade to end level
     */

    if (i == 1)
    {
      /* first song gets special treatment for end style */
      env_register_cp (channel_in->volenv,  0.0, 1.0);
    }
    else
    {
      env_register_cp (channel_in->volenv,  0.0            , 0.0000001); /* start muted */
      env_register_cp (channel_in->volenv,  i * 10.0 - 15.0, 0.0000001); /* start fade in */
      env_register_cp (channel_in->volenv,  i * 10.0 - 10.0, 1.0);
    }
    env_register_cp (channel_in->volenv,  i * 10.0 -  5.0, 1.0); /* end of full level */

    if (i != num_channels)
    {
      env_register_cp (channel_in->volenv,  i * 10.0         , 0.0000001); /* fade to black */
      env_register_cp (channel_in->volenv,  num_channels * 10.0 - 5.0, 0.0000001); /* start fade in */
    }   
    env_register_cp (channel_in->volenv,  num_channels * 10.0      , 1.0 / num_channels); /* to end level */

#ifndef GST_DISABLE_LOADSAVE
    xmlSaveFile("mixer.xml", gst_xml_write(GST_ELEMENT(main_bin)));
#endif

    /* start playing */
    gst_element_set_state(main_bin, GST_STATE_PLAYING);

    // write out the schedule
    gst_scheduler_show(GST_ELEMENT_SCHED(main_bin));
    playing = TRUE;

    j = 0;
    //printf ("main: start iterating from 0");
    while (playing && j < 100) 
    {
//      printf ("main: iterating %d\n", j);
      gst_bin_iterate(GST_BIN(main_bin));
     //fprintf(stderr,"after iterate()\n");
      ++j;
    }
  }
  printf ("main: all the channels are open\n");
  while (playing) 
  {
    gst_bin_iterate(GST_BIN(main_bin));
    //fprintf(stderr,"after iterate()\n");
  }
  /* stop the bin */
  gst_element_set_state(main_bin, GST_STATE_NULL);

  while (input_channels)
  {
    destroy_input_channel (input_channels->data);
    input_channels = g_list_next (input_channels);
  }
  g_list_free (input_channels);
  
  gst_object_destroy(GST_OBJECT(audiosink));

  gst_object_destroy(GST_OBJECT(main_bin));

  exit(0);
}

input_channel_t*
create_input_channel (int id, char* location)
{
  /* create an input channel, reading from location
   * return a pointer to the channel
   * return NULL if failed
   */

  input_channel_t *channel;
  
  char buffer[20]; 		/* hold the names */

  GstAutoplug *autoplug;
  GstCaps *srccaps;
  GstElement *new_element;  
  GstElement *decoder;

  GST_DEBUG (0, "c_i_p : creating channel with id %d for file %s\n",
  		  id, location);
  
  /* allocate channel */

  channel = (input_channel_t *) malloc (sizeof (input_channel_t));
  if (channel == NULL)
  {
    printf ("create_input_channel : could not allocate memory for channel !\n");
    return NULL;
  }

  /* create channel */

  GST_DEBUG (0, "c_i_p : creating pipeline\n");

  sprintf (buffer, "pipeline%d", id);
  channel->pipe = gst_bin_new (buffer);
  g_assert(channel->pipe != NULL);    
    
  /* create elements */

  GST_DEBUG(0, "c_i_p : creating disksrc\n");

  sprintf (buffer, "disksrc%d", id);
  channel->disksrc = gst_elementfactory_make ("disksrc", buffer);
  g_assert(channel->disksrc != NULL);    

  GST_DEBUG(0, "c_i_p : setting location\n");
  g_object_set(G_OBJECT(channel->disksrc),"location", location, NULL);

  /* add disksrc to the bin before autoplug */
  gst_bin_add(GST_BIN(channel->pipe), channel->disksrc);

  /* connect signal to eos of disksrc */
  g_signal_connect (G_OBJECT(channel->disksrc),"eos",
                     G_CALLBACK(eos),NULL);


#ifdef DEBUG
  printf ("DEBUG : c_i_p : creating volume envelope\n");
#endif

  sprintf (buffer, "volenv%d", id);
  channel->volenv = gst_elementfactory_make ("volenv", buffer);
  g_assert(channel->volenv != NULL);    

  /* autoplug the pipe */

#ifdef DEBUG
  printf ("DEBUG : c_i_p : getting srccaps\n");
#endif

#ifdef WITH_BUG
  srccaps = gst_play_typefind (GST_BIN (channel->pipe), channel->disksrc);
#endif
#ifdef WITH_BUG2
  {
    GstElement *pipeline;

    pipeline = gst_pipeline_new ("autoplug_pipeline");

    gst_bin_add (GST_BIN (pipeline), channel->pipe);
    gst_element_set_state (pipeline, GST_STATE_PLAYING);
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gst_bin_remove (GST_BIN (pipeline), channel->pipe);
    
  }
#endif

#ifdef AUTOPLUG
  if (!srccaps) {
    g_print ("could not autoplug, unknown media type...\n");
    exit (-1);
  }

#ifdef DEBUG
  printf ("DEBUG : c_i_p : creating autoplug\n");
#endif

  autoplug = gst_autoplugfactory_make ("static");
  g_assert (autoplug != NULL);

#ifdef DEBUG
  printf ("DEBUG : c_i_p : autoplugging\n");
#endif
 
  new_element = gst_autoplug_to_caps (autoplug, srccaps, 
  					gst_caps_new ("audio", "audio/raw", NULL), NULL);
 
  if (!new_element) {
    g_print ("could not autoplug, no suitable codecs found...\n");
    exit (-1);
  }

#else

  new_element = gst_bin_new ("autoplug_bin");

  /* static plug, use mad plugin and assume mp3 input */
  printf ("using static plugging for input channel\n");
  decoder =  gst_elementfactory_make ("mad", "mpg123");
  if (!decoder)
  {
    fprintf (stderr, "Could not get a decoder element !\n");
    exit (1);
  }
  gst_bin_add (GST_BIN (new_element), decoder);

  gst_element_add_ghost_pad (new_element, 
		  gst_element_get_pad (decoder, "sink"), "sink");
  gst_element_add_ghost_pad (new_element, 
		  gst_element_get_pad (decoder, "src"), "src_00");
  
#endif  
#ifndef GST_DISABLE_LOADSAVE
  xmlSaveFile ("mixer.gst", gst_xml_write (new_element));
#endif  

  gst_bin_add (GST_BIN(channel->pipe), channel->volenv);
  gst_bin_add (GST_BIN (channel->pipe), new_element);
  
  gst_element_connect (channel->disksrc, "src", new_element, "sink");
  gst_element_connect (new_element, "src_00", channel->volenv, "sink");
  
  /* add a ghost pad */
  sprintf (buffer, "channel%d", id);
  gst_element_add_ghost_pad (channel->pipe,
                             gst_element_get_pad (channel->volenv, "src"), buffer);

   
#ifdef DEBUG
  printf ("DEBUG : c_i_p : end function\n");
#endif

  return channel;
}

void
destroy_input_channel (input_channel_t *channel)
{
  /* 
   * destroy an input channel
   */
   
#ifdef DEBUG
  printf ("DEBUG : d_i_p : start\n");
#endif

  /* destroy elements */

  gst_object_destroy (GST_OBJECT (channel->pipe));

  free (channel);
}

void env_register_cp (GstElement *volenv, double cp_time, double cp_level)
{
  char buffer[30];

  sprintf (buffer, "%f:%f", cp_time, cp_level);
  g_object_set(G_OBJECT(volenv), "controlpoint", buffer, NULL);

}