ARDUINO Цветомузыка на Arduino. Обсуждение проекта

technotrasher

★★★★✩✩✩
14 Ноя 2019
508
227
все таки частотный вход нужен. :) посмотрите https://oshwlab.com/technotrasher/colormusic вот такой вариант. я там сделал пока универсально. думаю есть лишнее.

можно попросить этот кусок кода?
я уже просил. там на Фурье... нужно переписывать. хотя может оно и лучше. библиотека более корректная.
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
К примеру для того чтобы возможно было получить 10кГц вам необходимо иметь частоту семплирования не менее удвоенной масимальной частоты, т.е. это будет 20кГц, именно с этой частотой вы (образно, не вы конкретно, а программист реализующий задачу) обязан опрашивать аналоговый вход. При этом счет идет на микросекунды и задержки там микросекундные. Далее - чем больше семплов соберете, тем ниже будет граница для НЧ, т.е. - мало семплов - диапазон узкий, много - шире чем когда мало. Диапазон можно подвинуть уменьшив частоту семплирования, тем самым сдвинувшись в сторону низких частот, но потеряв в высоких. Либо же диапазон можно расширить за счет того, что собрать бОльшее количество семплов. Но много вы на ардуине не соберете, т.к. памяти не хватит. Еще одним вариантом будет смешанное семплирование - т.е. набираете два массива - один с частотой опроса 20кГц, другой с 2кГц - так можно будет посчитать отдельно высокие и низкие. Но затем нужно все объединить.
Ну и главное - кроме того что вы получили массив исходных данных (т.е. допустим заполнили массив в 256 значений прочитанных из АЦП) вам нужно его еще обсчитать - для этого нужны будут вспомогательные вещественные массивы 2 штуки по 256 значений, ну и временные затраты на обсчет всего этого дела. Вот как-то так...

Задача реализуемая вообще-то, но насколько хорошо именно на ардуине - я хз, особенно учитывая что по хорошему задача семплирование-обсчет-семплирование-обсчет должно крутиться постоянно, чтобы отзывчивость была достаточная, при этом желательно минимум 10 обсчетов в секунду неплохо было бы иметь. Сухим остатком получается то, что полноценный анализ частот делать на ардуине если и можно, то скорее всего это будет чуть ли не единственная задача, которая на ней будет крутиться полностью отъедая память и процессорное время, при иных раскладах вам придется идти на компромиссы.

Все написанное выше касалось именно задаче разложения частот, для анализа же амплитуды сигнала - все просто и проблем нет вообще.
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
я уже просил. там на Фурье... нужно переписывать. хотя может оно и лучше. библиотека более корректная.
Ну вообще я использую Хартли. Исходники доступны, лежат в гите. Могу тут конечно продублировать основную часть, мне не сложно, только это будет много текста. Лучше поглядите самостоятельно - файлы micFFT.h и micFFT.cpp - основная часть с семплированием и обработкой.

