blob: f409af2b50b44a9a3cb5fc6efa923acc49794795 [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);
Benny Prijono32d267b2009-01-01 22:08:21 +0000132 if (fgets(in, sizeof(in), stdin) == NULL)
133 return 1;
Benny Prijono79a1afe2008-01-12 11:31:39 +0000134 if (pj_tolower(in[0]) != 'y')
135 return 1;
136 }
137
138 /* Scan input file names */
139 for (input_cnt=0 ; pj_optind<argc && input_cnt<MAX_WAV;
140 ++pj_optind, ++input_cnt)
141 {
142 if (!pj_file_exists(argv[pj_optind])) {
143 printf("Error: input file %s doesn't exist\n",
144 argv[pj_optind]);
145 return 1;
146 }
147 wav_input[input_cnt].fname = argv[pj_optind];
148 wav_input[input_cnt].port = NULL;
149 wav_input[input_cnt].slot = 0;
150 }
151
152 if (input_cnt == 0) {
153 puts("Error: no input WAV is specified");
154 return 0;
155 }
156
157 /* Initialialize */
158 CHECK( pj_init() );
159 CHECK( pjlib_util_init() );
160 pj_caching_pool_init(&cp, NULL, 0);
161 CHECK( pjmedia_endpt_create(&cp.factory, NULL, 1, &med_ept) );
162
163 pool = pj_pool_create(&cp.factory, "mix", 1000, 1000, NULL);
164
165 /* Create the bridge */
166 CHECK( pjmedia_conf_create(pool, MAX_WAV+4, clock_rate, 1,
167 clock_rate * PTIME / 1000, 16,
168 PJMEDIA_CONF_NO_DEVICE, &conf) );
169
170 /* Create the WAV output */
171 CHECK( pjmedia_wav_writer_port_create(pool, out_fname, clock_rate, 1,
172 clock_rate * PTIME / 1000,
173 16, 0, 0, &wavout) );
174
175 /* Create and register each WAV input to the bridge */
176 for (i=0; i<input_cnt; ++i) {
177 pj_ssize_t len;
178
179 CHECK( pjmedia_wav_player_port_create(pool, wav_input[i].fname, 20,
180 PJMEDIA_FILE_NO_LOOP, 0,
181 &wav_input[i].port) );
182 len = pjmedia_wav_player_get_len(wav_input[i].port);
Benny Prijono9b65ba52008-01-12 14:16:30 +0000183 len = (pj_ssize_t)(len * 1.0 * clock_rate /
184 wav_input[i].port->info.clock_rate);
Benny Prijonoe9a65302008-01-12 15:32:17 +0000185 if (len > (pj_ssize_t)longest)
Benny Prijono79a1afe2008-01-12 11:31:39 +0000186 longest = len;
187
188 CHECK( pjmedia_conf_add_port(conf, pool, wav_input[i].port,
189 NULL, &wav_input[i].slot));
190
191 CHECK( pjmedia_conf_connect_port(conf, wav_input[i].slot, 0, 0) );
192 }
193
194 /* Loop reading frame from the bridge and write it to WAV */
195 processed = 0;
Benny Prijono9b65ba52008-01-12 14:16:30 +0000196 while (processed < longest + clock_rate * APPEND * 2 / 1000) {
Benny Prijono79a1afe2008-01-12 11:31:39 +0000197 pj_int16_t framebuf[PTIME * 48000 / 1000];
198 pjmedia_port *cp = pjmedia_conf_get_master_port(conf);
199 pjmedia_frame frame;
200
201 frame.buf = framebuf;
202 frame.size = cp->info.samples_per_frame * 2;
203 pj_assert(frame.size <= sizeof(framebuf));
204
Benny Prijono9b65ba52008-01-12 14:16:30 +0000205 CHECK( pjmedia_port_get_frame(cp, &frame) );
Benny Prijono79a1afe2008-01-12 11:31:39 +0000206
Benny Prijonoe9a65302008-01-12 15:32:17 +0000207 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
208 pj_bzero(frame.buf, frame.size);
209 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
210 }
211
Benny Prijono79a1afe2008-01-12 11:31:39 +0000212 CHECK( pjmedia_port_put_frame(wavout, &frame));
213
214 processed += frame.size;
215 }
216
Benny Prijono9b65ba52008-01-12 14:16:30 +0000217 PJ_LOG(3,(THIS_FILE, "Done. Output duration: %d.%03d",
218 (processed >> 2)/clock_rate,
219 ((processed >> 2)*1000/clock_rate) % 1000));
220
Benny Prijono79a1afe2008-01-12 11:31:39 +0000221 /* Shutdown everything */
222 CHECK( pjmedia_port_destroy(wavout) );
223 for (i=0; i<input_cnt; ++i) {
224 CHECK( pjmedia_conf_remove_port(conf, wav_input[i].slot) );
225 CHECK( pjmedia_port_destroy(wav_input[i].port) );
226 }
227
228 CHECK(pjmedia_conf_destroy(conf));
229 CHECK(pjmedia_endpt_destroy(med_ept));
230
231 pj_pool_release(pool);
232 pj_caching_pool_destroy(&cp);
233 pj_shutdown();
234
235 return 0;
236}
237