/* * Create the echo canceller. */ pjs_echo_canceller::pjs_echo_canceller(pj_pool_t *pool_, unsigned clock_rate, unsigned samples_per_frame_, unsigned tail_ms, unsigned latency_ms, unsigned options) { int sampling_rate = clock_rate; unsigned ptime, lat_cnt; unsigned delay_buf_opt = 0; lat_ready = PJ_FALSE; /* Create new pool and instantiate and init the EC */ pool = pj_pool_create(pool_->factory, "ec%p", 256, 256, NULL); lock = new PPJ_SemaphoreLock(pool, NULL, 1, 1); samples_per_frame = samples_per_frame_; frm_buf = (pj_int16_t*) pj_pool_alloc(pool, samples_per_frame << 1); state = speex_echo_state_init(samples_per_frame, clock_rate * tail_ms / 1000); speex_echo_ctl(state, SPEEX_ECHO_SET_SAMPLING_RATE, &sampling_rate); preprocess = speex_preprocess_state_init(samples_per_frame, clock_rate); tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame); speex_preprocess_ctl(preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, state); pj_list_init(&lat_buf); pj_list_init(&lat_free); PJ_LOG(5, (THIS_FILE, "Creating echo canceler")); /* Create latency buffers */ ptime = samples_per_frame * 1000 / clock_rate; if (latency_ms < ptime) { /* Give at least one frame delay to simplify programming */ latency_ms = ptime; } lat_cnt = latency_ms / ptime; while (lat_cnt--) { struct frame *frm; frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame << 1) + sizeof(struct frame)); pj_list_push_back(&lat_free, frm); } /* Create delay buffer to compensate drifts */ if (options & PJMEDIA_ECHO_USE_SIMPLE_FIFO) delay_buf_opt |= PJMEDIA_DELAY_BUF_SIMPLE_FIFO; pjmedia_delay_buf_create(pool, NULL, clock_rate, samples_per_frame, 1, (PJMEDIA_SOUND_BUFFER_COUNT + 1) * ptime, delay_buf_opt, &delay_buf); PJ_LOG(4, (THIS_FILE, "ECHO canceller created, clock_rate=%d, channel=%d, " "samples per frame=%d, tail length=%d ms, " "latency=%d ms", clock_rate, 1, samples_per_frame, tail_ms, latency_ms)); }
/* * Create the echo canceller. */ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned latency_ms, unsigned options, pjmedia_echo_state **p_echo ) { unsigned ptime, lat_cnt; unsigned delay_buf_opt = 0; pjmedia_echo_state *ec; pj_status_t status; /* Create new pool and instantiate and init the EC */ pool = pj_pool_create(pool->factory, "ec%p", 256, 256, NULL); ec = PJ_POOL_ZALLOC_T(pool, struct pjmedia_echo_state); ec->pool = pool; ec->obj_name = pool->obj_name; ec->samples_per_frame = samples_per_frame; ec->frm_buf = (pj_int16_t*)pj_pool_alloc(pool, samples_per_frame<<1); pj_list_init(&ec->lat_buf); pj_list_init(&ec->lat_free); /* Select the backend algorithm */ if (0) { /* Dummy */ ; #if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0 } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_SPEEX || (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) { ec->op = &speex_aec_op; #endif #if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_IPP || (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) { ec->op = &ipp_aec_op; #endif } else { ec->op = &echo_supp_op; } /* Completeness check for EC operation playback and capture, they must * be implemented both or none. */ pj_assert(!ec->op->ec_capture == !ec->op->ec_playback); PJ_LOG(5,(ec->obj_name, "Creating %s", ec->op->name)); /* Instantiate EC object */ status = (*ec->op->ec_create)(pool, clock_rate, channel_count, samples_per_frame, tail_ms, options, &ec->state); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } /* If EC algo does not have playback and capture callbakcs, * create latency buffer and delay buffer to handle drift. */ if (ec->op->ec_playback && ec->op->ec_capture) { latency_ms = 0; } else { /* Create latency buffers */ ptime = samples_per_frame * 1000 / clock_rate; if (latency_ms > ptime) { /* Normalize latency with delaybuf/WSOLA latency */ latency_ms -= PJ_MIN(ptime, PJMEDIA_WSOLA_DELAY_MSEC); } if (latency_ms < ptime) { /* Give at least one frame delay to simplify programming */ latency_ms = ptime; } lat_cnt = latency_ms / ptime; while (lat_cnt--) { struct frame *frm; frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame<<1) + sizeof(struct frame)); pj_list_push_back(&ec->lat_free, frm); } /* Create delay buffer to compensate drifts */ if (options & PJMEDIA_ECHO_USE_SIMPLE_FIFO) delay_buf_opt |= PJMEDIA_DELAY_BUF_SIMPLE_FIFO; status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate, samples_per_frame, channel_count, (PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime, delay_buf_opt, &ec->delay_buf); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } PJ_LOG(4,(ec->obj_name, "%s created, clock_rate=%d, channel=%d, " "samples per frame=%d, tail length=%d ms, " "latency=%d ms", ec->op->name, clock_rate, channel_count, samples_per_frame, tail_ms, latency_ms)); /* Done */ *p_echo = ec; return PJ_SUCCESS; }
/* Application init */ static pj_status_t app_init() { unsigned i, count; pj_status_t status; /* Redirect log */ pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer); pj_log_set_decor(PJ_LOG_HAS_NEWLINE); pj_log_set_level(3); /* Init pjlib */ status = pj_init(); if (status != PJ_SUCCESS) { app_perror("pj_init()", status); return status; } pj_caching_pool_init(&cp, NULL, 0); /* Init sound subsystem */ status = pjmedia_aud_subsys_init(&cp.factory); if (status != PJ_SUCCESS) { app_perror("pjmedia_snd_init()", status); pj_caching_pool_destroy(&cp); pj_shutdown(); return status; } count = pjmedia_aud_dev_count(); PJ_LOG(3,(THIS_FILE, "Device count: %d", count)); for (i=0; i<count; ++i) { pjmedia_aud_dev_info info; pj_status_t status; status = pjmedia_aud_dev_get_info(i, &info); pj_assert(status == PJ_SUCCESS); PJ_LOG(3, (THIS_FILE, "%d: %s %d/%d %dHz", i, info.name, info.input_count, info.output_count, info.default_samples_per_sec)); unsigned j; /* Print extended formats supported by this audio device */ PJ_LOG(3, (THIS_FILE, " Extended formats supported:")); for (j = 0; j < info.ext_fmt_cnt; ++j) { const char *fmt_name = NULL; switch (info.ext_fmt[j].id) { case PJMEDIA_FORMAT_PCMA: fmt_name = "PCMA"; break; case PJMEDIA_FORMAT_PCMU: fmt_name = "PCMU"; break; case PJMEDIA_FORMAT_AMR: fmt_name = "AMR-NB"; break; case PJMEDIA_FORMAT_G729: fmt_name = "G729"; break; case PJMEDIA_FORMAT_ILBC: fmt_name = "ILBC"; break; case PJMEDIA_FORMAT_PCM: fmt_name = "PCM"; break; default: fmt_name = "Unknown"; break; } PJ_LOG(3, (THIS_FILE, " - %s", fmt_name)); } } /* Create pool */ pool = pj_pool_create(&cp.factory, THIS_FILE, 512, 512, NULL); if (pool == NULL) { app_perror("pj_pool_create()", status); pj_caching_pool_destroy(&cp); pj_shutdown(); return status; } /* Init delay buffer */ status = pjmedia_delay_buf_create(pool, THIS_FILE, CLOCK_RATE, SAMPLES_PER_FRAME, CHANNEL_COUNT, 0, 0, &delaybuf); if (status != PJ_SUCCESS) { app_perror("pjmedia_delay_buf_create()", status); //pj_caching_pool_destroy(&cp); //pj_shutdown(); //return status; } return PJ_SUCCESS; }
/* * Create reverse phase port for the specified channel. */ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, pjmedia_port *splitcomb, unsigned ch_num, unsigned options, pjmedia_port **p_chport) { const pj_str_t name = pj_str("scomb-rev"); struct splitcomb *sc = (struct splitcomb*) splitcomb; struct reverse_port *rport; unsigned buf_cnt; const pjmedia_audio_format_detail *sc_afd, *p_afd; pjmedia_port *port; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL); /* Make sure this is really a splitcomb port */ PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL); /* Check the channel number */ PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL); /* options is unused for now */ PJ_UNUSED_ARG(options); sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1); /* Create the port */ rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port); rport->parent = sc; rport->ch_num = ch_num; /* Initialize port info... */ port = &rport->base; pjmedia_port_info_init(&port->info, &name, SIGNATURE_PORT, sc_afd->clock_rate, 1, sc_afd->bits_per_sample, PJMEDIA_PIA_SPF(&splitcomb->info) / sc_afd->channel_count); p_afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1); /* ... and the callbacks */ port->put_frame = &rport_put_frame; port->get_frame = &rport_get_frame; port->on_destroy = &rport_on_destroy; /* Buffer settings */ buf_cnt = options & 0xFF; if (buf_cnt == 0) buf_cnt = MAX_BUF_CNT; rport->max_burst = MAX_BURST; rport->max_null_frames = MAX_NULL_FRAMES; /* Create downstream/put buffers */ status = pjmedia_delay_buf_create(pool, "scombdb-dn", p_afd->clock_rate, PJMEDIA_PIA_SPF(&port->info), p_afd->channel_count, buf_cnt * p_afd->frame_time_usec / 1000, 0, &rport->buf[DIR_DOWNSTREAM].dbuf); if (status != PJ_SUCCESS) { return status; } /* Create upstream/get buffers */ status = pjmedia_delay_buf_create(pool, "scombdb-up", p_afd->clock_rate, PJMEDIA_PIA_SPF(&port->info), p_afd->channel_count, buf_cnt * p_afd->frame_time_usec / 1000, 0, &rport->buf[DIR_UPSTREAM].dbuf); if (status != PJ_SUCCESS) { pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf); return status; } /* And temporary upstream/get buffer */ rport->tmp_up_buf = (pj_int16_t*) pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&port->info)); /* Save port in the splitcomb */ sc->port_desc[ch_num].port = &rport->base; sc->port_desc[ch_num].reversed = PJ_TRUE; /* Done */ *p_chport = port; return status; }
/* Application init */ static pj_status_t app_init() { unsigned i, count; pj_status_t status; /* Redirect log */ pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer); pj_log_set_decor(PJ_LOG_HAS_NEWLINE); pj_log_set_level(3); /* Init pjlib */ status = pj_init(); if (status != PJ_SUCCESS) { app_perror("pj_init()", status); return status; } pj_caching_pool_init(&cp, NULL, 0); /* Init sound subsystem */ status = pjmedia_snd_init(&cp.factory); if (status != PJ_SUCCESS) { app_perror("pjmedia_snd_init()", status); pj_caching_pool_destroy(&cp); pj_shutdown(); return status; } count = pjmedia_snd_get_dev_count(); PJ_LOG(3,(THIS_FILE, "Device count: %d", count)); for (i=0; i<count; ++i) { const pjmedia_snd_dev_info *info; info = pjmedia_snd_get_dev_info(i); PJ_LOG(3, (THIS_FILE, "%d: %s %d/%d %dHz", i, info->name, info->input_count, info->output_count, info->default_samples_per_sec)); } /* Create pool */ pool = pj_pool_create(&cp.factory, THIS_FILE, 512, 512, NULL); if (pool == NULL) { app_perror("pj_pool_create()", status); pj_caching_pool_destroy(&cp); pj_shutdown(); return status; } /* Init delay buffer */ status = pjmedia_delay_buf_create(pool, THIS_FILE, CLOCK_RATE, SAMPLES_PER_FRAME, CHANNEL_COUNT, 0, 0, &delaybuf); if (status != PJ_SUCCESS) { app_perror("pjmedia_delay_buf_create()", status); //pj_caching_pool_destroy(&cp); //pj_shutdown(); //return status; } return PJ_SUCCESS; }