можно попросить этот кусок кода?
C++:
void MICWORKER::calibrate()
{
  if(!_isCaliblation){
    // начальный вход в калибровку
    for(uint16_t i=0; i<samples; i++){
      vImag[i] = 0;
    }
    _isCaliblation = true;
  }

  // читаем сырые данные
  for(uint16_t i=0; i<samples; i++){
    vReal[i] = analogRead(MIC_PIN); // ESP8266 Analog Pin ADC0 = A0
  }

  double sum=0, count=0;
  for (uint16_t i=0;i<samples;i++) {
    double cnt=vReal[i];
    sum+=(double)vReal[i] * cnt;
    count+=cnt;
  }
  //LOG(print(F("dbgAVG Count="));LOG(print(count);LOG(print(F(", sum="));LOG(print(sum);LOG(print(F(", sum/count="));LOG(println, sum/count);

  double average = sum/count;
  double sumSq=0;
  count=0;
  for (uint16_t i=0;i<samples;i++) {
    double cnt=vReal[i];
    double a = sq(((double)vReal[i]-average)) * cnt;
    sumSq+=a;
    count+=cnt;
  }
  double D = sumSq / count; // dispersion
  if(D>500) return; // слишком большой разброс, не включаем данный замер...
  //sqrt(D); // standard error
  //LOG(print(F("dispersion="));LOG(print(D);LOG(print(F(", standard error="));LOG(println, sqrt(D));

  uint16_t step; // где мы сейчас
  for(step=0; step<samples/2; step++){ // делим на 2 диапазона AVG+stderr
    if(vImag[step]==0.0){
      vImag[step] = sum/count; // среднее
      vImag[samples/2+step] = sqrt(D); // среднеквадратическое отклонение
      return; // после первого найденного - на выход
    }
  }

  if(step==samples/2){ // массив заполнен, окончательный рассчет
    _isCaliblation = false;
    double sum=0, count=0, sum2=0, count2=0;
    for (uint16_t i=0;i<samples/2;i++) {
      double cnt=vImag[i];
      sum+=(double)vImag[i] * cnt;
      count+=cnt;

      double cnt2=vImag[samples/2+i];
      sum2+=(double)vImag[samples/2+i] * cnt2;
      count2+=cnt2;
    }
#ifdef ESP8266
    scale = 512.0*count/sum; // смещение
#else
    scale = 2048.0*count/sum; // смещение
#endif
    noise = (sum2/count2)*scale; //+/- единиц шума
    LOG(print, F("AVG=")); LOG(print, sum/count); LOG(print, F(", noise=")); LOG(println, sum2/count2);
  }
}
только учтите, калибровка работает уже с предварительно полученными значениями, а за это отвечает другая часть программы
а, не вру, тут просто переиспользуются уже имеющиеся массивы, а чтение идет свое внутрях, при этом без выдерживания таймингов, а просто как есть :)


Дам пояснения по поводу логики работы самой калибровки, поскольку она неочевидная ни разу))).
Процедура calibrate() вызывается множество раз с заданной периодичностью до тех пор пока она не наберет достаточно данных для принятия решения о текущем уровне нуля и шума. Затем эти данные отдаются в главную программу, фрагмент кода который за это отвечает:

C++:
#ifdef MIC_EFFECTS
void LAMP::micHandler()
{
  static uint8_t counter=0;
  if(effects.getEn()==EFF_ENUM::EFF_NONE)
    return;
  if(mw==nullptr && !iflags.isCalibrationRequest){ // обычный режим
    mw = new MICWORKER(mic_scale,mic_noise);
    if(!mw) {
      mw=nullptr;
      return; // не удалось выделить память, на выход
    }
    //delete mw; mw = nullptr; return;
    
    samp_freq = mw->process(noise_reduce); // возвращаемое значение - частота семплирования
    last_min_peak = mw->getMinPeak();
    last_max_peak = mw->getMaxPeak();

    if(!counter) // раз на N измерений берем частоту, т.к. это требует обсчетов
      last_freq = mw->analyse(); // возвращаемое значение - частота главной гармоники
    if(iflags.micAnalyseDivider)
      counter = (counter+1)%(0x01<<(iflags.micAnalyseDivider-1)); // как часто выполнять анализ
    else
      counter = 1; // при micAnalyseDivider == 0 - отключено

    // EVERY_N_SECONDS(1){
    //   LOG(println, counter);
    // }

    //LOG(println, last_freq);
    //mw->debug();
    delete mw;
    mw = nullptr;
  } else if(iflags.isCalibrationRequest) {
    if(mw==nullptr){ // калибровка начало
      mw = new MICWORKER();
      mw->calibrate();
    } else { // калибровка продолжение
      mw->calibrate();
    }
    if(!mw->isCaliblation()){ // калибровка конец
      mic_noise = mw->getNoise();
      mic_scale = mw->getScale();
      iflags.isCalibrationRequest = false; // завершили
      delete mw;
      mw = nullptr;

      remote_action(RA::RA_MIC, NULL);
    }
  }
}
#endif
 

Slenk

