mixer.c 11 KB
Newer Older
1 2
/*
 * mixer.c - stereo audio mixer - thomas@apestaart.org
3
 * example based on helloworld
4 5 6
 * demonstrates the adder plugin and the volume envelope plugin 
 * work in progress but do try it out 
 * 
7
 * Latest change : 	28/08/2001
8 9 10 11 12
 * 					trying to adapt to incsched
 * 					delayed start for channels > 1
 *					now works by quickhacking the
 *					adder plugin to set
 * 					GST_ELEMENT_COTHREAD_STOPPING		
13
 * Version :		0.5.1
14 15
 */

Wim Taymans's avatar
Wim Taymans committed
16 17
#include <stdlib.h>
#include <gst/gst.h>
18
#include "mixer.h"
19
#include <unistd.h>
20

21 22 23 24
/*#define WITH_BUG */
/*#define WITH_BUG2 */
/*#define DEBUG */
/*#define AUTOPLUG	* define if you want autoplugging of input channels * */
25 26
/* function prototypes */

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

Wim Taymans's avatar
Wim Taymans committed
31 32 33 34 35 36 37

gboolean playing;


/* eos will be called when the src element has an end of stream */
void eos(GstElement *element) 
{
38
  g_print("have eos, quitting ?\n");
Wim Taymans's avatar
Wim Taymans committed
39

40
/*  playing = FALSE; */
Wim Taymans's avatar
Wim Taymans committed
41 42
}

43
G_GNUC_UNUSED static GstCaps*
44
gst_play_type_find (GstBin *bin, GstElement *element)
45 46
{
  GstElement *typefind;
47
  GstElement *pipeline;
48 49
  GstCaps *caps = NULL;

50
  GST_DEBUG ("GstPipeline: typefind for element \"%s\"",
51 52 53
             GST_ELEMENT_NAME(element));

  pipeline = gst_pipeline_new ("autoplug_pipeline");
54
 
55
  typefind = gst_element_factory_make ("typefind", "typefind");
56 57
  g_return_val_if_fail (typefind != NULL, FALSE);

58
  gst_pad_link (gst_element_get_pad (element, "src"),
59 60
                   gst_element_get_pad (typefind, "sink"));
  gst_bin_add (bin, typefind);
61
  gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (bin));
62
  
63
  gst_element_set_state (pipeline, GST_STATE_PLAYING);
64
  
65
  /* push a buffer... the have_type signal handler will set the found flag */
66
  gst_bin_iterate (GST_BIN (pipeline));
67
  
68
  gst_element_set_state (pipeline, GST_STATE_NULL);
69 70 71

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

72
  gst_pad_unlink (gst_element_get_pad (element, "src"),
73 74
                      gst_element_get_pad (typefind, "sink"));
  gst_bin_remove (bin, typefind);
75
  gst_bin_remove (GST_BIN (pipeline), GST_ELEMENT (bin));
76
  gst_object_unref (GST_OBJECT (typefind));
77
  gst_object_unref (GST_OBJECT (pipeline));
78 79 80 81
                   
  return caps;
}

