Ну вот пока , что удалось на розовом шуме сделать. С компрессией. Все питчи автоматом съедает.
Цифровая АРУ 0...1
243433 просмотров, 284 ответов — стр. 10 из 19
15 ноября 2020 г. в 09:09#136
15 ноября 2020 г. в 11:00#137
нужно сигнал на лимиттер садить иногда.
У меня, напомню, совсем короткие пички (типа, до 2 мс, точное время уже не помню) именно на лимитер и садятся, отрабатывать их АРУ смысла не вижу никакого..

15 ноября 2020 г. в 02:06#138
Я вообщем то все понимаю, но только не могу до конца понять от куда этот хвост появляется ?
Собственно с ним и борюсь уже три дня.
Ну вот какой участок осциллограммы дает этот хвост?
Собственно с ним и борюсь уже три дня.Ну вот какой участок осциллограммы дает этот хвост?
15 ноября 2020 г. в 02:38#139
Любое амплитудное вмешательство в исходник расширяет его спектр, и тем сильнее, чем с большей частотой (большей крутизной) Вы осуществляете регулирование, от этого уйти нельзя, это по определению.
Если стоит лимитер, или скоростная АРУ, рубящая пички, НЧ фильтр за ней очень полезен, гляньте, к примеру, на модуль 0913...

Если стоит лимитер, или скоростная АРУ, рубящая пички, НЧ фильтр за ней очень полезен, гляньте, к примеру, на модуль 0913...

15 ноября 2020 г. в 03:06#140
У меня стоит фильтр 300-ого порядка после ару
15 ноября 2020 г. в 03:08#141
стоит фильтр 300-ого порядка после аруЗачем так много? Уменьшите порядок до разумного

15 ноября 2020 г. в 03:08#142
Основной 800-ого порядка
15 ноября 2020 г. в 03:29#143
Любое амплитудное вмешательство в исходник расширяет его спектрДаже если от 0 до 0 я это делаю?
Я определяю начало периода и конец (через 1мс) тоже 0 или около того, и только потом умножаю на коэффициент.
Причем когда я это делаю на увеличение амплитуды то все ок хвоста нет, а на уменьшение почти всегда. Либо где-то не увидел не стыковку амплитуд либо еще что.
15 ноября 2020 г. в 03:40#144
Я определяю начало периода и конец (через 1мс) тоже 0 или около того, и только потом умножаю на коэффициент.
А какая разница? Это же, фактически, АМ со всеми вытекающими.

15 ноября 2020 г. в 03:54#145
Вот пример в котором алгоритм цепляется за питчики и запускает задержку и второй - это с поглощением питчиков.
Смотрим на водопад
Ну вот как раз "лимиттер" дает на водопаде лес.
Смотрим на водопад

Ну вот как раз "лимиттер" дает на водопаде лес.
15 ноября 2020 г. в 05:01#146
Это обычный шум? 