★★★★★★✩
21 Янв 2020
382
591
34
Краснодар
@kDn, два доп. массива я так понимаю хранят в себе коэффициенты для фильтрации/усиления каждой частоты в отдельности?
"до тех пор пока она не наберет достаточно данных для принятия решения " у Вас там "нейронная сеть" получается?) Круто)
 

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632
Сдаётся мне, что я быстрее дождусь микросхему MSGEQ7 и заточу код под неё.
Это наверное самый правильный вариант. Я не думаю, что то все, что описал kDn, можно реализовать в 2КБ ОЗУ.
 
Изменено:

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
@kDn, два доп. массива я так понимаю хранят в себе коэффициенты для фильтрации/усиления каждой частоты в отдельности?
"до тех пор пока она не наберет достаточно данных для принятия решения " у Вас там "нейронная сеть" получается?) Круто)
Вообще массивы
vReal и vImag - как раз те, которые используются для преобразования, но т.к. я под них память уже выделяю при конструировании объекта работы с микрофоном, то использую несколько не по назначению. Ну т.е. раз место уже выделено, то чего ему даром пропадать... Что же касается самого принципа оценки уровня шума, то просто делаю замеры 10 раз в секунду, при массиве в 256 семплов, будет 128 замеров т.е. около 12 секунд на калибровку. Отбрасываться будут только те замеры которые сильно выбиваются от среднего уровня. Мало ли, кто чихнул во время калибровки.
Цель всего этого дела вычислить два значения - текущий уровень шума и коэффициент компенсации нуля (т.е. на сколько сдвинуть измеренное значение ближе к центру диапазона). Т.е. допустим среднее значение в случае тишины ожидается 512, а по факту получаем 400, следовательно коэф. будет 1.285, 400*1.285=512 и далее любой входной сигнал домножается на этот коэффициент до того как будет впоследствии обсчитан.
 

Slenk

★★★★★★✩
21 Янв 2020
382
591
34
Краснодар
В общем психанул и выпилил половину кода, отвечающего за "уровни")), а тот что остался - малость переписал))
Выпилил "псевдо стерео", ибо в данной конфигурации его надо переписывать, а мне лень.
Выпилил "вольюм-сеттинг", ибо сомнительный костыль.
Опять подкрутил режим 8, постарался сделать так, что бы он именно "пики" отслеживал и отключил от него один фильтр, который актуален в "уровнях" но тут мешает.
Поправил "подсветку в тишине". Что бы сохранить режим - 4 раза "ОК". Сохраняет сразу все настройки (подрежим, скорость, насыщенность, яркость и т.д.). Сохраняет в энергонезависимую память и никак не влияет на "активные" режимы.
Добавил пару строчек, что бы прошивка более менее нормально работала при малом количестве светодиодов. Пробовал компилировать на 10 (меньше уже не стал, ибо у меня это и так 7 сантиметров), всё вроде работает, в том числе "анализатор спектра".

П.С. Можно считать это Бетой. Выкладываю сейчас и сюда потому что имею предчувствие, что со дня на день меня плотно схватят за кукарели и скажут работу работать, а не фигней страдать)) И в любой момент я просто могу потеряться на несколько недель или месяцев, но там дальше видно будет.
 

Вложения

Slenk

★★★★★★✩
21 Янв 2020
382
591
34
Краснодар
А может уже "переехать" на ЕSP8266/32 ?
были мысли, но лично для меня оставаться до последнего на ардуине - это своего рода вызов. Пытаться впихнуть невпихуемое) И максимально оптимизировать код)
 

Diman

★★✩✩✩✩✩
20 Апр 2019
312
72
@kostyamat, В данном проекте для стерео входа какой диапазон частот способна обработать мега328 с учетом ограничений?
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
Это применительно к стерео варианту или для моно тоже всё так плохо?
Если взять 64 семпла, то расход памяти (для float) будет 4*2*64=512байт, если задать частоту семплирования 20кГц, то разложением можно выделить гармоники из диапазона 312.5Гц...10кГц, если этого достаточно - то можно делать. Если нужно больше высоких - будет в ущерб низким, если нужно больше низких - в ущерб высоким. И то и другое получить в широком диапазоне не особо выйдет, т.к. памяти не хватит, в два захода можно тоже делать - отдельно высокие считать, отдельно низкие - но это усложнение кода и за счет быстродействия, т.к. пересчет тоже не так чтобы бесплатная операция и время контроллера жрет.

