00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00029 #include <pjlib.h>
00030 #include <pjlib-util.h>
00031 #include <pjmedia.h>
00032
00033 #define THIS_FILE "mix.c"
00034
00035 static const char *desc =
00036 " mix\n"
00037 "\n"
00038 " PURPOSE:\n"
00039 " Mix input WAV files and save it to output WAV. Input WAV can have\n"
00040 " different clock rate.\n"
00041 "\n"
00042 "\n"
00043 " USAGE:\n"
00044 " mix [options] output.wav input1.wav [input2.wav] ...\n"
00045 "\n"
00046 " arguments:\n"
00047 " output.wav Set the output WAV filename.\n"
00048 " input1.wav Set the input WAV filename.\n"
00049 " input2.wav Set the input WAV filename.\n"
00050 "\n"
00051 " options:\n"
00052 " -c N Set clock rate to N Hz (default 16000)\n"
00053 " -f Force write (overwrite output without warning\n"
00054 ;
00055
00056 #define MAX_WAV 16
00057 #define PTIME 20
00058 #define APPEND 1000
00059
00060 struct wav_input
00061 {
00062 const char *fname;
00063 pjmedia_port *port;
00064 unsigned slot;
00065 };
00066
00067 static int err_ret(const char *title, pj_status_t status)
00068 {
00069 char errmsg[PJ_ERR_MSG_SIZE];
00070 pj_strerror(status, errmsg, sizeof(errmsg));
00071 PJ_LOG(3,(THIS_FILE, "%s error: %s", title, errmsg));
00072 return 1;
00073 }
00074
00075 static void usage(void)
00076 {
00077 puts(desc);
00078 }
00079
00080 int main(int argc, char *argv[])
00081 {
00082 pj_caching_pool cp;
00083 pj_pool_t *pool;
00084 pjmedia_endpt *med_ept;
00085 unsigned clock_rate = 16000;
00086 int c, force=0;
00087 const char *out_fname;
00088 pjmedia_conf *conf;
00089 pjmedia_port *wavout;
00090 struct wav_input wav_input[MAX_WAV];
00091 pj_size_t longest = 0, processed;
00092 unsigned i, input_cnt = 0;
00093 pj_status_t status;
00094
00095 #define CHECK(op) do { \
00096 status = op; \
00097 if (status != PJ_SUCCESS) \
00098 return err_ret(#op, status); \
00099 } while (0)
00100
00101
00102
00103 while ((c=pj_getopt(argc, argv, "c:f")) != -1) {
00104 switch (c) {
00105 case 'c':
00106 clock_rate = atoi(pj_optarg);
00107 if (clock_rate < 1000) {
00108 puts("Error: invalid clock rate");
00109 usage();
00110 return -1;
00111 }
00112 break;
00113 case 'f':
00114 force = 1;
00115 break;
00116 }
00117 }
00118
00119
00120 if (pj_optind == argc) {
00121 puts("Error: no WAV output is specified");
00122 usage();
00123 return 1;
00124 }
00125
00126 out_fname = argv[pj_optind++];
00127 if (force==0 && pj_file_exists(out_fname)) {
00128 char in[8];
00129
00130 printf("File %s exists, overwrite? [Y/N] ", out_fname);
00131 fflush(stdout);
00132 if (fgets(in, sizeof(in), stdin) == NULL)
00133 return 1;
00134 if (pj_tolower(in[0]) != 'y')
00135 return 1;
00136 }
00137
00138
00139 for (input_cnt=0 ; pj_optind<argc && input_cnt<MAX_WAV;
00140 ++pj_optind, ++input_cnt)
00141 {
00142 if (!pj_file_exists(argv[pj_optind])) {
00143 printf("Error: input file %s doesn't exist\n",
00144 argv[pj_optind]);
00145 return 1;
00146 }
00147 wav_input[input_cnt].fname = argv[pj_optind];
00148 wav_input[input_cnt].port = NULL;
00149 wav_input[input_cnt].slot = 0;
00150 }
00151
00152 if (input_cnt == 0) {
00153 puts("Error: no input WAV is specified");
00154 return 0;
00155 }
00156
00157
00158 CHECK( pj_init() );
00159 CHECK( pjlib_util_init() );
00160 pj_caching_pool_init(&cp, NULL, 0);
00161 CHECK( pjmedia_endpt_create(&cp.factory, NULL, 1, &med_ept) );
00162
00163 pool = pj_pool_create(&cp.factory, "mix", 1000, 1000, NULL);
00164
00165
00166 CHECK( pjmedia_conf_create(pool, MAX_WAV+4, clock_rate, 1,
00167 clock_rate * PTIME / 1000, 16,
00168 PJMEDIA_CONF_NO_DEVICE, &conf) );
00169
00170
00171 CHECK( pjmedia_wav_writer_port_create(pool, out_fname, clock_rate, 1,
00172 clock_rate * PTIME / 1000,
00173 16, 0, 0, &wavout) );
00174
00175
00176 for (i=0; i<input_cnt; ++i) {
00177 pj_ssize_t len;
00178
00179 CHECK( pjmedia_wav_player_port_create(pool, wav_input[i].fname, 20,
00180 PJMEDIA_FILE_NO_LOOP, 0,
00181 &wav_input[i].port) );
00182 len = pjmedia_wav_player_get_len(wav_input[i].port);
00183 len = (pj_ssize_t)(len * 1.0 * clock_rate /
00184 PJMEDIA_PIA_SRATE(&wav_input[i].port->info));
00185 if (len > (pj_ssize_t)longest)
00186 longest = len;
00187
00188 CHECK( pjmedia_conf_add_port(conf, pool, wav_input[i].port,
00189 NULL, &wav_input[i].slot));
00190
00191 CHECK( pjmedia_conf_connect_port(conf, wav_input[i].slot, 0, 0) );
00192 }
00193
00194
00195 processed = 0;
00196 while (processed < longest + clock_rate * APPEND * 2 / 1000) {
00197 pj_int16_t framebuf[PTIME * 48000 / 1000];
00198 pjmedia_port *cp = pjmedia_conf_get_master_port(conf);
00199 pjmedia_frame frame;
00200
00201 frame.buf = framebuf;
00202 frame.size = PJMEDIA_PIA_SPF(&cp->info) * 2;
00203 pj_assert(frame.size <= sizeof(framebuf));
00204
00205 CHECK( pjmedia_port_get_frame(cp, &frame) );
00206
00207 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
00208 pj_bzero(frame.buf, frame.size);
00209 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
00210 }
00211
00212 CHECK( pjmedia_port_put_frame(wavout, &frame));
00213
00214 processed += frame.size;
00215 }
00216
00217 PJ_LOG(3,(THIS_FILE, "Done. Output duration: %d.%03d",
00218 (processed >> 2)/clock_rate,
00219 ((processed >> 2)*1000/clock_rate) % 1000));
00220
00221
00222 CHECK( pjmedia_port_destroy(wavout) );
00223 for (i=0; i<input_cnt; ++i) {
00224 CHECK( pjmedia_conf_remove_port(conf, wav_input[i].slot) );
00225 CHECK( pjmedia_port_destroy(wav_input[i].port) );
00226 }
00227
00228 CHECK(pjmedia_conf_destroy(conf));
00229 CHECK(pjmedia_endpt_destroy(med_ept));
00230
00231 pj_pool_release(pool);
00232 pj_caching_pool_destroy(&cp);
00233 pj_shutdown();
00234
00235 return 0;
00236 }
00237