blob: 6cf93a0fe8f52952066741d236db82bc85012d03 [file] [log] [blame]
Benny Prijono79a1afe2008-01-12 11:31:39 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono79a1afe2008-01-12 11:31:39 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /**
22 * \page page_pjmedia_samples_mix_c Samples: Mixing WAV files
23 *
24 * This file is pjsip-apps/src/samples/mix.c
25 *
26 * \includelineno mix.c
27 */
28
29#include <pjlib.h>
30#include <pjlib-util.h>
31#include <pjmedia.h>
32
33#define THIS_FILE "mix.c"
34
35static const char *desc =
36 " mix\n"
37 "\n"
38 " PURPOSE:\n"
39 " Mix input WAV files and save it to output WAV. Input WAV can have\n"
40 " different clock rate.\n"
41 "\n"
42 "\n"
43 " USAGE:\n"
44 " mix [options] output.wav input1.wav [input2.wav] ...\n"
45 "\n"
46 " arguments:\n"
47 " output.wav Set the output WAV filename.\n"
48 " input1.wav Set the input WAV filename.\n"
49 " input2.wav Set the input WAV filename.\n"
50 "\n"
51 " options:\n"
52 " -c N Set clock rate to N Hz (default 16000)\n"
Benny Prijono9b65ba52008-01-12 14:16:30 +000053 " -f Force write (overwrite output without warning\n"
Benny Prijono79a1afe2008-01-12 11:31:39 +000054;
55
56#define MAX_WAV 16
57#define PTIME 20
Benny Prijono9b65ba52008-01-12 14:16:30 +000058#define APPEND 1000
Benny Prijono79a1afe2008-01-12 11:31:39 +000059
60struct wav_input
61{
62 const char *fname;
63 pjmedia_port *port;
64 unsigned slot;
65};
66
67static int err_ret(const char *title, pj_status_t status)
68{
69 char errmsg[PJ_ERR_MSG_SIZE];
70 pj_strerror(status, errmsg, sizeof(errmsg));
71 PJ_LOG(3,(THIS_FILE, "%s error: %s", title, errmsg));
72 return 1;
73}
74
Benny Prijono35f5ce92008-07-04 23:49:44 +000075static void usage(void)
76{
77 puts(desc);
78}
79
Benny Prijono79a1afe2008-01-12 11:31:39 +000080int main(int argc, char *argv[])
81{
82 pj_caching_pool cp;
83 pj_pool_t *pool;
84 pjmedia_endpt *med_ept;
85 unsigned clock_rate = 16000;
Benny Prijono9b65ba52008-01-12 14:16:30 +000086 int c, force=0;
Benny Prijono79a1afe2008-01-12 11:31:39 +000087 const char *out_fname;
88 pjmedia_conf *conf;
89 pjmedia_port *wavout;
90 struct wav_input wav_input[MAX_WAV];
Benny Prijonoe9a65302008-01-12 15:32:17 +000091 pj_size_t longest = 0, processed;
Benny Prijono79a1afe2008-01-12 11:31:39 +000092 unsigned i, input_cnt = 0;
93 pj_status_t status;
94
95#define CHECK(op) do { \
96 status = op; \
97 if (status != PJ_SUCCESS) \
98 return err_ret(#op, status); \
99 } while (0)
100
101
102 /* Parse arguments */
Benny Prijono9b65ba52008-01-12 14:16:30 +0000103 while ((c=pj_getopt(argc, argv, "c:f")) != -1) {
Benny Prijono79a1afe2008-01-12 11:31:39 +0000104 switch (c) {
105 case 'c':
106 clock_rate = atoi(pj_optarg);
107 if (clock_rate < 1000) {
108 puts("Error: invalid clock rate");
Benny Prijono35f5ce92008-07-04 23:49:44 +0000109 usage();
Benny Prijono79a1afe2008-01-12 11:31:39 +0000110 return -1;
111 }
112 break;
Benny Prijono9b65ba52008-01-12 14:16:30 +0000113 case 'f':
114 force = 1;
115 break;
Benny Prijono79a1afe2008-01-12 11:31:39 +0000116 }
117 }
118
119 /* Get output WAV name */
120 if (pj_optind == argc) {
121 puts("Error: no WAV output is specified");
Benny Prijono35f5ce92008-07-04 23:49:44 +0000122 usage();
Benny Prijono79a1afe2008-01-12 11:31:39 +0000123 return 1;
124 }
125
126 out_fname = argv[pj_optind++];
Benny Prijono9b65ba52008-01-12 14:16:30 +0000127 if (force==0 && pj_file_exists(out_fname)) {
Benny Prijono79a1afe2008-01-12 11:31:39 +0000128 char in[8];
129
130 printf("File %s exists, overwrite? [Y/N] ", out_fname);
131 fflush(stdout);
132 fgets(in, sizeof(in), stdin);
133 if (pj_tolower(in[0]) != 'y')
134 return 1;
135 }
136
137 /* Scan input file names */
138 for (input_cnt=0 ; pj_optind<argc && input_cnt<MAX_WAV;
139 ++pj_optind, ++input_cnt)
140 {
141 if (!pj_file_exists(argv[pj_optind])) {
142 printf("Error: input file %s doesn't exist\n",
143 argv[pj_optind]);
144 return 1;
145 }
146 wav_input[input_cnt].fname = argv[pj_optind];
147 wav_input[input_cnt].port = NULL;
148 wav_input[input_cnt].slot = 0;
149 }
150
151 if (input_cnt == 0) {
152 puts("Error: no input WAV is specified");
153 return 0;
154 }
155
156 /* Initialialize */
157 CHECK( pj_init() );
158 CHECK( pjlib_util_init() );
159 pj_caching_pool_init(&cp, NULL, 0);
160 CHECK( pjmedia_endpt_create(&cp.factory, NULL, 1, &med_ept) );
161
162 pool = pj_pool_create(&cp.factory, "mix", 1000, 1000, NULL);
163
164 /* Create the bridge */
165 CHECK( pjmedia_conf_create(pool, MAX_WAV+4, clock_rate, 1,
166 clock_rate * PTIME / 1000, 16,
167 PJMEDIA_CONF_NO_DEVICE, &conf) );
168
169 /* Create the WAV output */
170 CHECK( pjmedia_wav_writer_port_create(pool, out_fname, clock_rate, 1,
171 clock_rate * PTIME / 1000,
172 16, 0, 0, &wavout) );
173
174 /* Create and register each WAV input to the bridge */
175 for (i=0; i<input_cnt; ++i) {
176 pj_ssize_t len;
177
178 CHECK( pjmedia_wav_player_port_create(pool, wav_input[i].fname, 20,
179 PJMEDIA_FILE_NO_LOOP, 0,
180 &wav_input[i].port) );
181 len = pjmedia_wav_player_get_len(wav_input[i].port);
Benny Prijono9b65ba52008-01-12 14:16:30 +0000182 len = (pj_ssize_t)(len * 1.0 * clock_rate /
183 wav_input[i].port->info.clock_rate);
Benny Prijonoe9a65302008-01-12 15:32:17 +0000184 if (len > (pj_ssize_t)longest)
Benny Prijono79a1afe2008-01-12 11:31:39 +0000185 longest = len;
186
187 CHECK( pjmedia_conf_add_port(conf, pool, wav_input[i].port,
188 NULL, &wav_input[i].slot));
189
190 CHECK( pjmedia_conf_connect_port(conf, wav_input[i].slot, 0, 0) );
191 }
192
193 /* Loop reading frame from the bridge and write it to WAV */
194 processed = 0;
Benny Prijono9b65ba52008-01-12 14:16:30 +0000195 while (processed < longest + clock_rate * APPEND * 2 / 1000) {
Benny Prijono79a1afe2008-01-12 11:31:39 +0000196 pj_int16_t framebuf[PTIME * 48000 / 1000];
197 pjmedia_port *cp = pjmedia_conf_get_master_port(conf);
198 pjmedia_frame frame;
199
200 frame.buf = framebuf;
201 frame.size = cp->info.samples_per_frame * 2;
202 pj_assert(frame.size <= sizeof(framebuf));
203
Benny Prijono9b65ba52008-01-12 14:16:30 +0000204 CHECK( pjmedia_port_get_frame(cp, &frame) );
Benny Prijono79a1afe2008-01-12 11:31:39 +0000205
Benny Prijonoe9a65302008-01-12 15:32:17 +0000206 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
207 pj_bzero(frame.buf, frame.size);
208 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
209 }
210
Benny Prijono79a1afe2008-01-12 11:31:39 +0000211 CHECK( pjmedia_port_put_frame(wavout, &frame));
212
213 processed += frame.size;
214 }
215
Benny Prijono9b65ba52008-01-12 14:16:30 +0000216 PJ_LOG(3,(THIS_FILE, "Done. Output duration: %d.%03d",
217 (processed >> 2)/clock_rate,
218 ((processed >> 2)*1000/clock_rate) % 1000));
219
Benny Prijono79a1afe2008-01-12 11:31:39 +0000220 /* Shutdown everything */
221 CHECK( pjmedia_port_destroy(wavout) );
222 for (i=0; i<input_cnt; ++i) {
223 CHECK( pjmedia_conf_remove_port(conf, wav_input[i].slot) );
224 CHECK( pjmedia_port_destroy(wav_input[i].port) );
225 }
226
227 CHECK(pjmedia_conf_destroy(conf));
228 CHECK(pjmedia_endpt_destroy(med_ept));
229
230 pj_pool_release(pool);
231 pj_caching_pool_destroy(&cp);
232 pj_shutdown();
233
234 return 0;
235}
236