BLOG | DOCUMENTATION | TRAC

Home --> Documentations --> PJMEDIA Reference

Samples: Using Custom Ports (Sine Wave Generator)

This example demonstrate how to create a custom media port (in this case, a sine wave generator) and connect it to the sound device.

This file is pjsip-apps/src/samples/playsine.c

00001 /* $Id: playsine.c 3664 2011-07-19 03:42:28Z nanang $ */
00002 /* 
00003  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
00004  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
00019  */
00020 
00021 
00033 /*
00034  * playsine.c
00035  *
00036  * PURPOSE:
00037  *  Demonstrate how to create and use custom media port which
00038  *  simply feed a sine wav to the sound player.
00039  *
00040  * USAGE:
00041  *  playsine [nchannel]
00042  *
00043  * where:
00044  *  nchannel is 1 for mono (this is the default) or 2 for stereo.
00045  */
00046 
00047 #include <pjmedia.h>
00048 #include <pjlib.h>
00049 
00050 #include <stdlib.h>     /* atoi() */
00051 #include <stdio.h>
00052 #include <math.h>       /* sin()  */
00053 
00054 /* For logging purpose. */
00055 #define THIS_FILE   "playsine.c"
00056 
00057 
00058 /* Util to display the error message for the specified error code  */
00059 static int app_perror( const char *sender, const char *title, 
00060                        pj_status_t status)
00061 {
00062     char errmsg[PJ_ERR_MSG_SIZE];
00063 
00064     PJ_UNUSED_ARG(sender);
00065 
00066     pj_strerror(status, errmsg, sizeof(errmsg));
00067 
00068     printf("%s: %s [code=%d]\n", title, errmsg, status);
00069     return 1;
00070 }
00071 
00072 
00073 /* Struct attached to sine generator */
00074 typedef struct
00075 {
00076     pj_int16_t  *samples;       /* Sine samples.    */
00077 } port_data;
00078 
00079 
00080 /* This callback is called to feed more samples */
00081 static pj_status_t sine_get_frame( pjmedia_port *port, 
00082                                    pjmedia_frame *frame)
00083 {
00084     port_data *sine = port->port_data.pdata;
00085     pj_int16_t *samples = frame->buf;
00086     unsigned i, count, left, right;
00087 
00088     /* Get number of samples */
00089     count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info);
00090 
00091     left = 0;
00092     right = 0;
00093 
00094     for (i=0; i<count; ++i) {
00095         *samples++ = sine->samples[left];
00096         ++left;
00097 
00098         if (PJMEDIA_PIA_CCNT(&port->info) == 2) {
00099             *samples++ = sine->samples[right];
00100             right += 2; /* higher pitch so we can distinguish left and right. */
00101             if (right >= count)
00102                 right = 0;
00103         }
00104     }
00105 
00106     /* Must set frame->type correctly, otherwise the sound device
00107      * will refuse to play.
00108      */
00109     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
00110 
00111     return PJ_SUCCESS;
00112 }
00113 
00114 #ifndef M_PI
00115 #define M_PI  (3.14159265)
00116 #endif
00117 
00118 /*
00119  * Create a media port to generate sine wave samples.
00120  */
00121 static pj_status_t create_sine_port(pj_pool_t *pool,
00122                                     unsigned sampling_rate,
00123                                     unsigned channel_count,
00124                                     pjmedia_port **p_port)
00125 {
00126     pjmedia_port *port;
00127     unsigned i;
00128     unsigned count;
00129     pj_str_t name;
00130     port_data *sine;
00131 
00132     PJ_ASSERT_RETURN(pool && channel_count > 0 && channel_count <= 2, 
00133                      PJ_EINVAL);
00134 
00135     port = pj_pool_zalloc(pool, sizeof(pjmedia_port));
00136     PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM);
00137 
00138     /* Fill in port info. */
00139     name = pj_str("sine generator");
00140     pjmedia_port_info_init(&port->info, &name,
00141                            PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'),
00142                            sampling_rate,
00143                            channel_count,
00144                            16, sampling_rate * 20 / 1000 * channel_count);
00145     
00146     /* Set the function to feed frame */
00147     port->get_frame = &sine_get_frame;
00148 
00149     /* Create sine port data */
00150     port->port_data.pdata = sine = pj_pool_zalloc(pool, sizeof(port_data));
00151 
00152     /* Create samples */
00153     count = PJMEDIA_PIA_SPF(&port->info) / channel_count;
00154     sine->samples = pj_pool_alloc(pool, count * sizeof(pj_int16_t));
00155     PJ_ASSERT_RETURN(sine->samples != NULL, PJ_ENOMEM);
00156 
00157     /* initialise sinusoidal wavetable */
00158     for( i=0; i<count; i++ )
00159     {
00160         sine->samples[i] = (pj_int16_t) (10000.0 * 
00161                 sin(((double)i/(double)count) * M_PI * 8.) );
00162     }
00163 
00164     *p_port = port;
00165 
00166     return PJ_SUCCESS;
00167 }
00168 
00169 
00170 /* Show usage */
00171 static void usage(void)
00172 {
00173     puts("");
00174     puts("Usage: playsine [nchannel]");
00175     puts("");
00176     puts("where");
00177     puts(" nchannel is number of audio channels (1 for mono, or 2 for stereo).");
00178     puts(" Default is 1 (mono).");
00179     puts("");
00180 }
00181 
00182 
00183 /*
00184  * main()
00185  */
00186 int main(int argc, char *argv[])
00187 {
00188     pj_caching_pool cp;
00189     pjmedia_endpt *med_endpt;
00190     pj_pool_t *pool;
00191     pjmedia_port *sine_port;
00192     pjmedia_snd_port *snd_port;
00193     char tmp[10];
00194     int channel_count = 1;
00195     pj_status_t status;
00196 
00197     if (argc == 2) {
00198         channel_count = atoi(argv[1]);
00199         if (channel_count < 1 || channel_count > 2) {
00200             puts("Error: invalid arguments");
00201             usage();
00202             return 1;
00203         }
00204     }
00205 
00206     /* Must init PJLIB first: */
00207     status = pj_init();
00208     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00209 
00210     /* Must create a pool factory before we can allocate any memory. */
00211     pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
00212 
00213     /* 
00214      * Initialize media endpoint.
00215      * This will implicitly initialize PJMEDIA too.
00216      */
00217     status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
00218     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00219 
00220     /* Create memory pool for our sine generator */
00221     pool = pj_pool_create( &cp.factory,     /* pool factory         */
00222                            "wav",           /* pool name.           */
00223                            4000,            /* init size            */
00224                            4000,            /* increment size       */
00225                            NULL             /* callback on error    */
00226                            );
00227 
00228     /* Create a media port to generate sine wave samples. */
00229     status = create_sine_port( pool,        /* memory pool          */
00230                                11025,       /* sampling rate        */
00231                                channel_count,/* # of channels       */
00232                                &sine_port   /* returned port        */
00233                              );
00234     if (status != PJ_SUCCESS) {
00235         app_perror(THIS_FILE, "Unable to create sine port", status);
00236         return 1;
00237     }
00238 
00239     /* Create sound player port. */
00240     status = pjmedia_snd_port_create_player( 
00241                  pool,                              /* pool                 */
00242                  -1,                                /* use default dev.     */
00243                  PJMEDIA_PIA_SRATE(&sine_port->info),/* clock rate.         */
00244                  PJMEDIA_PIA_CCNT(&sine_port->info),/* # of channels.       */
00245                  PJMEDIA_PIA_SPF(&sine_port->info), /* samples per frame.   */
00246                  PJMEDIA_PIA_BITS(&sine_port->info),/* bits per sample.     */
00247                  0,                                 /* options              */
00248                  &snd_port                          /* returned port        */
00249                  );
00250     if (status != PJ_SUCCESS) {
00251         app_perror(THIS_FILE, "Unable to open sound device", status);
00252         return 1;
00253     }
00254 
00255     /* Connect sine generator port to the sound player 
00256      * Stream playing will commence immediately.
00257      */
00258     status = pjmedia_snd_port_connect( snd_port, sine_port);
00259     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00260 
00261 
00262 
00263     /* 
00264      * Audio should be playing in a loop now, using sound device's thread. 
00265      */
00266 
00267 
00268     /* Sleep to allow log messages to flush */
00269     pj_thread_sleep(100);
00270 
00271 
00272     puts("Playing sine wave..");
00273     puts("");
00274     puts("Press <ENTER> to stop playing and quit");
00275 
00276     if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
00277         puts("EOF while reading stdin, will quit now..");
00278     }
00279 
00280     
00281     /* Start deinitialization: */
00282 
00283     /* Disconnect sound port from file port */
00284     status = pjmedia_snd_port_disconnect(snd_port);
00285     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00286 
00287     /* Without this sleep, Windows/DirectSound will repeteadly
00288      * play the last frame during destroy.
00289      */
00290     pj_thread_sleep(100);
00291 
00292     /* Destroy sound device */
00293     status = pjmedia_snd_port_destroy( snd_port );
00294     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00295 
00296 
00297     /* Destroy sine generator */
00298     status = pjmedia_port_destroy( sine_port );
00299     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00300 
00301 
00302     /* Release application pool */
00303     pj_pool_release( pool );
00304 
00305     /* Destroy media endpoint. */
00306     pjmedia_endpt_destroy( med_endpt );
00307 
00308     /* Destroy pool factory */
00309     pj_caching_pool_destroy( &cp );
00310 
00311     /* Shutdown PJLIB */
00312     pj_shutdown();
00313 
00314 
00315     /* Done. */
00316     return 0;
00317 }

 


PJMEDIA small footprint Open Source media stack
Copyright (C) 2006-2008 Teluu Inc.