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
//#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
#ifndef GST_DISABLE_LOADSAVE
186
    xmlSaveFile("mixer.xml", gst_xml_write(GST_ELEMENT(main_bin)));
187
#endif
188

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

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

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

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

  gst_object_destroy(GST_OBJECT(main_bin));

  exit(0);
}

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

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

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

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

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

258 259
  /* create channel */

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

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

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

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

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

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

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

284

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

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

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

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

326 327
  autoplug = gst_autoplugfactory_make ("static");
  g_assert (autoplug != NULL);
328

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

#else

  new_element = gst_bin_new ("autoplug_bin");

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

365
  gst_bin_add (GST_BIN(channel->pipe), channel->volenv);
366 367 368 369 370
  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");
  
371 372
  /* add a ghost pad */
  sprintf (buffer, "channel%d", id);
373 374
  gst_element_add_ghost_pad (channel->pipe,
                             gst_element_get_pad (channel->volenv, "src"), buffer);
375

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

381
  return channel;
382 383 384
}

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

395
  /* destroy elements */
396

397
  gst_object_destroy (GST_OBJECT (channel->pipe));
398

399
  free (channel);
400 401
}

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

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

409
}
410