blob: f10ab487e4b461e937b19e7e19269fe0860a439a [file] [log] [blame]
Benny Prijono79a1afe2008-01-12 11:31:39 +00001/* $Id$ */
2/*
Benny Prijono32177c02008-06-20 22:44:47 +00003 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono79a1afe2008-01-12 11:31:39 +00004 *
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
20 /**
21 * \page page_pjmedia_samples_mix_c Samples: Mixing WAV files
22 *
23 * This file is pjsip-apps/src/samples/mix.c
24 *
25 * \includelineno mix.c
26 */
27
28#include <pjlib.h>
29#include <pjlib-util.h>
30#include <pjmedia.h>
31
32#define THIS_FILE "mix.c"
33
34static const char *desc =
35 " mix\n"
36 "\n"
37 " PURPOSE:\n"
38 " Mix input WAV files and save it to output WAV. Input WAV can have\n"
39 " different clock rate.\n"
40 "\n"
41 "\n"
42 " USAGE:\n"
43 " mix [options] output.wav input1.wav [input2.wav] ...\n"
44 "\n"
45 " arguments:\n"
46 " output.wav Set the output WAV filename.\n"
47 " input1.wav Set the input WAV filename.\n"
48 " input2.wav Set the input WAV filename.\n"
49 "\n"
50 " options:\n"
51 " -c N Set clock rate to N Hz (default 16000)\n"
Benny Prijono9b65ba52008-01-12 14:16:30 +000052 " -f Force write (overwrite output without warning\n"
Benny Prijono79a1afe2008-01-12 11:31:39 +000053;
54
55#define MAX_WAV 16
56#define PTIME 20
Benny Prijono9b65ba52008-01-12 14:16:30 +000057#define APPEND 1000
Benny Prijono79a1afe2008-01-12 11:31:39 +000058
59struct wav_input
60{
61 const char *fname;
62 pjmedia_port *port;
63 unsigned slot;
64};
65
66static int err_ret(const char *title, pj_status_t status)
67{
68 char errmsg[PJ_ERR_MSG_SIZE];
69 pj_strerror(status, errmsg, sizeof(errmsg));
70 PJ_LOG(3,(THIS_FILE, "%s error: %s", title, errmsg));
71 return 1;
72}
73
Benny Prijono35f5ce92008-07-04 23:49:44 +000074static void usage(void)
75{
76 puts(desc);
77}
78
Benny Prijono79a1afe2008-01-12 11:31:39 +000079int main(int argc, char *argv[])
80{
81 pj_caching_pool cp;
82 pj_pool_t *pool;
83 pjmedia_endpt *med_ept;
84 unsigned clock_rate = 16000;
Benny Prijono9b65ba52008-01-12 14:16:30 +000085 int c, force=0;
Benny Prijono79a1afe2008-01-12 11:31:39 +000086 const char *out_fname;
87 pjmedia_conf *conf;
88 pjmedia_port *wavout;
89 struct wav_input wav_input[MAX_WAV];
Benny Prijonoe9a65302008-01-12 15:32:17 +000090 pj_size_t longest = 0, processed;
Benny Prijono79a1afe2008-01-12 11:31:39 +000091 unsigned i, input_cnt = 0;
92 pj_status_t status;
93
94#define CHECK(op) do { \
95 status = op; \
96 if (status != PJ_SUCCESS) \
97 return err_ret(#op, status); \
98 } while (0)
99
100
101 /* Parse arguments */
Benny Prijono9b65ba52008-01-12 14:16:30 +0000102 while ((c=pj_getopt(argc, argv, "c:f")) != -1) {
Benny Prijono79a1afe2008-01-12 11:31:39 +0000103 switch (c) {
104 case 'c':
105 clock_rate = atoi(pj_optarg);
106 if (clock_rate < 1000) {
107 puts("Error: invalid clock rate");
Benny Prijono35f5ce92008-07-04 23:49:44 +0000108 usage();
Benny Prijono79a1afe2008-01-12 11:31:39 +0000109 return -1;
110 }
111 break;
Benny Prijono9b65ba52008-01-12 14:16:30 +0000112 case 'f':
113 force = 1;
114 break;
Benny Prijono79a1afe2008-01-12 11:31:39 +0000115 }
116 }
117
118 /* Get output WAV name */
119 if (pj_optind == argc) {
120 puts("Error: no WAV output is specified");
Benny Prijono35f5ce92008-07-04 23:49:44 +0000121 usage();
Benny Prijono79a1afe2008-01-12 11:31:39 +0000122 return 1;
123 }
124
125 out_fname = argv[pj_optind++];
Benny Prijono9b65ba52008-01-12 14:16:30 +0000126 if (force==0 && pj_file_exists(out_fname)) {
Benny Prijono79a1afe2008-01-12 11:31:39 +0000127 char in[8];
128
129 printf("File %s exists, overwrite? [Y/N] ", out_fname);
130 fflush(stdout);
131 fgets(in, sizeof(in), stdin);
132 if (pj_tolower(in[0]) != 'y')
133 return 1;
134 }
135
136 /* Scan input file names */
137 for (input_cnt=0 ; pj_optind<argc && input_cnt<MAX_WAV;
138 ++pj_optind, ++input_cnt)
139 {
140 if (!pj_file_exists(argv[pj_optind])) {
141 printf("Error: input file %s doesn't exist\n",
142 argv[pj_optind]);
143 return 1;
144 }
145 wav_input[input_cnt].fname = argv[pj_optind];
146 wav_input[input_cnt].port = NULL;
147 wav_input[input_cnt].slot = 0;
148 }
149
150 if (input_cnt == 0) {
151 puts("Error: no input WAV is specified");
152 return 0;
153 }
154
155 /* Initialialize */
156 CHECK( pj_init() );
157 CHECK( pjlib_util_init() );
158 pj_caching_pool_init(&cp, NULL, 0);
159 CHECK( pjmedia_endpt_create(&cp.factory, NULL, 1, &med_ept) );
160
161 pool = pj_pool_create(&cp.factory, "mix", 1000, 1000, NULL);
162
163 /* Create the bridge */
164 CHECK( pjmedia_conf_create(pool, MAX_WAV+4, clock_rate, 1,
165 clock_rate * PTIME / 1000, 16,
166 PJMEDIA_CONF_NO_DEVICE, &conf) );
167
168 /* Create the WAV output */
169 CHECK( pjmedia_wav_writer_port_create(pool, out_fname, clock_rate, 1,
170 clock_rate * PTIME / 1000,
171 16, 0, 0, &wavout) );
172
173 /* Create and register each WAV input to the bridge */
174 for (i=0; i<input_cnt; ++i) {
175 pj_ssize_t len;
176
177 CHECK( pjmedia_wav_player_port_create(pool, wav_input[i].fname, 20,
178 PJMEDIA_FILE_NO_LOOP, 0,
179 &wav_input[i].port) );
180 len = pjmedia_wav_player_get_len(wav_input[i].port);
Benny Prijono9b65ba52008-01-12 14:16:30 +0000181 len = (pj_ssize_t)(len * 1.0 * clock_rate /
182 wav_input[i].port->info.clock_rate);
Benny Prijonoe9a65302008-01-12 15:32:17 +0000183 if (len > (pj_ssize_t)longest)
Benny Prijono79a1afe2008-01-12 11:31:39 +0000184 longest = len;
185
186 CHECK( pjmedia_conf_add_port(conf, pool, wav_input[i].port,
187 NULL, &wav_input[i].slot));
188
189 CHECK( pjmedia_conf_connect_port(conf, wav_input[i].slot, 0, 0) );
190 }
191
192 /* Loop reading frame from the bridge and write it to WAV */
193 processed = 0;
Benny Prijono9b65ba52008-01-12 14:16:30 +0000194 while (processed < longest + clock_rate * APPEND * 2 / 1000) {
Benny Prijono79a1afe2008-01-12 11:31:39 +0000195 pj_int16_t framebuf[PTIME * 48000 / 1000];
196 pjmedia_port *cp = pjmedia_conf_get_master_port(conf);
197 pjmedia_frame frame;
198
199 frame.buf = framebuf;
200 frame.size = cp->info.samples_per_frame * 2;
201 pj_assert(frame.size <= sizeof(framebuf));
202
Benny Prijono9b65ba52008-01-12 14:16:30 +0000203 CHECK( pjmedia_port_get_frame(cp, &frame) );
Benny Prijono79a1afe2008-01-12 11:31:39 +0000204
Benny Prijonoe9a65302008-01-12 15:32:17 +0000205 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
206 pj_bzero(frame.buf, frame.size);
207 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
208 }
209
Benny Prijono79a1afe2008-01-12 11:31:39 +0000210 CHECK( pjmedia_port_put_frame(wavout, &frame));
211
212 processed += frame.size;
213 }
214
Benny Prijono9b65ba52008-01-12 14:16:30 +0000215 PJ_LOG(3,(THIS_FILE, "Done. Output duration: %d.%03d",
216 (processed >> 2)/clock_rate,
217 ((processed >> 2)*1000/clock_rate) % 1000));
218
Benny Prijono79a1afe2008-01-12 11:31:39 +0000219 /* Shutdown everything */
220 CHECK( pjmedia_port_destroy(wavout) );
221 for (i=0; i<input_cnt; ++i) {
222 CHECK( pjmedia_conf_remove_port(conf, wav_input[i].slot) );
223 CHECK( pjmedia_port_destroy(wav_input[i].port) );
224 }
225
226 CHECK(pjmedia_conf_destroy(conf));
227 CHECK(pjmedia_endpt_destroy(med_ept));
228
229 pj_pool_release(pool);
230 pj_caching_pool_destroy(&cp);
231 pj_shutdown();
232
233 return 0;
234}
235