15 ноября 2020 г. в 06:52#147
Это розовый шум!! и на него ару ох сильно реагирует.
15 ноября 2020 г. в 06:55#148
Сделал !!!
Новая версия. Работает даже если стробировать сигнал около 1 !!! Без дельты. С дельтой вооще огонь
Код: [Выделить]float32_t max = 0.49975586f; //максималка от -0.5 до +0.5 примерно для 12 бит
uint16_t count = 100;
float32_t pit1 = -1;
float32_t pit2 = 0;
uint8_t stopGoingUp = 0;
float32_t rmsPik = 0;
float32_t rmsFrame = 0;
void agcDo(float32_t *buffer) {
float32_t agcMetrTemp = 0;
if (tim > 0)tim--; //уменьшение задержки
stopGoingUp = 0;
for (uint16_t i = 0; i < FRAME_SIZE; i++) {
float32_t a;
//a = (buffer[i] < 0.0f) ? -1.0f * buffer[i] : buffer[i];// adc V
a = fabsf(buffer[i]);
// seach min
if (a > 0)nowAgc = max / a;
else nowAgc = max / 0.000005f;
uint16_t upIdx = i;
float32_t pikBuf = -1;
uint8_t exit = 1;
uint8_t exitYN = 0;
uint16_t wait = 48;// 1msec
int direction = 0;//напрвление вниз (отрицательная синусоида)
if (buffer[i] > 0) direction = 1;//напрвление вверх (положительная синусоида)
if (buffer[i] == 0) exit = 0; //пропустим поиск пика в блоке
while (exit) { //поиск пика в блоке от 0 до 0
exit = 0;//выходим
float32_t p1 = fabsf(buffer[upIdx]);//feature
if (upIdx < FRAME_SIZE) {
if (direction) {//+++++++++++++++++++++++++++++
//если условие остановило в ++ то добиваем его до 0
if ((buffer[upIdx] >= 0 && !exitYN) || (upIdx - i < wait)) {
upIdx++;
if (p1 > pikBuf) {
pikBuf = p1;
}
exit = 1;//не выходим
}
//если условие остановило в -- то добиваем его до 0 и выходим
if (((buffer[upIdx] < 0)) && (upIdx - i >= wait)) {
upIdx++;
exitYN = 1; //не дадим пред условию сработать
if (p1 > pikBuf) {
pikBuf = p1;
}
exit = 1;//не выходим
}
} else { //------------------------
//going up
//если условие остановило в -- то добиваем его до 0 и выходим
if ((buffer[upIdx] <= 0 && !exitYN) || (upIdx - i < wait)) {
upIdx++;
if (p1 > pikBuf) {
pikBuf = p1;
}
exit = 1;//не выходим
}
//если условие остановило в ++ то добиваем его до 0
if (((buffer[upIdx] > 0)) && (upIdx - i >= wait)) {
upIdx++;
exitYN = 1; //не дадим пред условию сработать
if (p1 > pikBuf) {
pikBuf = p1;
}
exit = 1;//не выходим
}
}
//добавим блок если в нем есть глобальный пик (выброс)
if (upIdx - i >= wait && max / pikBuf < agcLevel) {
if (256 + upIdx < FRAME_SIZE) wait = 256;
}
}
}//while
if (upIdx > i) { //если блок не пустой то...
float32_t locNowAgc;
//проверка на 0
if (pikBuf != 0) {
locNowAgc = max / pikBuf;//максимальное значение
} else locNowAgc = max / 0.00005f;
//типа среднее значение . исползуется для отката .
rmsPik = (rmsPik + pikBuf) / 2.0f;
//agcLevel == текущий уровень ару
if (locNowAgc < agcLevel) { // всплеск или питч V, идем вниз ..........................
stopGoingUp=0;
//заполним блок
if (i) {
for (uint16_t z = i; z <= upIdx; z++) {
buffer[z] *= locNowAgc;//применить новый коэфф
}
} else {//механизм сочленения с пред фреймом ...ждем первого нуля
if (buffer[0] > 0) direction = 1;//напрвление вверх (положительная синусоида)
exit = 1;
uint16_t idx0 = 0;
while (exit) {
exit = 0;//выходим
if (direction) {//+++++++++++++++++++++++++++++
if (buffer[idx0] > 0) {
idx0++;
exit = 1;//не выходим
}
} else { //------------------------
//going up
if (buffer[idx0] < 0) {
idx0++;
exit = 1;//не выходим
}
}
}//while
for (uint16_t z = 0; z <= upIdx; z++) {
if (z <= idx0)buffer[z] *= agcLevel;//применить старый коэфф
else buffer[z] *= locNowAgc;//применить новый коэфф
}
}
if ((pit2< pit1 && pit1 > pikBuf ) ) {//смотрим следующий питч если
agcLevel -= (agcLevel - locNowAgc) / 5;
} else {
// agcLevel = locNowAgc;
agcLevel -= (agcLevel - locNowAgc) / 2;
}
if ((pit2< pit1 && pit1 < pikBuf )) {//смотрим следующий питч если
// agcLevel = locNowAgc;
agcLevel -= (agcLevel - locNowAgc) / 1;
}
pit2 = pit1;
pit1 = pikBuf;
tim = 30;//задежка отпускания
// }
} else {//идем вверх/////////////////////////////////////////////////////////////////////
float32_t cc ;
if (tim == 0) {//подождали и начинаем
cc = agcLevel + (locNowAgc - agcLevel) / 600;
//проверим можем нет еще откатить
if (rmsFrame * cc < max - 0.0f && !stopGoingUp) {//дельта отката 0.4
agcLevel = cc;
} else stopGoingUp=1; //остановить откат
}
//заполним полупериод
for (uint16_t z = i; z <= upIdx; z++) {
buffer[z] *= agcLevel;//применить новый коэфф
}
}
i = upIdx; // след цикл будет стратовать от upIdx
} else { //это ситуация редкая но тоже надо . сделано по классике одного замера
if (nowAgc < agcLevel) { // всплеск V
agcLevel -= (agcLevel - nowAgc) / 2;
// tim = 10;//задежка отпускания
}
float32_t f = a * agcLevel;
//контроль вылета в out
if (f > max) {//out
if (buffer[i] >= 0) buffer[i] = max;//max
else buffer[i] = -max;//max
// agcLevel = 1;
} else
buffer[i] *= agcLevel;
}
if (a > agcMetrTemp) agcMetrTemp = a; //s-meter макс V
}//for
rmsFrame=(rmsFrame+rmsPik)/2.0f; // глобальный меж фреймовый средничок нужен для отката до какого-то уровня.
agcMetr = agcMetrTemp; //s-meter
}
Новая версия. Работает даже если стробировать сигнал около 1 !!! Без дельты. С дельтой вооще огонь