Wim Taymans's avatar
Wim Taymans committed
82 83
int main(int argc,char *argv[]) 
{
84
  int i, j;
85 86 87 88 89 90 91
  int num_channels;
  
  char buffer[20];
  
  GList *input_channels;		/* structure holding all the input channels */
  
  input_channel_t *channel_in;
92 93
  
  GstElement *main_bin;
Wim Taymans's avatar
Wim Taymans committed
94 95 96 97 98 99 100
  GstElement *adder;
  GstElement *audiosink;

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

  gst_init(&argc,&argv);

101 102
  if (argc == 1) {
    g_print("usage: %s <filename1> <filename2> <...>\n", argv[0]);
Wim Taymans's avatar
Wim Taymans committed
103 104
    exit(-1);
  }
105 106 107 108
  num_channels = argc - 1;
  
  /* set up output channel and main bin */
  
Wim Taymans's avatar
Wim Taymans committed
109
  /* create adder */
110
  adder = gst_element_factory_make ("adder", "adderel");
Wim Taymans's avatar
Wim Taymans committed
111 112

  /* create an audio sink */
113
  audiosink = gst_element_factory_make ("esdsink", "play_audio");
Wim Taymans's avatar
Wim Taymans committed
114

115
  /* create main bin */
116
  main_bin = gst_pipeline_new("bin");
Wim Taymans's avatar
Wim Taymans committed
117

118
  /* link adder and output to bin */
119
  GST_INFO ( "main: adding adder to bin");
120
  gst_bin_add (GST_BIN(main_bin), adder);
121
  GST_INFO ( "main: adding audiosink to bin");
122
  gst_bin_add (GST_BIN(main_bin), audiosink);
Wim Taymans's avatar
Wim Taymans committed
123

124
  /* link adder and audiosink */
Wim Taymans's avatar
Wim Taymans committed
125

126
  gst_pad_link(gst_element_get_pad(adder,"src"),
Wim Taymans's avatar
Wim Taymans committed
127
                  gst_element_get_pad(audiosink,"sink"));
128
  
129
  /* start looping */
130 131 132 133 134 135
  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]);
136 137 138 139
    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);
140

141
    /* request pads and link to adder */
142
    GST_INFO ( "requesting pad\n");
143
    pad = gst_element_get_request_pad (adder, "sink%d");
144
    printf ("\tGot new adder sink pad %s\n", gst_pad_get_name (pad));
145
    sprintf (buffer, "channel%d", i);
146
    gst_pad_link (gst_element_get_pad (channel_in->pipe, buffer), pad);
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

    /* 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 */
Wim Taymans's avatar
Wim Taymans committed
183

184
#ifndef GST_DISABLE_LOADSAVE
185
    gst_xml_write_file (GST_ELEMENT (main_bin), fopen ("mixer.xml", "w"));
186
#endif
187

188 189
    /* start playing */
    gst_element_set_state(main_bin, GST_STATE_PLAYING);
Wim Taymans's avatar
Wim Taymans committed
190

191
    /* write out the schedule */
192
    gst_scheduler_show(GST_ELEMENT_SCHED(main_bin));
193
    playing = TRUE;
Wim Taymans's avatar
Wim Taymans committed
194

195
    j = 0;
196
    /*printf ("main: start iterating from 0"); */
197 198
    while (playing && j < 100) 
    {
199
/*      printf ("main: iterating %d\n", j); */
200
      gst_bin_iterate(GST_BIN(main_bin));
201
     /*fprintf(stderr,"after iterate()\n"); */
202 203 204 205 206 207
      ++j;
    }
  }
  printf ("main: all the channels are open\n");
  while (playing) 
  {
Wim Taymans's avatar
Wim Taymans committed
208
    gst_bin_iterate(GST_BIN(main_bin));
209
    /*fprintf(stderr,"after iterate()\n"); */
Wim Taymans's avatar
Wim Taymans committed
210 211 212 213
  }
  /* stop the bin */
  gst_element_set_state(main_bin, GST_STATE_NULL);

214 215 216 217 218 219
  while (input_channels)
  {
    destroy_input_channel (input_channels->data);
    input_channels = g_list_next (input_channels);
  }
  g_list_free (input_channels);
220
  
221
  gst_object_unref(GST_OBJECT(audiosink));
Wim Taymans's avatar
Wim Taymans committed
222

223
  gst_object_unref(GST_OBJECT(main_bin));
Wim Taymans's avatar
Wim Taymans committed
224 225 226 227

  exit(0);
}

