#include "micpath.h" #include aec_stream::aec_stream(pw_loop* lp,ringbuffer* hp,ringbuffer* mic, logger_record& ps, aec_params p): pwstream(48000, 1, 1, lp, "aec-output", pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Source", PW_KEY_MEDIA_ROLE, "DSP", NULL)), hp_rb(hp), mic_rb(mic), pstrm(ps), params(p) {} void aec_stream::on_process() { pw_buffer* pwb; if((pwb = pw_stream_dequeue_buffer(this->strm)) == nullptr) { return; } auto* b = pwb->buffer; float* dst = (float*)b->datas[0].data; if(dst == nullptr) return; auto n_frames = b->datas[0].maxsize / sizeof(float); if(pwb->requested) { n_frames = SPA_MIN(pwb->requested, n_frames); } size_t effective = 0; switch(status) { case INIT: hp_mic_delta = hp_rb->dequeue_time - mic_rb->enqueue_time; status = SKIP_EARLY; remaining_samples = 48000*10; [[fallthrough]]; case SKIP_EARLY: effective = mic_rb->dequeue(dst, n_frames, true, false); if(remaining_samples >= 48000 * 2 && remaining_samples - effective < 48000*2) { std::puts("Please keep silent for the next few seconds"); } if((remaining_samples-=effective) <= 0) { status = SCAN_SILENT; prm_mutex.lock(); remaining_samples = 48000; } break; case SCAN_SILENT: effective = mic_rb->dequeue(dst, n_frames, true, false); for(size_t i = 0; i < effective; ++i) { params.noise_power += dst[i] * dst[i]; } if((remaining_samples-=effective) <= 0) { status = SCAN_LOUD; params.noise_power /= 48000 - remaining_samples; remaining_samples = 48000; pstrm.request_noise(true, .1); } break; case SCAN_LOUD: { effective = mic_rb->dequeue(dst, n_frames, true, false); for(size_t i = 1; i <= effective; ++i) { for(size_t j = 0; j < 8192; ++j) { auto t = hp_rb->w_head_pos - 2*i - 2*j; while(t < 0) {t += ringbuffer::bsize;} if(t >= ringbuffer::bsize) {t -= ringbuffer::bsize;} cov[j] += dst[effective - i] * hp_rb->buffer[t + 1]; } } if((remaining_samples-=effective) <= 0) { pstrm.request_noise(false, 0.f); status = INACTIVE; params.echo_return_gain /= 48000 - remaining_samples; params.echo_return_gain -= params.noise_power; params.echo_return_gain *= 1/(.1*.1); // Renormalize params.valid = true; prm_mutex.unlock(); } break; } case INACTIVE: effective = mic_rb->dequeue(dst, n_frames, true, false); break; } b->datas[0].chunk->offset = 0; b->datas[0].chunk->stride = sizeof(float); b->datas[0].chunk->size = effective*sizeof(float); pw_stream_queue_buffer(this->strm, pwb); } aec_params aec_stream::get_aec_params() { prm_mutex.lock(); auto copy = params; prm_mutex.unlock(); return copy; }