Самодельные электронные ударные

Тема в разделе "Arduino & Shields", создана пользователем Dan, 24 дек 2016.

  1. Dan

    Dan Гик

    всем привет
    долгое время не было возможности позаниматься ударкой и вот сегодня наконец-то я дорвался до своего этого уже отнявшего кучу времени занятия и применил к пэду пиковый детектор, взятый отсюда: http://radiokot.ru/circuit/digital/game/12/
    только я хотел обрадоваться, но звука нет вообще, никаких намеков на работу
    номиналы взял все те же, что и на схеме: http://radiokot.ru/circuit/digital/game/12/05.gif
    единственное, что там не ардуино, может кто по этому поводу что посоветовать?датчик по прежнему ЗП-3


    попробовал диоды заменить на BAT85, звук появился, но звуки все равно иногда некорректно воспроизводятся - бывает ударишь тише - выскочит громкий звук и наоборт
     
    Последнее редактирование: 14 май 2017
  2. РоманК

    РоманК Нуб

  3. EvgenBel

    EvgenBel Нуб

    Всем добрый день, подскажите, может есть готовое решение для ударки, независимой от компа (цель взять в поход и т.д.) есть что то подобное - http://diyourself.ru/technology/how-to-make-an-electronic-drum-not-a-midi-sampler.html (сам в программировании не очень силен) подскажите свое мнение как люди которые с этим уже "собаку съели"
     
  4. mcureenab

    mcureenab Гуру

  5. mcureenab

    mcureenab Гуру

  6. EvgenBel

    EvgenBel Нуб

    очень смешно
     
  7. mcureenab

    mcureenab Гуру

    Абсолютно соответствует вашим требованиям.
     
  8. Dan

    Dan Гик

    Всем привет.
    Мучался с пэдом и модулем, сделал пиковый детектор на каждый датчик, вроде бы все устраивает, но почему-то при загрузке скетча, где запрограммированы два датчика, опрашивается только один, первый, второй игнорируется.
    Вот скетч:
    Код (C++):
    #define LED 13

    #include <MIDI.h>

    MIDI_CREATE_DEFAULT_INSTANCE();

    uint16_t kick_sample_time=2; // время звучания семпла
    uint16_t kick_treshold=1; // пороговый уровень датчика
    uint16_t kick_mute_time=0; // время ожидания успокоения датчика в миллисекундах
    uint32_t kick_note_off=0; // счетчик времени звучания семпла
    uint32_t kick_mute=5; // счетчик времени успокоения датчика в микросекундах
    uint16_t kick_val=0; // предыдущее значение датчика
    uint8_t kick_note_number=38; // номер ноты в GeneralMIDI
    const uint8_t kick_pin=A0;

    uint16_t hh_sample_time=2; // время звучания семпла
    uint16_t hh_treshold=1; // пороговый уровень датчика
    uint16_t hh_mute_time=20; // время ожидания успокоения датчика в миллисекундах
    uint32_t hh_note_off=0; // счетчик времени звучания семпла
    uint32_t hh_mute=5; // счетчик времени успокоения датчика в микросекундах
    uint16_t hh_val=0; // предыдущее значение датчика
    uint8_t hh_note_number=49; // номер ноты в GeneralMIDI
    const uint8_t hh_pin=A1;

    int foto_val = 0;
    int foto_pin = A15;


    void test_midi();

    int tmp;
    int tmp_hh;
    void setup() {
      MIDI.begin();
    //  Serial.begin(31250);
      Serial.begin(115200);
     
      pinMode(kick_pin, INPUT);
      pinMode(hh_pin, INPUT);
      pinMode(LED, OUTPUT);
      pinMode(foto_pin, OUTPUT);
      digitalWrite(LED, LOW);
      // делитель 16 - частота опроса 1МГц разрешение 8 бит
      ADCSRA |= (1 << ADPS2);                     //Биту ADPS2 присваиваем единицу
      ADCSRA &= ~ ((1 << ADPS1) | (1 << ADPS0));  //Битам ADPS1 и ADPS0 присваиваем нули

      test_midi();
    }

    void noteOn(int cmd, int pitch, int velocity) {
        Serial.write( (0x8F+cmd) );
        Serial.write(pitch);
        Serial.write(velocity);
    }

    void test_midi() {
      for (int note = 0x1E; note < 0x2A; note ++) {
        noteOn(10, note, 0x45);
        delay(100);
        noteOn(10, note, 0x00);
        delay(100);
      }
    }

    void loop(){
    // ------------------------------KICK--------------------------
     
      if (kick_note_off > 0) { // здесь проверяем время звучания семпла и выключаем ноту
        if (millis() > kick_note_off) { // нота уже должна отзвучать
          kick_note_off=0;
          //noteOff
          noteOn(10, kick_note_number, 0);
        }
      }

      if (kick_mute > 0) { // здесь проверяем время затухания датчика
        if (millis() > kick_mute) { // уже должны затухнуть
          pinMode(kick_pin, INPUT);
          kick_mute = 0;
          kick_val=0;
          digitalWrite(LED, LOW);
        }
      }
      else {
        tmp = analogRead(kick_pin);
        if (tmp > kick_treshold ) { // уровень превышает порог+гистерезис
          if (tmp <= kick_val ) { // датчик пошел на спад - пик уже получен
            uint8_t velocity = map(kick_val, kick_treshold, 150, 1, 127);
            if (velocity > 127) velocity = 127; // оставил на всякий случай
            kick_note_off = millis() + kick_sample_time; // ставим таймер на время звучания семпла
            kick_mute = millis() + kick_mute_time; // время ожидания успокоения сенсора
            noteOn(10, kick_note_number, velocity); // играем ноту
            digitalWrite(LED, HIGH);
            pinMode(kick_pin, OUTPUT);
            digitalWrite(kick_pin, LOW); // придавливаем датчик чтобы быстрее успокоился
          }
          else { // сигнал сенсора растет
            kick_val = tmp; // запоминаем текущее значение для сравнения со следующим
          }
        }
      }
     
     
     
     
     
    // ------------------------------HIHAT--------------------------
     
      if (hh_note_off > 0) { // здесь проверяем время звучания семпла и выключаем ноту
        if (millis() > hh_note_off) { // нота уже должна отзвучать
          hh_note_off=0;
          //noteOff
          noteOn(10, hh_note_number, 0);
        }
      }

      if (hh_mute > 0) { // здесь проверяем время затухания датчика
        if (millis() > hh_mute) { // уже должны затухнуть
          pinMode(hh_pin, INPUT);
          hh_mute = 0;
          hh_val=0;
          digitalWrite(LED, LOW);
        }
      }
      else {
        hh_val = 0;
        tmp_hh = analogRead(hh_pin);
        if (tmp_hh > hh_treshold ) { // уровень превышает порог+гистерезис
          if (tmp_hh <= hh_val ) { // датчик пошел на спад - пик уже получен
            uint8_t velocity = map(hh_val, hh_treshold, 750, 1, 127);
            if (velocity > 127) velocity = 127; // оставил на всякий случай
            hh_note_off = millis() + hh_sample_time; // ставим таймер на время звучания семпла
            hh_mute = millis() + hh_mute_time; // время ожидания успокоения сенсора
              foto_val = analogRead(foto_pin);
              if(foto_val > 0 && foto_val <= 40)
              {
                hh_note_number = 51;
              }
              if(foto_val > 40 && foto_val <= 80)
              {
                hh_note_number = 52;
              }
              if(foto_val > 80 && foto_val <= 120)
              {
                hh_note_number = 54;
              }
              if(foto_val > 120 && foto_val <= 160)
              {
                hh_note_number = 55;
              }
              if(foto_val > 160 && foto_val <= 200)
              {
                hh_note_number = 56;
              }
              if(foto_val > 200 && foto_val <= 240)
              {
                hh_note_number = 57;
              }
              noteOn(10, hh_note_number, velocity); // играем ноту
            digitalWrite(LED, HIGH);
            pinMode(hh_pin, OUTPUT);
            digitalWrite(hh_pin, LOW); // придавливаем датчик чтобы быстрее успокоился
          }
          else { // сигнал сенсора растет
            hh_val = tmp_hh; // запоминаем текущее значение для сравнения со следующим
          }
        }
      }
    }




     
    Воспроизводится звук с датчика, подсоединенного к А0, а стучу по датчику, подключенному к А1. Не понимаю в чем косяк
     
  9. sslobodyan

    sslobodyan Гик

    О, знакомый текст :) Только в этом скетче не предусмотрена игра с нескольких датчиков. Обрабатывается только один вход. Где-то в теме был скетч на несколько входов, там номера входов в массивах задаются.
    А то, что датчик подключен к одному порту, а слышится на другом, объясняется просто. Если все другие аналоговые входы висят в воздухе, то запоминающий кондесатор на входе модуля АЦП в контроллере не изменяет свой заряд при переключении на другой вход (который в воздухе) и создается ложное впечатление о наличии сигнала (реально захваченного с предыдущего измерения на другом, подключенном, входе) на не подключенном порту.
     
    arkadyf нравится это.
  10. Dan

    Dan Гик

    Да, это с Вашей помощью я добился результата, а вот с массивами теперь будет морока, как их обрабатывать я не знаю(
    Скетч нужно поискать, однозначно
     
  11. sslobodyan

    sslobodyan Гик

    Если сделали пик-детекторы, то логика опроса (кмк) такая.
    Опрашиваем очередной вход (номера входов в массиве - не забыли?).
    Если напряжение на нем превышает порог, то обрабатываем дальше, иначе переключаемся на опрос следующего входа.
    Если предыдущее напряжение на входе было меньше (храним их в массиве для каждого входа отдельно), то запоминаем текущее состояние в это массив и переключаемся на опрос следующего входа.
    А вот если напряжение не изменилось или стало меньшим, то значит пик уже пройден и пик детектор дает нам максимальный уровень сигнала. Переключаем вход на вывод и выводим 0 для принудительной разрядки пик-детектора. А пока берем это значение сигнала, высчитываем уровень громкости и посылаем соответствующую ноту в порт. Отослали - переводим вход опять в аналоговый режим.
    Итого три массива: номера входов, уровень предыдущего сигнала или 0 если не превышен порог, номер ноты для данного канала.
    Как правило, миди секвенсоры не требуют отсылки "ноте_офф" для барабанов, за исключением принудительного гашения тарелок. Но если захотите отсылать отключение ноты, то придется завести еще два массива: 1) с длительностью звучания каждого инструмента и 2) временем, когда надо отослать"ноте_офф". В момент отсылки "ноте_он" в массив 2 пишете сумму текущего millis() и времени звучания из массива 1. А в главном цикле постоянно смотрите в этот массив 2 и если там не 0 и записано время, меньшее текущего millis(), то обнуляете значение и шлете "ноте_офф". Глушение тарелки таким образом происходит с помощью записи в массив 2 текущего millis() - при первой же проверке канала тарелок будет отослана команда "ноте_офф".
    Удачи в программировании :)
     
    arkadyf нравится это.
  12. Dan

    Dan Гик

    Это вот примерно вот этот скетч по Вашему посту?

    Код (C++):
    /*

      MIDI ударная установка
      sslobodyan@ya.ru 2017
    */


    #define LED 13

    #define PIEZ_CNT 16 // количество датчиков
    #define DRUM_CHANNEL 9

    #define ANOTHER_MUTE_PIN_
    #define SHOW_

    // пороговый уровень датчика
    int kick_treshold[PIEZ_CNT]={50,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150};

    // время на остановку колебаний
    long kick_mute_time[PIEZ_CNT]={60000,50000,50000,50000,50000,50000,50000,50000,50000,50000,50000,50000,50000,50000,50000,50000};

    // ноты на каналах
    byte kick_note[PIEZ_CNT]={35,36,37,38,39,40,40,40,40,40,40,40,40,40,40,40};

    // канал молчит
    bool mute[PIEZ_CNT]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};

    // ножка датчика
    const uint8_t kick_pin[PIEZ_CNT]={A0,A1,A2,A3,A4,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1};

    #ifdef ANOTHER_MUTE_PIN
      const uint8_t stop_pin[PIEZ_CNT]={A0,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1};
    #endif

    int kick_val[PIEZ_CNT]; // предыдущее значение датчика
    int velocity;

    uint32_t tm[PIEZ_CNT], tm_led;
    byte cnt=0, piez_idx=0;

    void noteOn(int chan, int note, int velocity) {
        Serial.write( (0x90 | chan) );
        Serial.write(note);
        Serial.write(velocity);
    }

    void noteOff(int chan, int note, int velocity) {
        Serial.write( (0x80 | chan) );
        Serial.write(note);
        Serial.write(velocity);
    }

    void channelVolume(int chan, int velocity) {
        Serial.write( (0xb0 | chan) );
        Serial.write( 0x07 );
        Serial.write(velocity);
    }

    void channelPan(int chan, int pan) {
        Serial.write( (0xb0 | chan) );
        Serial.write( 0x0a );
        Serial.write(pan);
    }

    void channelBank(int chan, int dat) {
        Serial.write( (0xB0 | chan) );
        Serial.write( 0x00 );
        Serial.write( dat );
    }

    void channelInstrum(int chan, int dat) {
        Serial.write( (0xC0 | chan) );
        Serial.write( dat );
    }

    void setup() {
      //Serial.begin(115200); // эмулятор МИДИ на компе
      Serial.begin(115200); // реальный МИДИ
      pinMode(LED, OUTPUT);
      digitalWrite(LED, LOW);
      for (byte i=0; i<PIEZ_CNT; i++) {
        digitalWrite(kick_pin[i], LOW);
        pinMode(kick_pin[i], INPUT);
    #ifdef ANOTHER_MUTE_PIN  
        digitalWrite(stop_pin[i], LOW);
        pinMode(stop_pin[i], INPUT);  
    #endif  
      }
      channelBank(1, 0);
      channelInstrum(1, DRUM_CHANNEL);
      channelVolume(1, 70);

      // делитель 16 - частота опроса 1МГц разрешение 8 бит
      ADCSRA |= (1 << ADPS2);                     //Биту ADPS2 присваиваем единицу
      ADCSRA &= ~ ((1 << ADPS1) | (1 << ADPS0));  //Битам ADPS1 и ADPS0 присваиваем нули
    }


    void loop(){

        if(piez_idx>0) piez_idx--; else piez_idx=PIEZ_CNT-1; // выбор следующего датчика
        int adc = analogRead(kick_pin[piez_idx]);
     
        if (adc > kick_treshold[piez_idx]) {
          if ( tm[piez_idx] == 0 ){
            tm[piez_idx] = micros() + 2000;
            kick_val[piez_idx] = 0;
            digitalWrite(LED, HIGH);
            tm_led = millis()+10;
          }
        }

        if (tm_led > 0) {
          if (millis() > tm_led) {
            tm_led = 0;
            digitalWrite(LED, LOW);
          }
        }
         
        if ( tm[piez_idx] > 0 ) {  
          if ( micros() > tm[piez_idx] ) {
            if ( mute[piez_idx] ) {
              mute[piez_idx] = false;
              velocity = map(kick_val[piez_idx], kick_treshold[piez_idx], 1023, 1, 127);
              if (velocity > 127) velocity = 127;
              noteOn(DRUM_CHANNEL, kick_note[piez_idx], velocity); // играем ноту
              tm[piez_idx] += kick_mute_time[piez_idx];
              #ifdef ANOTHER_MUTE_PIN
                pinMode(stop_pin[piez_idx], OUTPUT); // разряжаем кондер  
              #else
                pinMode(kick_pin[piez_idx], OUTPUT); // разряжаем кондер  
              #endif
              #ifdef SHOW
                cnt++;
                velocity = kick_val[piez_idx];
                Serial.print("IDX="); Serial.print(piez_idx);
                Serial.print(" Vel="); Serial.print(velocity);
                Serial.print(" cnt="); Serial.println(cnt);
              #endif
            }
          }
          if ( kick_val[piez_idx] < adc ) {
            kick_val[piez_idx] = adc; // ищем максимум сигнала от датчика
          }
          if ( micros() >  tm[piez_idx] ) {
            // закончили сканирование одного удара
            tm[piez_idx] = 0;
            mute[piez_idx] = true;
            // отключаем разрядку кондера
            #ifdef ANOTHER_MUTE_PIN
              pinMode(stop_pin[piez_idx], INPUT);
            #else
              pinMode(kick_pin[piez_idx], INPUT);
            #endif      
          }
        }

    }
     
  13. sslobodyan

    sslobodyan Гик

    Не совсем. Массивы там есть, но нет глушения нот "ноте_офф" и вместо поиска максимума по пик-детектору просто ждем некоторое время и считываем значение. Проверьте, может оно и взлетит. Я в железе не проверял, так что возможны ньюансы.
     
  14. Dan

    Dan Гик

    И еще, я так понимаю, нужно в начале кода подключить МИДИ-библиотеку?
    Вообщем, я повожусь с этим скетчем, потестю, отпишусь)
     
  15. sslobodyan

    sslobodyan Гик

    не нужно. в моем коде уже прописаны все миди-посылки - это обычные посылки в сериал. будете подключать в комп - не забудьте согласовать скорости.
     
  16. Dan

    Dan Гик

    скорость порта я уже поправил на ту, что у меня сейчас есть
     
  17. Neu

    Neu Нерд

    Интересная тема! Dan'у и причастным лицам - желаю удачи, все получиться :)!
    Можно мне, как наблюдающему со стороны нубу, задать вопрос: а что если подойти к проблеме эмулятора ударных чуток с другой стороны? Вот есть же на компе разные VST плагины, типа Abbey Road Modern Drums. И ими можно барабанить на обычной клавиатуре. Вроде даже сила и скорость нажатия имеет значение (но могу ошибаться, не помню).
    Если в пэде вместо пьезы будет кнопка (или что-то подобное), а ардуинка будет просто эмулировать нажатие клавиатуры, то удастся добиться эффекта электронных ударных, как считаете?
     
  18. Dan

    Dan Гик

    Можно, это уже будет похоже на миди клавиатуру. Но мне принципиально именно, чтобы была имитация настоящей акустической ударной установки, потому что как музыканту хочется освоить инструмент, а на настоящие ударные нет ни денег, ни помещения.

    .P.S. Спасибо за поддержку)
     
  19. Neu

    Neu Нерд

    Да, пожалуй... Проку, наверное, от такой эмуляции будет немного. Разве что моторику пытаться развивать.
     
  20. Dan

    Dan Гик

    Это кому как удобно просто, кто-то меньше заморачивается и требования другие к конструкции, я решил выбрать вариант "похардкорнее". А так то для записи вообще все программно делается и не нужна ни клавиатура, ни ударные, только VST-плагин и умение им пользоваться.
    Кстати, минус клавиатуры в том, что не будет динамики - звук либо есть и максимально возможной громкости, либо его нет, промежуточной громкости не получится добиться