Rough Lustre infra
This commit is contained in:
parent
4126db3e0e
commit
e584338466
8 changed files with 279 additions and 65 deletions
|
@ -1,6 +1,8 @@
|
|||
CXXFLAGS=-O3 -march=native $(CXXFLAGS)
|
||||
CXXFLAGS += -O3 -march=native
|
||||
|
||||
.PHONY: build
|
||||
build: main.out
|
||||
main.out: main.o
|
||||
$(CXX) $(LDFLAGS) $^ -o $@
|
||||
main.o: main.cc circ_buffer.h
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
|
32
pw_plugin/circ_buffer.h
Normal file
32
pw_plugin/circ_buffer.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#pragma once
|
||||
|
||||
template<size_t N, typename DataType=float>
|
||||
class circ_buffer {
|
||||
public:
|
||||
void enqueue(DataType* ptr, size_t num) {
|
||||
auto fst_xfer = std::min(num, N - head);
|
||||
std::memcpy(&raw[head], ptr, fst_xfer*sizeof(DataType));
|
||||
|
||||
if(fst_xfer < num) {
|
||||
std::memcpy(&raw[0], &ptr[fst_xfer], (num - fst_xfer)*sizeof(DataType));
|
||||
head = num - fst_xfer;
|
||||
} else {
|
||||
head += num;
|
||||
}
|
||||
}
|
||||
|
||||
// 0 means the last sample in the buffer, etc.
|
||||
DataType operator[](int numsamps) {
|
||||
int offset = head + numsamps - 1;
|
||||
if(offset <= 0) {
|
||||
offset += N;
|
||||
}
|
||||
return raw[offset];
|
||||
}
|
||||
|
||||
DataType raw[N] = {0};
|
||||
int head = 0; // head is where the next sample is going to be stored
|
||||
};
|
8
pw_plugin/link_record.sh
Executable file
8
pw_plugin/link_record.sh
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
pw-link audio-filter:AEC pw-record:input_FR
|
||||
#pw-link alsa_input.pci-0000_00_1b.0.analog-stereo:capture_FL pw-record:input_FL
|
||||
|
||||
|
||||
#pw-link mpv:output_FL pw-record:input_FL
|
||||
#pw-link mpv:output_FR pw-record:input_FR
|
|
@ -3,10 +3,7 @@
|
|||
pw-link -d mpv:output_FL alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo:playback_FL
|
||||
pw-link -d mpv:output_FR alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo:playback_FR
|
||||
|
||||
|
||||
# Setup the 3 ports
|
||||
pw-link mpv:output_FR audio-filter:musinput
|
||||
pw-link audio-filter:output alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo:playback_FR
|
||||
pw-link alsa_input.pci-0000_00_1b.0.analog-stereo:capture_FL audio-filter:micinput
|
||||
|
||||
#pw-link alsa_input.usb-Logitech_Logitech_USB_Headset-00.mono-fallback:capture_MONO audio-filter:input
|
||||
|
|
|
@ -3,79 +3,96 @@
|
|||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include "pipewire/pipewire.h"
|
||||
#include "spa/param/audio/format-utils.h"
|
||||
#include "spa/param/latency-utils.h"
|
||||
|
||||
#include "circ_buffer.h"
|
||||
|
||||
constexpr int samplerate = 48000;
|
||||
|
||||
struct local_data {
|
||||
pw_main_loop* loop;
|
||||
pw_thread_loop* loop;
|
||||
pw_filter* filter;
|
||||
|
||||
void* mic_port; // Input data from mic
|
||||
void* aec_port; // Echo cancelled data from mic
|
||||
|
||||
void* listen_port; // Input music port
|
||||
void* hp_port; // Output data to headphone
|
||||
|
||||
// Hopefully we never have >100ms roundtrip delay
|
||||
float record_buffer[samplerate * 10];
|
||||
float mic_buffer[samplerate * 10];
|
||||
// Initial learning buffers
|
||||
circ_buffer<samplerate * 5> record_buffer;
|
||||
circ_buffer<samplerate * 5> mic_buffer;
|
||||
std::atomic<bool> isearly{true};
|
||||
|
||||
int32_t head_index = 0; // The head is where the NEXT sample is going to be saved
|
||||
|
||||
/*double xcor_buffer[samplerate / 10] = {0};
|
||||
uint32_t sample_count = 0;*/
|
||||
// Contains the hp output needed for computing FIR filter
|
||||
circ_buffer<samplerate> filter_buf;
|
||||
std::array<float, 128> impulse_response;
|
||||
int initial_delay = 0;
|
||||
std::atomic<bool> isavailable{false};
|
||||
|
||||
circ_buffer<samplerate*5> aec_outbuf;
|
||||
|
||||
int head_index = 0;
|
||||
uint32_t call_count = 0;
|
||||
|
||||
std::atomic<bool> isdone{false};
|
||||
|
||||
float phase = 1.0;
|
||||
};
|
||||
|
||||
void on_process(local_data* ld, spa_io_position* position) {
|
||||
int32_t n_samples = (int32_t)position->clock.duration;
|
||||
|
||||
float* mic_in = (float*)pw_filter_get_dsp_buffer(ld->mic_port, n_samples);
|
||||
float* aec_out = (float*)pw_filter_get_dsp_buffer(ld->aec_port, n_samples);
|
||||
|
||||
float* listen_in = (float*)pw_filter_get_dsp_buffer(ld->listen_port, n_samples);
|
||||
float* hp_out = (float*)pw_filter_get_dsp_buffer(ld->hp_port, n_samples);
|
||||
|
||||
if(ld->isdone) {return;}
|
||||
if(mic_in == NULL || listen_in == NULL || hp_out == NULL) {return;}
|
||||
|
||||
// Simply echo the outgoing data
|
||||
int copy_frames = std::min(samplerate*10 - ld->head_index, n_samples);
|
||||
|
||||
std::memcpy(hp_out, listen_in, n_samples * sizeof(float));
|
||||
|
||||
std::memcpy(&ld->record_buffer[ld->head_index], listen_in, copy_frames*sizeof(float));
|
||||
std::memcpy(&ld->mic_buffer[ld->head_index], mic_in, copy_frames*sizeof(float));
|
||||
ld->filter_buf.enqueue(listen_in, n_samples); // Inefficient, ugly, and bad
|
||||
|
||||
ld->head_index += copy_frames;
|
||||
/*
|
||||
// Now, copy data to the circular buffer
|
||||
int32_t fst_cpy = std::min(n_samples, (int)(samplerate/10) - ld->head_index);
|
||||
std::memcpy(&ld->record_buffer[ld->head_index], listen_in, fst_cpy * sizeof(float));
|
||||
ld->head_index += fst_cpy;
|
||||
if(!ld->isavailable) {
|
||||
if(aec_out != NULL){
|
||||
std::memcpy(aec_out, mic_in, n_samples * sizeof(float));
|
||||
}
|
||||
|
||||
int32_t snd_cpy = std::max(0, n_samples - fst_cpy);
|
||||
std::memcpy(&ld->record_buffer[0], &listen_in[fst_cpy], snd_cpy * sizeof(float));
|
||||
if(snd_cpy != 0) { ld->head_index = snd_cpy; }
|
||||
*/
|
||||
if(ld->isearly) {
|
||||
ld->record_buffer.enqueue(listen_in, n_samples);
|
||||
ld->mic_buffer.enqueue(mic_in, n_samples);
|
||||
}
|
||||
} else {
|
||||
ld->record_buffer.enqueue(listen_in, n_samples);
|
||||
ld->mic_buffer.enqueue(mic_in, n_samples);
|
||||
for(int i = 0; i < n_samples; ++i) {
|
||||
double corr = 0.0;
|
||||
// Compute correction to be done at time -(n_samples-1 - i)
|
||||
for(int d = 0; d < ld->impulse_response.size(); ++d) {
|
||||
corr += ld->impulse_response[d] * ld->filter_buf[-d-ld->initial_delay - (n_samples-1 - i)];
|
||||
}
|
||||
|
||||
// To save on computation time, we randomly sample only one of the mic_in
|
||||
// inputs.
|
||||
if(aec_out != NULL) {aec_out[i] = *mic_in++ - corr;}
|
||||
float aec = *mic_in++ - corr;
|
||||
ld->aec_outbuf.enqueue(&aec, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
float input = mic_in[n_samples - 1];
|
||||
int32_t head_pos = ld->head_index - 1;
|
||||
if(head_pos == -1) {head_pos = samplerate/10 - 1;}
|
||||
ld->head_index += n_samples;
|
||||
if(ld->head_index >= 10*samplerate) {
|
||||
ld->isearly=false;
|
||||
}
|
||||
|
||||
// Update the xcorr
|
||||
for(int i = 0; i < samplerate / 10; ++i) {
|
||||
ld->xcor_buffer[i] = (1 - 1.0/512)*ld->xcor_buffer[i]
|
||||
+ 1.0/512 * input * ld->record_buffer[head_pos];
|
||||
head_pos--;
|
||||
if(head_pos == -1) {head_pos = samplerate/10 - 1;}
|
||||
}*/
|
||||
|
||||
ld->call_count++;
|
||||
if(ld->head_index == samplerate*10) {
|
||||
pw_main_loop_quit(ld->loop);
|
||||
if(ld->head_index >= 20*samplerate) {
|
||||
ld->isdone = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,21 +101,24 @@ const struct pw_filter_events filter_events {
|
|||
.process = (void(*)(void*, spa_io_position*))on_process,
|
||||
};
|
||||
|
||||
float compute_RMS_power(float* buffer, int n_samps) {
|
||||
template<size_t N, typename DT=float>
|
||||
DT compute_power(circ_buffer<N, DT>& buf) {
|
||||
double acc = 0;
|
||||
for(int i = 0; i < n_samps; ++i) {
|
||||
acc += buffer[i] * buffer[i];
|
||||
for(int i = 0; i < N; ++i) {
|
||||
acc += buf.raw[i] * buf.raw[i];
|
||||
}
|
||||
return (float)std::sqrt(acc/n_samps);
|
||||
return (DT)acc/N;
|
||||
}
|
||||
|
||||
// Delta positif -> retard du micro par rapport au HP -> physique.
|
||||
float avg_correlation(float bufferHP[], float bufferMIC[], int delta, int n_samps) {
|
||||
|
||||
template<size_t N, typename DT=float>
|
||||
DT avg_correlation(circ_buffer<N, DT>& bufHP, circ_buffer<N, DT>& bufMIC, int delta) {
|
||||
double acc = 0;
|
||||
for(int i = delta; i < n_samps; ++i) {
|
||||
acc += bufferHP[i - delta] * bufferMIC[i];
|
||||
for(int i = 0; -i-delta > -N; ++i) {
|
||||
acc += bufHP[-i - delta] * bufMIC[-i];
|
||||
}
|
||||
return acc / (n_samps - delta);
|
||||
|
||||
return acc / (N-delta);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
@ -106,14 +126,14 @@ int main(int argc, char** argv) {
|
|||
|
||||
pw_init(&argc, &argv);
|
||||
|
||||
local.loop = pw_main_loop_new(NULL);
|
||||
local.loop = pw_thread_loop_new("thread", NULL);
|
||||
if(local.loop == NULL) {
|
||||
std::cerr << "Could not create loop!\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
local.filter = pw_filter_new_simple(
|
||||
pw_main_loop_get_loop(local.loop),
|
||||
pw_thread_loop_get_loop(local.loop),
|
||||
"audio-filter",
|
||||
pw_properties_new(
|
||||
PW_KEY_MEDIA_TYPE, "Audio",
|
||||
|
@ -133,6 +153,18 @@ int main(int argc, char** argv) {
|
|||
NULL),
|
||||
NULL, 0);
|
||||
|
||||
|
||||
local.aec_port = pw_filter_add_port(local.filter,
|
||||
PW_DIRECTION_OUTPUT,
|
||||
PW_FILTER_PORT_FLAG_MAP_BUFFERS,
|
||||
sizeof(void*),
|
||||
pw_properties_new(
|
||||
PW_KEY_FORMAT_DSP, "32 bit float mono audio",
|
||||
PW_KEY_PORT_NAME, "AEC",
|
||||
PW_KEY_MEDIA_CATEGORY, "Capture",
|
||||
NULL),
|
||||
NULL, 0);
|
||||
|
||||
local.hp_port = pw_filter_add_port(local.filter,
|
||||
PW_DIRECTION_OUTPUT,
|
||||
PW_FILTER_PORT_FLAG_MAP_BUFFERS,
|
||||
|
@ -173,23 +205,99 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
std::printf("Waiting for connection\n");
|
||||
pw_main_loop_run(local.loop);
|
||||
std::printf("Successfully recorded 10s\n");
|
||||
pw_thread_loop_start(local.loop);
|
||||
|
||||
while(local.isearly) {};
|
||||
|
||||
float max_crosscor = -1.0f;
|
||||
int max_crosscor_index = 0;
|
||||
|
||||
std::array<float, samplerate/5> xcors;
|
||||
|
||||
for(int delta = 0; delta < samplerate/5; ++delta) {
|
||||
xcors[delta] = avg_correlation(local.record_buffer, local.mic_buffer, delta);
|
||||
if(std::abs(xcors[delta]) > max_crosscor) {
|
||||
max_crosscor = std::abs(xcors[delta]);
|
||||
max_crosscor_index = delta;
|
||||
}
|
||||
}
|
||||
|
||||
// If the x-fer coeff was 1, then avg_correlation would return the power input
|
||||
float input_power = compute_power(local.record_buffer);
|
||||
std::printf("input power : %e (%f dB)\n", input_power, 10*std::log10(input_power));
|
||||
max_crosscor_index -= 16; // Allow for non-zero rise time (can you be smarter?)
|
||||
|
||||
std::printf("Estimated latency : %d samples\n", max_crosscor_index);
|
||||
std::printf("h = [");
|
||||
for(int i = 0; i < 128; ++i) {
|
||||
std::printf("%e,", xcors[max_crosscor_index+i]);
|
||||
//local.impulse_response[i] = xcors[max_crosscor_index+i]/input_power;
|
||||
local.impulse_response[i] = 0.0;
|
||||
}
|
||||
for(int i = 128; i < 4096; ++i) {
|
||||
std::printf("%e,", xcors[max_crosscor_index+i]);
|
||||
}
|
||||
std::printf("]\n");
|
||||
local.initial_delay = max_crosscor_index;
|
||||
local.isavailable = true;
|
||||
|
||||
while(!local.isdone) {}
|
||||
pw_thread_loop_stop(local.loop);
|
||||
pw_filter_destroy(local.filter);
|
||||
pw_main_loop_destroy(local.loop);
|
||||
pw_thread_loop_destroy(local.loop);
|
||||
pw_deinit();
|
||||
|
||||
std::printf("Call count : %u\n", local.call_count);
|
||||
|
||||
std::printf("XCor = [\n");
|
||||
// Look at the first 200ms...
|
||||
for(int delta = 0; delta < 48000/5; ++delta) {
|
||||
float correct_crosscor = avg_correlation(&local.record_buffer[48000], &local.mic_buffer[48000], delta, 48000*9);
|
||||
// Normalizing -> if h(n) = del_0(n) then correct_crosscor = del_0.
|
||||
std::printf("%e,", correct_crosscor);
|
||||
std::printf("hMIC = [");
|
||||
for(int i = 0; i < 4096; ++i) {
|
||||
float x = avg_correlation(local.record_buffer, local.mic_buffer, i + local.initial_delay);
|
||||
std::printf("%e,", x);
|
||||
}
|
||||
std::printf("]\n");
|
||||
|
||||
std::printf("hAEC = [");
|
||||
for(int i = 0; i < 4096; ++i) {
|
||||
float x = avg_correlation(local.record_buffer, local.aec_outbuf, i + local.initial_delay);
|
||||
std::printf("%e,", x);
|
||||
}
|
||||
std::printf("]\n");
|
||||
|
||||
|
||||
float interleavedData[samplerate*5][2];
|
||||
for(int i = 0; i < samplerate*5; ++i) {
|
||||
interleavedData[i][0] = local.aec_outbuf[-i];
|
||||
interleavedData[i][1] = local.mic_buffer[-i];
|
||||
}
|
||||
|
||||
float mic_RMS = std::sqrt(compute_power(local.mic_buffer));
|
||||
float aec_RMS = std::sqrt(compute_power(local.aec_outbuf));
|
||||
std::printf("RMS power mic : %f dB (with AEC : %f dB)\n",
|
||||
20*std::log10(mic_RMS),
|
||||
20*std::log10(aec_RMS));
|
||||
|
||||
std::FILE* f = std::fopen("record.wav", "wb");
|
||||
if(f == nullptr) {
|
||||
std::fprintf(stderr, "Could not open record file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::fwrite("RIFFxxxxWAVEfmt ", 16, 1, f);
|
||||
uint16_t header[] = {16, 0, // BlocSize
|
||||
3, // AudioFormat : IEEE 754 float
|
||||
2, // 2 Channels
|
||||
(uint16_t)samplerate, 0, // Samplerate
|
||||
(samplerate * sizeof(float) * 2) % 65536, (samplerate * sizeof(float) * 2)/65536, // BitsPerSec
|
||||
2 * sizeof(float),
|
||||
sizeof(float)*8,
|
||||
};
|
||||
std::fwrite(header, sizeof(header), 1, f);
|
||||
std::fwrite("data", 4, 1, f);
|
||||
|
||||
std::fwrite(interleavedData, sizeof(interleavedData), 1, f);
|
||||
uint32_t size = (uint32_t)std::ftell(f) - 8;
|
||||
std::fseek(f, 4, SEEK_SET);
|
||||
std::fwrite(&size, sizeof(uint32_t), 1, f);
|
||||
std::fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@ let
|
|||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "Active echo cancelling PW plugin";
|
||||
nativeBuildInputs = [pkgs.pkg-config pkgs.pipewire];
|
||||
nativeBuildInputs = [pkgs.pkg-config pkgs.pipewire
|
||||
pkgs.ocamlPackages.lustre-v6];
|
||||
buildInputs = [pkgs.pipewire];
|
||||
|
||||
shellHook =
|
||||
|
|
59
pw_plugin/test.lus
Normal file
59
pw_plugin/test.lus
Normal file
|
@ -0,0 +1,59 @@
|
|||
node FIR_proto(x : real; h : real)
|
||||
returns (delay: real; val: real);
|
||||
let
|
||||
delay = 0.0 -> pre x;
|
||||
val = x * h;
|
||||
tel
|
||||
|
||||
node FIR<<const n : int>>(x : real; h : real^n)
|
||||
returns (y : real);
|
||||
var tmp : real^n;
|
||||
var d : real;
|
||||
let
|
||||
(d, tmp) = fillred<<FIR_proto; n>>(x, h);
|
||||
y = red<<+, n>>(0.0, tmp);
|
||||
tel
|
||||
|
||||
node delay_chain(x : real)
|
||||
returns (delay : real; val: real);
|
||||
let
|
||||
delay = 0.0 -> pre x;
|
||||
val = x;
|
||||
tel
|
||||
|
||||
node delayed_array<< const n : int>>(x : real)
|
||||
returns (y : real^n);
|
||||
var d : real;
|
||||
let
|
||||
(d, y) = fill<<delay_chain, n>>(x);
|
||||
tel
|
||||
|
||||
function power_acc(p : real; s : real)
|
||||
returns (pp : real);
|
||||
let
|
||||
pp = p + s*s;
|
||||
tel
|
||||
|
||||
function scalar_mult(c : real; x : real)
|
||||
returns (prop: real; y : real)
|
||||
let
|
||||
y = x * c;
|
||||
prop = c;
|
||||
tel
|
||||
|
||||
node NLMS_FIR<< const n : int>>(ref : real; x : real; mu : real)
|
||||
returns (y : real; h : real^n);
|
||||
var delayed : real^n;
|
||||
--var h : real^n;
|
||||
var stepsize : real;
|
||||
var currstep : real^n;
|
||||
var e : real;
|
||||
let
|
||||
h = (0. ^ n) -> map<<+, n>>(pre h, pre currstep);
|
||||
y = FIR<<n>>(ref, h);
|
||||
e = x - y;
|
||||
delayed = delayed_array<<n>>(ref);
|
||||
(stepsize, currstep) = fillred<<scalar_mult, n>>(mu * e / red<<power_acc, n>>(0.0, delayed), delayed);
|
||||
tel
|
||||
|
||||
node EchoCancel = NLMS_FIR<<2>>;
|
7
releves_experimentaux/aec_nonadapt.md
Normal file
7
releves_experimentaux/aec_nonadapt.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
Waiting for connection
|
||||
input power : 8.481511e-02 (-10.715267 dB)
|
||||
Estimated latency : 2072 samples
|
||||
h = [3.271739e-06,2.924688e-06,1.763123e-06,1.598872e-06,-1.542474e-06,-2.790719e-06,1.138158e-06,1.259070e-06,-2.636593e-07,3.095695e-06,2.068845e-06,-1.731367e-06,-1.835926e-06,-1.503702e-05,-2.782991e-05,5.644692e-06,5.565347e-05,5.031102e-05,9.191661e-06,-1.363320e-05,-2.762607e-05,-3.404056e-05,-7.370017e-06,2.171157e-05,1.603674e-05,6.665189e-06,1.563925e-05,1.478698e-05,4.828333e-07,-2.030652e-06,-1.818409e-07,-1.775017e-05,-3.986377e-05,-2.998993e-05,7.863064e-06,3.160931e-05,2.641590e-05,1.158924e-05,-6.329020e-06,-2.449810e-05,-2.577206e-05,-1.341154e-05,-4.543574e-06,6.948965e-06,2.075484e-05,1.586906e-05,-1.010263e-06,-5.743027e-06,-3.433221e-06,-5.374510e-06,-5.269185e-06,-2.836608e-06,-2.665492e-06,-7.826074e-07,2.716754e-06,4.538443e-06,6.862248e-06,6.459541e-06,-2.901018e-06,-1.516135e-05,-1.791405e-05,-7.413397e-06,6.792774e-06,1.159226e-05,7.775971e-06,4.389139e-06,-1.251449e-07,-7.714205e-06,-9.886030e-06,-4.942924e-06,3.975412e-07,5.141976e-06,7.397623e-06,3.172451e-06,-1.232587e-06,2.185396e-07,1.398136e-06,-1.647457e-06,-4.080293e-06,-2.917006e-06,3.151812e-07,2.822137e-06,3.108870e-06,3.022219e-06,2.844609e-06,-1.747608e-07,-4.133819e-06,-4.318808e-06,-1.262797e-06,1.830398e-06,3.236334e-06,2.409960e-06,1.033152e-06,7.254291e-07,3.811505e-07,-2.935707e-07,-4.974708e-07,-9.766055e-07,-1.443246e-06,-6.549155e-07,7.018296e-07,2.451788e-06,4.527684e-06,3.812206e-06,-5.081766e-07,-3.695353e-06,-3.028376e-06,-8.052680e-07,1.095064e-06,2.858859e-06,4.124033e-06,3.635738e-06,1.437906e-06,-6.250842e-07,-1.541621e-06,-1.481056e-06,-9.444751e-08,1.469135e-06,9.936580e-07,-5.918948e-08,5.872722e-07,1.212643e-06,8.634079e-07,1.092343e-06,9.934481e-07,-5.270325e-07,-1.599179e-06,-1.404751e-06,]
|
||||
hp = [7.579419e-07,1.017279e-06,1.120930e-06,9.922273e-07,1.014994e-06,1.161230e-06,1.268406e-06,1.329308e-06,1.125896e-06,9.912051e-07,1.450320e-06,1.656983e-06,1.043058e-06,8.018420e-07,1.148983e-06,9.484432e-07,6.409828e-07,1.052445e-06,1.298868e-06,1.018692e-06,9.794798e-07,9.946230e-07,7.896238e-07,9.054657e-07,1.044953e-06,5.803265e-07,7.747900e-07,6.516865e-07,-1.843298e-07,-5.232859e-08,1.091970e-06,1.197647e-06,5.271366e-07,5.290972e-07,6.395687e-07,3.632493e-07,4.361071e-07,6.774196e-07,5.345980e-07,4.479882e-07,5.279770e-07,3.773886e-07,2.525861e-07,2.849170e-07,1.306191e-07,7.347014e-08,2.891112e-07,8.824291e-08,-4.077927e-07,-2.459246e-07,3.170903e-07,1.783436e-07,1.092767e-07,3.889834e-07,6.991422e-07,6.159433e-07,2.175560e-07,-1.534838e-07,-3.576327e-07,-2.331370e-07,3.752074e-07,8.600920e-07,5.320671e-07,2.998904e-08,9.391530e-08,2.023770e-07,1.424387e-07,3.567343e-07,3.646473e-07,-1.206053e-07,-1.689709e-07,2.175323e-07,2.101427e-07,2.398210e-07,5.951874e-07,5.159268e-07,1.398182e-07,4.355232e-07,2.294949e-07,2.002979e-08,5.931172e-07,8.346058e-07,1.528707e-07,-9.937406e-08,4.122094e-07,7.207732e-07,6.171209e-07,3.245305e-07,1.799761e-07,5.853640e-07,9.454486e-07,7.361014e-07,7.513118e-07,1.168803e-06,1.099719e-06,5.500127e-07,1.805435e-07,3.062708e-07,9.006810e-07,4.063539e-07,1.124199e-07,-2.978902e-07,-2.234152e-07,-1.947296e-07,-1.614850e-07,3.397181e-07,6.400992e-07,3.742877e-07,2.063836e-07,2.024570e-07,4.615952e-08,-1.889897e-07,-5.784012e-07,-9.342185e-07,-8.092598e-07,-5.507102e-07,-4.695933e-07,-3.399265e-07,-6.158162e-07,-1.375348e-06,-1.406166e-06,-6.660063e-07,-3.841649e-07,-5.350056e-07,2.596009e-07,2.292975e-08,-5.579458e-07,-3.376216e-07,]
|
||||
|
||||
On obtient ~25dB d'annulation d'écho.
|
Loading…
Add table
Reference in a new issue