mixer.c 10.8 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 8 9 10 11 12 13
 * Latest change : 	28/04/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
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
//#define WITH_BUG
//#define WITH_BUG2
23
//#define DEBUG
24
//#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 44 45 46
static GstCaps*
gst_play_typefind (GstBin *bin, GstElement *element)
{
  GstElement *typefind;
47
  GstElement *pipeline;
48 49
  GstCaps *caps = NULL;

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

  pipeline = gst_pipeline_new ("autoplug_pipeline");
54 55 56 57 58 59 60
 
  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);
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 72 73 74

  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);
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
  int num_channels;
86
  gboolean done;
87 88 89 90 91 92
  
  char buffer[20];
  
  GList *input_channels;		/* structure holding all the input channels */
  
  input_channel_t *channel_in;
93 94
  
  GstElement *main_bin;
Wim Taymans's avatar
Wim Taymans committed
95 96 97 98 99 100 101
  GstElement *adder;
  GstElement *audiosink;

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

  gst_init(&argc,&argv);

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

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

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

119
  /* connect adder and output to bin */
120 121 122 123
  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);
Wim Taymans's avatar
Wim Taymans committed
124 125 126 127 128

  /* connect adder and audiosink */

  gst_pad_connect(gst_element_get_pad(adder,"src"),
                  gst_element_get_pad(audiosink,"sink"));
129
  
130
  /* start looping */
131 132 133 134 135 136
  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]);
137 138 139 140
    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);
141 142

    /* request pads and connect to adder */
143
    GST_INFO (0, "requesting pad\n");
144
    pad = gst_element_request_pad_by_name (adder, "sink%d");
145
    printf ("\tGot new adder sink pad %s\n", gst_pad_get_name (pad));
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
    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 */
Wim Taymans's avatar
Wim Taymans committed
184

185
    xmlSaveFile("mixer.xml", gst_xml_write(GST_ELEMENT(main_bin)));
186

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

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

194 195 196 197 198 199 200 201 202 203 204 205 206
    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) 
  {
Wim Taymans's avatar
Wim Taymans committed
207
    gst_bin_iterate(GST_BIN(main_bin));
208
    //fprintf(stderr,"after iterate()\n");
Wim Taymans's avatar
Wim Taymans committed
209 210 211 212
  }
  /* stop the bin */
  gst_element_set_state(main_bin, GST_STATE_NULL);

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

  gst_object_destroy(GST_OBJECT(main_bin));

  exit(0);
}

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

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

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

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

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

256 257
  /* create channel */

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

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

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

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

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

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

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

282

283 284 285 286 287 288 289 290 291 292 293 294 295
#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
296

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

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

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

324 325
  autoplug = gst_autoplugfactory_make ("static");
  g_assert (autoplug != NULL);
326

327 328 329 330 331 332 333 334 335 336 337
#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);
  }
338 339 340 341 342 343 344 345 346 347 348 349 350 351

#else

  new_element = gst_bin_new ("autoplug_bin");

  /* static plug, use mad plugin and assume mp3 input */
  decoder =  gst_elementfactory_make ("mad", "mpg123");

  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");
352
  
353 354 355
#endif  
  xmlSaveFile ("mixer.gst", gst_xml_write (new_element));

356
  gst_bin_add (GST_BIN(channel->pipe), channel->volenv);
357 358 359 360 361
  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");
  
362 363
  /* add a ghost pad */
  sprintf (buffer, "channel%d", id);
364 365
  gst_element_add_ghost_pad (channel->pipe,
                             gst_element_get_pad (channel->volenv, "src"), buffer);
366

367
   
368 369 370 371
#ifdef DEBUG
  printf ("DEBUG : c_i_p : end function\n");
#endif

372
  return channel;
373 374 375
}

void
376
destroy_input_channel (input_channel_t *channel)
377 378
{
  /* 
379
   * destroy an input channel
380 381 382 383 384 385
   */
   
#ifdef DEBUG
  printf ("DEBUG : d_i_p : start\n");
#endif

386
  /* destroy elements */
387

388
  gst_object_destroy (GST_OBJECT (channel->pipe));
389

390
  free (channel);
391 392
}

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

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

400
}
401