Код: [Выделить]float32_t max = 0.49975586f; //максималка от -0.5 до +0.5 примерно для 12 бит
uint16_t count = 100;
float32_t pit1 = -1;
float32_t pit2 = 0;
uint8_t stopGoingUp = 0;
float32_t rmsPik = 0;
float32_t rmsFrame = 0;
void agcDo(float32_t *buffer) {
float32_t agcMetrTemp = 0;
if (tim > 0)tim--; //уменьшение задержки
stopGoingUp = 0;
for (uint16_t i = 0; i < FRAME_SIZE; i++) {
float32_t a;
//a = (buffer[i] < 0.0f) ? -1.0f * buffer[i] : buffer[i];// adc V
a = fabsf(buffer[i]);
// seach min
if (a > 0)nowAgc = max / a;
else nowAgc = max / 0.000005f;
uint16_t upIdx = i;
float32_t pikBuf = -1;
uint8_t exit = 1;
uint8_t exitYN = 0;
uint16_t wait = 48;// 1msec
int direction = 0;//напрвление вниз (отрицательная синусоида)
if (buffer[i] > 0) direction = 1;//напрвление вверх (положительная синусоида)
if (buffer[i] == 0) exit = 0; //пропустим поиск пика в блоке
while (exit) { //поиск пика в блоке от 0 до 0
exit = 0;//выходим
float32_t p1 = fabsf(buffer[upIdx]);//feature
if (upIdx < FRAME_SIZE) {
if (direction) {//+++++++++++++++++++++++++++++
//если условие остановило в ++ то добиваем его до 0
if ((buffer[upIdx] >= 0 && !exitYN) || (upIdx - i < wait)) {
upIdx++;
if (p1 > pikBuf) {
pikBuf = p1;
}
exit = 1;//не выходим
}
//если условие остановило в -- то добиваем его до 0 и выходим
if (((buffer[upIdx] < 0)) && (upIdx - i >= wait)) {
upIdx++;
exitYN = 1; //не дадим пред условию сработать
if (p1 > pikBuf) {
pikBuf = p1;
}
exit = 1;//не выходим
}
} else { //------------------------
//going up
//если условие остановило в -- то добиваем его до 0 и выходим
if ((buffer[upIdx] <= 0 && !exitYN) || (upIdx - i < wait)) {
upIdx++;
if (p1 > pikBuf) {
pikBuf = p1;
}
exit = 1;//не выходим
}
//если условие остановило в ++ то добиваем его до 0
if (((buffer[upIdx] > 0)) && (upIdx - i >= wait)) {
upIdx++;
exitYN = 1; //не дадим пред условию сработать
if (p1 > pikBuf) {
pikBuf = p1;
}
exit = 1;//не выходим
}
}
//добавим блок если в нем есть глобальный пик (выброс)
if (upIdx - i >= wait && max / pikBuf < agcLevel) {
if (256 + upIdx < FRAME_SIZE) wait = 256;
}
}
}//while
if (upIdx > i) { //если блок не пустой то...
float32_t locNowAgc;
//проверка на 0
if (pikBuf != 0) {
locNowAgc = max / pikBuf;//максимальное значение
} else locNowAgc = max / 0.00005f;
//типа среднее значение . исползуется для отката .
rmsPik = (rmsPik + pikBuf) / 2.0f;
//agcLevel == текущий уровень ару
if (locNowAgc < agcLevel) { // всплеск или питч V, идем вниз ..........................
stopGoingUp=0;
//заполним блок
if (i) {
for (uint16_t z = i; z <= upIdx; z++) {
buffer[z] *= locNowAgc;//применить новый коэфф
}
} else {//механизм сочленения с пред фреймом ...ждем первого нуля
if (buffer[0] > 0) direction = 1;//напрвление вверх (положительная синусоида)
exit = 1;
uint16_t idx0 = 0;
while (exit) {
exit = 0;//выходим
if (direction) {//+++++++++++++++++++++++++++++
if (buffer[idx0] > 0) {
idx0++;
exit = 1;//не выходим
}
} else { //------------------------
//going up
if (buffer[idx0] < 0) {
idx0++;
exit = 1;//не выходим
}
}
}//while
for (uint16_t z = 0; z <= upIdx; z++) {
if (z <= idx0)buffer[z] *= agcLevel;//применить старый коэфф
else buffer[z] *= locNowAgc;//применить новый коэфф
}
}
if ((pit2< pit1 && pit1 > pikBuf ) ) {//смотрим следующий питч если
agcLevel -= (agcLevel - locNowAgc) / 5;
} else {
// agcLevel = locNowAgc;
agcLevel -= (agcLevel - locNowAgc) / 2;
}
if ((pit2< pit1 && pit1 < pikBuf )) {//смотрим следующий питч если
// agcLevel = locNowAgc;
agcLevel -= (agcLevel - locNowAgc) / 1;
}
pit2 = pit1;
pit1 = pikBuf;
tim = 30;//задежка отпускания
// }
} else {//идем вверх/////////////////////////////////////////////////////////////////////
float32_t cc ;
if (tim == 0) {//подождали и начинаем
cc = agcLevel + (locNowAgc - agcLevel) / 600;
//проверим можем нет еще откатить
if (rmsFrame * cc < max - 0.0f && !stopGoingUp) {//дельта отката 0.4
agcLevel = cc;
} else stopGoingUp=1; //остановить откат
}
//заполним полупериод
for (uint16_t z = i; z <= upIdx; z++) {
buffer[z] *= agcLevel;//применить новый коэфф
}
}
i = upIdx; // след цикл будет стратовать от upIdx
} else { //это ситуация редкая но тоже надо . сделано по классике одного замера
if (nowAgc < agcLevel) { // всплеск V
agcLevel -= (agcLevel - nowAgc) / 2;
// tim = 10;//задежка отпускания
}
float32_t f = a * agcLevel;
//контроль вылета в out
if (f > max) {//out
if (buffer[i] >= 0) buffer[i] = max;//max
else buffer[i] = -max;//max
// agcLevel = 1;
} else
buffer[i] *= agcLevel;
}
if (a > agcMetrTemp) agcMetrTemp = a; //s-meter макс V
}//for
rmsFrame=(rmsFrame+rmsPik)/2.0f; // глобальный меж фреймовый средничок нужен для отката до какого-то уровня.
agcMetr = agcMetrTemp; //s-meter
}
15 ноября 2020 г. в 06:58#149
вот алгоритм убирания питчиков
if ((pit2< pit1 && pit1 > pikBuf ) ) {//смотрим следующий питч если
agcLevel -= (agcLevel - locNowAgc) / 5;
} else {
agcLevel -= (agcLevel - locNowAgc) / 2;
}
if ((pit2< pit1 && pit1 < pikBuf )) {//смотрим следующий питч если
agcLevel -= (agcLevel - locNowAgc) / 1;
}
pit2 = pit1;
pit1 = pikBuf;
pit1 pit2 - это глобальные амплитуды всплесков, которые требуют занижать амплитуду
agcLevel - самая главная переменная в ару !
pikBuf - текущий пик
locNowAgc - значение лимиттера по которому был опущен пик
if ((pit2< pit1 && pit1 > pikBuf ) ) {//смотрим следующий питч если
agcLevel -= (agcLevel - locNowAgc) / 5;
} else {
agcLevel -= (agcLevel - locNowAgc) / 2;
}
if ((pit2< pit1 && pit1 < pikBuf )) {//смотрим следующий питч если
agcLevel -= (agcLevel - locNowAgc) / 1;
}
pit2 = pit1;
pit1 = pikBuf;
pit1 pit2 - это глобальные амплитуды всплесков, которые требуют занижать амплитуду
agcLevel - самая главная переменная в ару !
pikBuf - текущий пик
locNowAgc - значение лимиттера по которому был опущен пик
15 ноября 2020 г. в 07:08#150
Очень много контроля и самое главное был косяк: пропуск применения главного множителя, отсюда и сигнал ломался. Сейчас тоже проскакиваю нестыковки. Скорей всего межфреймомые дела. И достаточно редко. И ару ОЧЕНЬ не любит низкие частоты 150Гц и ниже. Запустил музыку и ару посчитало, что низы - это ступенька. 