228 229
input_channel_t*
create_input_channel (int id, char* location)
230
{
231 232
  /* create an input channel, reading from location
   * return a pointer to the channel
233 234 235
   * return NULL if failed
   */

236 237
  input_channel_t *channel;
  
238 239
  char buffer[20]; 		/* hold the names */

240 241
/*  GstAutoplug *autoplug;
  GstCaps *srccaps; */
242
  GstElement *new_element;  
243
  GstElement *decoder;
244

245
  GST_DEBUG ( "c_i_p : creating channel with id %d for file %s",
246 247
  		  id, location);
  
248
  /* allocate channel */
249

250 251
  channel = (input_channel_t *) malloc (sizeof (input_channel_t));
  if (channel == NULL)
252
  {
253
    printf ("create_input_channel : could not allocate memory for channel !\n");
254 255 256
    return NULL;
  }

257 258
  /* create channel */

259
  GST_DEBUG ( "c_i_p : creating pipeline");
260

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

267
  GST_DEBUG ( "c_i_p : creating filesrc");
268

269
  sprintf (buffer, "filesrc%d", id);
270
  channel->filesrc = gst_element_factory_make ("filesrc", buffer);
271
  g_assert(channel->filesrc != NULL);    
272

273
  GST_DEBUG ( "c_i_p : setting location");
274
  g_object_set(G_OBJECT(channel->filesrc),"location", location, NULL);
275

276 277
  /* add filesrc to the bin before autoplug */
  gst_bin_add(GST_BIN(channel->pipe), channel->filesrc);
278

279
  /* link signal to eos of filesrc */
280
  g_signal_connect (G_OBJECT(channel->filesrc),"eos",
281
                     G_CALLBACK(eos),NULL);
282

283

284 285 286 287 288
#ifdef DEBUG
  printf ("DEBUG : c_i_p : creating volume envelope\n");
#endif

  sprintf (buffer, "volenv%d", id);
289
  channel->volenv = gst_element_factory_make ("volenv", buffer);
290 291 292 293 294 295 296
  g_assert(channel->volenv != NULL);    

  /* autoplug the pipe */

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

298
#ifdef WITH_BUG
299
  srccaps = gst_play_type_find (GST_BIN (channel->pipe), channel->filesrc);
300 301 302 303
#endif
#ifdef WITH_BUG2
  {
    GstElement *pipeline;
304

305 306 307 308 309 310 311 312 313 314 315
    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
316 317 318 319 320 321 322 323
  if (!srccaps) {
    g_print ("could not autoplug, unknown media type...\n");
    exit (-1);
  }

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

325
  autoplug = gst_autoplug_factory_make ("static");
326
  g_assert (autoplug != NULL);
327

328 329 330 331 332 333 334 335 336 337 338
#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);
  }
339 340 341 342 343 344

#else

  new_element = gst_bin_new ("autoplug_bin");

  /* static plug, use mad plugin and assume mp3 input */
345
  printf ("using static plugging for input channel\n");
346
  decoder =  gst_element_factory_make ("mad", "mpg123");
347 348 349 350 351
  if (!decoder)
  {
    fprintf (stderr, "Could not get a decoder element !\n");
    exit (1);
  }
352 353 354 355 356 357
  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");
358
  
359
#endif  
360
#ifndef GST_DISABLE_LOADSAVE
361
  gst_xml_write_file (GST_ELEMENT (new_element), fopen ("mixer.gst", "w"));
362
#endif  
363

364
  gst_bin_add (GST_BIN(channel->pipe), channel->volenv);
365 366
  gst_bin_add (GST_BIN (channel->pipe), new_element);
  
367 368
  gst_element_link_pads (channel->filesrc, "src", new_element, "sink");
  gst_element_link_pads (new_element, "src_00", channel->volenv, "sink");
369
  
370 371
  /* add a ghost pad */
  sprintf (buffer, "channel%d", id);
372 373
  gst_element_add_ghost_pad (channel->pipe,
                             gst_element_get_pad (channel->volenv, "src"), buffer);
374

375
   
376 377 378 379
#ifdef DEBUG
  printf ("DEBUG : c_i_p : end function\n");
#endif

380
  return channel;
381 382 383
}

void
384
destroy_input_channel (input_channel_t *channel)
385 386
{
  /* 
387
   * destroy an input channel
388 389 390 391 392 393
   */
   
#ifdef DEBUG
  printf ("DEBUG : d_i_p : start\n");
#endif

394
  /* destroy elements */
395

396
  gst_object_unref (GST_OBJECT (channel->pipe));
397

398
  free (channel);
399 400
}

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

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

408
}