Зачем раскладывать на частоты стерео мне вообще не сильно ясно. Уж лучше амплитудное брать из стерео, а гармоники всеже из моно.
 
  • Лойс +1
Реакции: Diman

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632
@Diman, вон выше написали, добавьте к этому буфер светодиодов 3байта на каждый, переменные ядра и библиотек, а так же самих эффектов. Микросхема msgeq7 имхо лучшее решение, без вариантов. Если для ардуино. Не зря все забугорцы именно ее применяют в своих поделках.
 

Diman

★★✩✩✩✩✩
20 Апр 2019
312
72
@kDn, Я категорически против стерео. Просто ранее была дискуссия по этому вопросу.

@kDn, По частотам думаю лучше сместить к низким. Например от 150Гц до 6 или 8кГц.
 
  • Лойс +1
Реакции: novvel

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
@kDn, По частотам думаю лучше сместить к низким. Например от 150Гц до 6 или 8кГц.
Вообще формула для расчета нижней границы простая - нужно взять частоту семплирования и поделить на кол-во семплов.
Т.е. для 6кГц частота семплирования 12кГц, следовательно 12000/64=187.5Гц, только вот я не помню - для FFT достаточно полупериода или нужен обязательно полный... Если полный то минимальную границу нужно домножить на 2.
 
  • Лойс +1
Реакции: Diman

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632
@technotrasher, принцип разумной достаточности подсказывает, что msgeq7 + arduino Nano дешевле, ну или столько же, а главное более надёжное решение, чем Мега. Памяти то там больше, но 8-ми битный ЦП на 16МГц никто отменить не может. Соответственно тормозная реакция на управление, и на звук, тоже никуда не уйдет.
 

Dinozavr

★✩✩✩✩✩✩
25 Ноя 2020
24
11
@Slenk, Прошивка 2.6.5 у меня не заработала. Вернул 2.6 работает хорошо. В изменении плотности ленты особой разницы не заметил, при любой плотности красиво(100 светодиодов, 60 шт. на метр). И изменение общей яркости ленты по моему мнению ни к чему не приводит что 255, что 40 разницы в яркости не заметно. И вопрос цвет подсветки в цму 3, 5 полос можно изменить на всю длину ленты пурпурный?
 
Изменено:

novvel

★★★✩✩✩✩
29 Сен 2018
568
192
В случае микрофона - если речь идет о max9814 - эта постоянная составляющая уже есть. Конденсатор последовательно выступает как фильтр высоких частот, какая частота среза - нужно считать исходя из используемой емкости и эквивалентного сопротивления входа, т.к. явного резистора нет. Ввиду того что на картинке я вижу разброс 1...100мкФ, то это мне подсказывает, что конденсатор впихнут отболды, без понимания для чего он вообще там нужен. ИМХО. :)

Ну а я в лампе попросту микрофон напрямую подключаю к входу контроллера, смещение затем компенсируется значением которое было получено при калибровке (т.е. замер контроллером уровня сигнала при тишине, усреднение с отбрасыванием всплесков, далее это и будет считаться нулем). Т.к. для ардуины калибровку добавлять накладно будет, то это либо отдельной программой можно сделать, либо попросту забить гвоздями (дефайном) рассчитав самостоятельно исходя из сигнала и входного делителя на АЦП. :)
В анализаторе я это и пытался сделать - железно замерил и вписал значение в код и формулу, хз только правильно или нет) но работает оно нормально после этого.

@Slenk, поставил бету твою, сначала при включении все висело и черная лента, потыкал пульт и завелось, в общем 8 режим бегущие точки стал как то чуть плавнее что ли и бежит до конца ленты теперь (возможно это из-за 1 режима плотности, но раньше они не добегали и нормально гасли на 1/8 ленты где то с конца ), больше изменений не заметил. В 9 как то более стабильно стало тоже или мне кажется, как то мелкие уровни громкости лучше отрабатывают что ли...
 
Изменено: