blob: 1040d8fd19f2f0a5b0a5426dc159f21a9b57d0ee [file] [log] [blame]
Benny Prijonocbc1c472006-03-19 00:50:23 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
Benny Prijonocbc1c472006-03-19 00:50:23 +000020/*
21 * playsine.c
22 *
23 * PURPOSE:
24 * Demonstrate how to create and use custom media port which
25 * simply feed a sine wav to the sound player.
26 *
27 * USAGE:
28 * playsine [nchannel]
29 *
30 * where:
31 * nchannel is 1 for mono (this is the default) or 2 for stereo.
32 */
33
34#include <pjmedia.h>
35#include <pjlib.h>
36
37#include <stdlib.h> /* atoi() */
38#include <stdio.h>
39
40
41/* For logging purpose. */
42#define THIS_FILE "playsine.c"
43
44
45/* Util to display the error message for the specified error code */
46static int app_perror( const char *sender, const char *title,
47 pj_status_t status)
48{
49 char errmsg[PJ_ERR_MSG_SIZE];
50
51 pj_strerror(status, errmsg, sizeof(errmsg));
52
53 printf("%s: %s [code=%d]\n", title, errmsg, status);
54 return 1;
55}
56
57
58/* Struct attached to sine generator */
59typedef struct
60{
61 pj_int16_t *samples; /* Sine samples. */
62} port_data;
63
64
65/* This callback is called to feed more samples */
66static pj_status_t sine_get_frame( pjmedia_port *port,
67 pjmedia_frame *frame)
68{
69 port_data *sine = port->user_data;
70 pj_int16_t *samples = frame->buf;
71 unsigned i, count, left, right;
72
73 /* Get number of samples */
74 count = frame->size / 2 / port->info.channel_count;
75
76 left = 0;
77 right = 0;
78
79 for (i=0; i<count; ++i) {
80 *samples++ = sine->samples[left];
81 ++left;
82
83 if (port->info.channel_count == 2) {
84 *samples++ = sine->samples[right];
85 right += 2; /* higher pitch so we can distinguish left and right. */
86 if (right >= count)
87 right = 0;
88 }
89 }
90
91 /* Must set frame->type correctly, otherwise the sound device
92 * will refuse to play.
93 */
94 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
95
96 return PJ_SUCCESS;
97}
98
99#ifndef M_PI
100#define M_PI (3.14159265)
101#endif
102
103/*
104 * Create a media port to generate sine wave samples.
105 */
106static pj_status_t create_sine_port(pj_pool_t *pool,
107 unsigned sampling_rate,
108 unsigned channel_count,
109 pjmedia_port **p_port)
110{
111 pjmedia_port *port;
112 unsigned i;
113 unsigned count;
114 port_data *sine;
115
116 PJ_ASSERT_RETURN(pool && channel_count > 0 && channel_count <= 2,
117 PJ_EINVAL);
118
119 port = pj_pool_zalloc(pool, sizeof(pjmedia_port));
120 PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM);
121
122 /* Fill in port info. */
123 port->info.bits_per_sample = 16;
124 port->info.channel_count = channel_count;
125 port->info.encoding_name = pj_str("pcm");
126 port->info.has_info = 1;
127 port->info.name = pj_str("sine generator");
128 port->info.need_info = 0;
129 port->info.pt = 0xFF;
130 port->info.sample_rate = sampling_rate;
131 port->info.samples_per_frame = sampling_rate * 20 / 1000 * channel_count;
132 port->info.bytes_per_frame = port->info.samples_per_frame * 2;
133 port->info.type = PJMEDIA_TYPE_AUDIO;
134
135 /* Set the function to feed frame */
136 port->get_frame = &sine_get_frame;
137
138 /* Create sine port data */
139 port->user_data = sine = pj_pool_zalloc(pool, sizeof(port_data));
140
141 /* Create samples */
142 count = port->info.samples_per_frame / channel_count;
143 sine->samples = pj_pool_alloc(pool, count * sizeof(pj_int16_t));
144 PJ_ASSERT_RETURN(sine->samples != NULL, PJ_ENOMEM);
145
146 /* initialise sinusoidal wavetable */
147 for( i=0; i<count; i++ )
148 {
149 sine->samples[i] = (pj_int16_t) (10000.0 *
150 sin(((double)i/(double)count) * M_PI * 8.) );
151 }
152
153 *p_port = port;
154
155 return PJ_SUCCESS;
156}
157
158
159/* Show usage */
160static void usage(void)
161{
162 puts("");
163 puts("Usage: playsine [nchannel]");
164 puts("");
165 puts("where");
166 puts(" nchannel is number of audio channels (1 for mono, or 2 for stereo).");
167 puts(" Default is 1 (mono).");
168 puts("");
169}
170
171
172/*
173 * main()
174 */
175int main(int argc, char *argv[])
176{
177 pj_caching_pool cp;
178 pjmedia_endpt *med_endpt;
179 pj_pool_t *pool;
180 pjmedia_port *sine_port;
181 pjmedia_snd_port *snd_port;
182 char tmp[10];
183 int channel_count = 1;
184 pj_status_t status;
185
186 if (argc == 2) {
187 channel_count = atoi(argv[1]);
188 if (channel_count < 1 || channel_count > 2) {
189 puts("Error: invalid arguments");
190 usage();
191 return 1;
192 }
193 }
194
195 /* Must init PJLIB first: */
196 status = pj_init();
197 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
198
199 /* Must create a pool factory before we can allocate any memory. */
200 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
201
202 /*
203 * Initialize media endpoint.
204 * This will implicitly initialize PJMEDIA too.
205 */
206 status = pjmedia_endpt_create(&cp.factory, &med_endpt);
207 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
208
209 /* Create memory pool for our sine generator */
210 pool = pj_pool_create( &cp.factory, /* pool factory */
211 "wav", /* pool name. */
212 4000, /* init size */
213 4000, /* increment size */
214 NULL /* callback on error */
215 );
216
217 /* Create a media port to generate sine wave samples. */
218 status = create_sine_port( pool, /* memory pool */
219 11025, /* sampling rate */
220 channel_count,/* # of channels */
221 &sine_port /* returned port */
222 );
223 if (status != PJ_SUCCESS) {
224 app_perror(THIS_FILE, "Unable to create sine port", status);
225 return 1;
226 }
227
228 /* Create sound player port. */
229 status = pjmedia_snd_port_create_player(
230 pool, /* pool */
231 -1, /* use default dev. */
232 sine_port->info.sample_rate, /* clock rate. */
233 sine_port->info.channel_count, /* # of channels. */
234 sine_port->info.samples_per_frame, /* samples per frame. */
235 sine_port->info.bits_per_sample, /* bits per sample. */
236 0, /* options */
237 &snd_port /* returned port */
238 );
239 if (status != PJ_SUCCESS) {
240 app_perror(THIS_FILE, "Unable to open sound device", status);
241 return 1;
242 }
243
244 /* Connect sine generator port to the sound player
245 * Stream playing will commence immediately.
246 */
247 status = pjmedia_snd_port_connect( snd_port, sine_port);
248 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
249
250
251
252 /*
253 * Audio should be playing in a loop now, using sound device's thread.
254 */
255
256
257 /* Sleep to allow log messages to flush */
258 pj_thread_sleep(100);
259
260
261 puts("Playing sine wave..");
262 puts("");
263 puts("Press <ENTER> to stop playing and quit");
264
265 fgets(tmp, sizeof(tmp), stdin);
266
267
268 /* Start deinitialization: */
269
270 /* Destroy sound device */
271 status = pjmedia_snd_port_destroy( snd_port );
272 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
273
274
275 /* Destroy sine generator */
276 status = pjmedia_port_destroy( sine_port );
277 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
278
279
280 /* Release application pool */
281 pj_pool_release( pool );
282
283 /* Destroy media endpoint. */
284 pjmedia_endpt_destroy( med_endpt );
285
286 /* Destroy pool factory */
287 pj_caching_pool_destroy( &cp );
288
289
290 /* Done. */
291 return 0;
292}