mixer.c 9.29 KB
Newer Older
1 2
/*
 * mixer.c - stereo audio mixer - thomas@apestaart.org
3
 * example based on helloworld
4 5 6 7
 * demonstrates the adder plugin and the volume envelope plugin 
 * work in progress but do try it out 
 * 
 * Latest change : 	16/04/2001
8 9 10
 * 					multiple input channels allowed
 * 					volume envelope adapted 
 * Version :		0.3
11 12
 */

Wim Taymans's avatar
Wim Taymans committed
13 14
#include <stdlib.h>
#include <gst/gst.h>
15
#include "mixer.h"
16
#include <unistd.h>
17

18
//#define DEBUG
19 20 21

/* function prototypes */

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

Wim Taymans's avatar
Wim Taymans committed
26 27 28 29 30 31 32

gboolean playing;


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

35
//  playing = FALSE;
Wim Taymans's avatar
Wim Taymans committed
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
static void
gst_play_have_type (GstElement *sink, GstElement *sink2, gpointer data)
{
  GST_DEBUG (0,"GstPipeline: play have type %p\n", (gboolean *)data);
 
  *(gboolean *)data = TRUE;
}

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

  GST_DEBUG (0,"GstPipeline: typefind for element \"%s\" %p\n",
             GST_ELEMENT_NAME(element), &found);
 
  typefind = gst_elementfactory_make ("typefind", "typefind");
  g_return_val_if_fail (typefind != NULL, FALSE);

  gtk_signal_connect (GTK_OBJECT (typefind), "have_type",  
                      GTK_SIGNAL_FUNC (gst_play_have_type), &found);
 
  gst_pad_connect (gst_element_get_pad (element, "src"),
                   gst_element_get_pad (typefind, "sink"));
  gst_bin_add (bin, typefind);
  
  gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING);
  
  // push a buffer... the have_type signal handler will set the found flag
  gst_bin_iterate (bin);
  
  gst_element_set_state (GST_ELEMENT (bin), 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_object_unref (GST_OBJECT (typefind));
                   
  return caps;
}

Wim Taymans's avatar
Wim Taymans committed
83 84
int main(int argc,char *argv[]) 
{
85 86 87 88 89 90 91 92
  int i;
  int num_channels;
  
  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 111 112 113 114 115
  /* create adder */
  adder = gst_elementfactory_make("adder", "adderel");

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

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

119 120
  /* connect adder and output to bin */

Wim Taymans's avatar
Wim Taymans committed
121 122 123 124 125 126 127
  gst_bin_add(GST_BIN(main_bin), adder);
  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"));
128 129
  
  /* create input channels, add to bin and connect */
Wim Taymans's avatar
Wim Taymans committed
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
  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);  
    gst_bin_add(GST_BIN(main_bin), channel_in->pipe);

    /* request pads and connect to adder */
    pad = gst_element_request_pad_by_name (adder, "sink%d");
    g_print ("\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 */
  }
182

183
  /* sleep a few seconds doesn't seem to help anyway */
Wim Taymans's avatar
Wim Taymans committed
184

185 186 187
  printf ("Sleeping a few seconds ...\n");
  sleep (2);
  printf ("Waking up ...\n");
188

189
  
Wim Taymans's avatar
Wim Taymans committed
190 191 192 193 194 195 196 197 198 199 200 201
  /* start playing */
  gst_element_set_state(main_bin, GST_STATE_PLAYING);

  playing = TRUE;

  while (playing) {
    gst_bin_iterate(GST_BIN(main_bin));
  }

  /* stop the bin */
  gst_element_set_state(main_bin, GST_STATE_NULL);

202 203 204 205 206 207
  while (input_channels)
  {
    destroy_input_channel (input_channels->data);
    input_channels = g_list_next (input_channels);
  }
  g_list_free (input_channels);
208
  
Wim Taymans's avatar
Wim Taymans committed
209 210 211 212 213 214 215
  gst_object_destroy(GST_OBJECT(audiosink));

  gst_object_destroy(GST_OBJECT(main_bin));

  exit(0);
}

216 217
input_channel_t*
create_input_channel (int id, char* location)
218
{
219 220
  /* create an input channel, reading from location
   * return a pointer to the channel
221 222 223
   * return NULL if failed
   */

224 225
  input_channel_t *channel;
  
226 227
  char buffer[20]; 		/* hold the names */

228 229 230
  GstAutoplug *autoplug;
  GstCaps *srccaps;
  GstElement *new_element;  
231 232

#ifdef DEBUG
233
  printf ("DEBUG : c_i_p : creating channel with id %d for file %s\n",
234 235 236
  		  id, location);
#endif
  
237
  /* allocate channel */
238

239 240
  channel = (input_channel_t *) malloc (sizeof (input_channel_t));
  if (channel == NULL)
241
  {
242
    printf ("create_input_channel : could not allocate memory for channel !\n");
243 244 245
    return NULL;
  }

246 247 248 249 250
  /* create channel */

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

252 253 254
  channel->pipe = gst_bin_new ("pipeline");
  g_assert(channel->pipe != NULL);    
    
255 256
  /* create elements */

257 258 259 260
#ifdef DEBUG
  printf ("DEBUG : c_i_p : creating disksrc\n");
#endif

261
  sprintf (buffer, "disksrc%d", id);
262 263 264 265 266 267 268 269
  channel->disksrc = gst_elementfactory_make ("disksrc", buffer);
  g_assert(channel->disksrc != NULL);    
  
  gtk_object_set(GTK_OBJECT(channel->disksrc),"location", location, NULL);

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

270 271
  /* connect signal to eos of disksrc */
  gtk_signal_connect(GTK_OBJECT(channel->disksrc),"eos",
272
                     GTK_SIGNAL_FUNC(eos),NULL);
273

274

275 276 277 278 279 280 281 282 283 284 285 286 287
#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
288

289 290 291 292 293 294 295 296 297 298
  srccaps = gst_play_typefind (GST_BIN (channel->pipe), channel->disksrc);

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

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

300 301
  autoplug = gst_autoplugfactory_make ("static");
  g_assert (autoplug != NULL);
302

303 304 305 306 307 308 309 310 311 312 313 314
#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);
  }
  
315
  gst_bin_add (GST_BIN(channel->pipe), channel->volenv);
316 317 318 319 320
  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");
  
321 322
  /* add a ghost pad */
  sprintf (buffer, "channel%d", id);
323 324
  gst_element_add_ghost_pad (channel->pipe,
                             gst_element_get_pad (channel->volenv, "src"), buffer);
325

326
   
327 328 329 330
#ifdef DEBUG
  printf ("DEBUG : c_i_p : end function\n");
#endif

331
  return channel;
332 333 334
}

void
335
destroy_input_channel (input_channel_t *channel)
336 337
{
  /* 
338
   * destroy an input channel
339 340 341 342 343 344
   */
   
#ifdef DEBUG
  printf ("DEBUG : d_i_p : start\n");
#endif

345
  /* destroy elements */
346

347
  gst_object_destroy (GST_OBJECT (channel->pipe));
348

349
  free (channel);
350 351
}

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

356 357
  sprintf (buffer, "%f:%f", cp_time, cp_level);
  gtk_object_set(GTK_OBJECT(volenv), "controlpoint", buffer, NULL);
358

359
}
360