예제 #1
0
  private void end(boolean initAfter) {
    COLLECTING = false;

    if (model.plottingSelected()) {
      plotterYIN.stop();
      plotterYIN.setVisible(false);
      plotterMPM.stop();
      plotterMPM.setVisible(false);
      plotterBUFFER.stop();
      plotterBUFFER.setVisible(false);
    }

    showStatistics();

    model.firePropertyChange(ControllerEngine.START_STOP_PROCESSING_BUTTON_PROPERTY, "stop", "rec");
    model.firePropertyChange(ControllerEngine.INPUTLEVEL_PROPERTY, -1, 0);
    reset();

    if (initAfter && !model.isEvaluating()) model.initProcessing(null);
  }
예제 #2
0
  /** in loop: get buffer from queue and call detectPitchAndCollect() */
  public void run() {
    //		fft.start(); //TODO fft

    // System.out.println("starte collector, TIME: " +
    // (System.currentTimeMillis()-jAM.GLOBAL_TIMESTAMP) );

    if (model.plottingSelected()) {
      plotterYIN = new SimplePlotterFrame("YIN Buffer Plotter", 200);
      plotterYIN.start();
      plotterMPM = new SimplePlotterFrame("MPM", 400);
      plotterMPM.start();
      plotterBUFFER = new SimplePlotterFrame("Audio Buffer Plotter", 600);
      plotterBUFFER.start();
    }

    // detectionStartTime=System.currentTimeMillis();

    try {
      while (thread != null) {
        // long start = System.currentTimeMillis();
        // get data: block if queue is empty
        float[] audioFloatBuffer = queue.get();

        // check end-of-stream marker
        if (audioFloatBuffer == null) {
          // System.out.println("COLLECTOR:  buffer is null!");
          end(true);
          return;
        }

        double level = jAMUtils.soundPressureLevel(audioFloatBuffer);

        // TODO level (dB) to percent ?
        //				System.out.println(Math.round(1000000000*Math.pow(10,level/20))/10000000 + "%");

        countSamples += audioFloatBuffer.length - overlap;

        // ----- ok wir haben nun einen buffer aus der queue geholt
        // -----
        detectPitchAndCollect(audioFloatBuffer, level);

        // TODO doc evaluation: auf intel 2 core blabla zB 3ms fuer
        // detectPitchAndCollect() -> diesen pipeline schritt
        // System.out.println("===TIME FOR ONE STEP===> " +
        // (System.currentTimeMillis()-start) + "ms");
      }
      // end();
    } catch (InterruptedException e) {
      // e.printStackTrace();
      System.err.println("catched interr exc in collector.run()");
    }
  }
예제 #3
0
  /** show some statistics at the end of the collector process */
  public void showStatistics() {
    // TODO nur fuer mich die folgenden stats...haben in eval
    // erstmal! nix verloren
    String stat = "";
    if (!model.isEvaluating()) {
      stat += "\n****************************** START STATS ******************************\n";
      stat += "!ALL! DETECTED NOTES AND THEIR LENGTHS IN ms: (just for stats)\n";
      stat += midiKeySammlerInsgesamt.toString() + "\n\n";

      stat += "AbcNotes-Backup: \n" + notesAsString + "\n";

      stat += "YIN/MPM - STATS:\n";
      float sum = yin_cnt + mpm_cnt;
      stat += "YIN: " + yin_cnt / sum * 100 + "% - MPM: " + mpm_cnt / sum * 100 + "%\n";
      stat += "****************************** END STATS ******************************\n\n";
      jAM.log(stat, false);
    } else evaluator.evaluateCurrentTranscription(evaluationSammler, PITCHDETECTOR, notesAsString);
  }
예제 #4
0
  private void detectRest(float duration) {
    float noteDur = midiKeysRests.size() * duration;

    // NE PAUSE MUSS MIND NE 16tel lang sein, sonst wird einfach ignoriert!
    int min =
        timeFor16thNote
            - MINIMUM_DURATION; // MINIMUM_DURATION; //timeFor16thNote - MINIMUM_DURATION; //
                                // -MINIMUM_DURATION;

    if (noteDur < min) {
      if (!model.isEvaluating() && jAM.SYSOUT)
        System.out.println(
            "NoteCollectorWorker: ########## IGNORED: "
                + noteDur
                + "ms OF RESTS!  -   min: "
                + min
                + " midiKeysRests: "
                + midiKeysRests);

      midiKeysRests.clear();
      return;
    }
    addNoteOrRest(0, noteDur);
  }
예제 #5
0
  private Vector<Double> getMostDetectedNoteInSequence(
      Vector<Float[]> midiKeysAndLevels, float durationOfOneNote) {
    int midiKey = 0;
    int cnt = 0;
    Vector<Double> ret = new Vector<Double>();

    float[] midiKeys = new float[midiKeysAndLevels.size()];
    double[] levels = new double[midiKeysAndLevels.size()];

    // 1. erst zŠhlen:
    int[] modeArray = new int[128]; // 128 midiKeys !

    // copy from Vector<Float[]> to float[]
    for (int i = 0; i < midiKeys.length; i++) {
      midiKeys[i] = midiKeysAndLevels.get(i)[0];
      levels[i] = Math.abs(midiKeysAndLevels.get(i)[1]); // betrag, dann nach maxima suchen
    }

    for (int i = 0; i < midiKeys.length; i++) {
      modeArray[(int) midiKeys[i]] += 1; // count
    }

    // 2. dann den Haeufigsten suchen
    for (int i = 0; i < modeArray.length; i++) {
      if (modeArray[i] > cnt) {
        cnt = modeArray[i];
        midiKey = i;
      }
    }
    //      System.out.println(midiKey + " occures " + cnt + " times");

    //      return midiKey;

    // first add taken MidiKey
    ret.add((double) midiKey);

    /**
     * minima/maxima: idea: if there are local minima in the levelVector of this notesequence then
     * we can assume that the SAME note was played more than one time!
     */
    // find extrema in RMS Levels
    boolean minima = false;
    Vector<double[]> extremwerte = jAMUtils.detectExtremum(levels, delta, minima);
    // sysout
    if (!model.isEvaluating() && extremwerte.size() > 0) {
      System.out.print("EXTREMWERTE " + (minima ? " MINIMA " : " MAXIMA ") + " ===> [");
      for (int i = 0; i < extremwerte.size(); i++) {
        System.out.print("(" + extremwerte.get(i)[0] + "," + extremwerte.get(i)[1] + "), ");
      }
      System.out.println("] durationOfOneNote: " + durationOfOneNote);
    }

    // midiKey, notevalue1, notevalue2 usw...
    //		return i.e. [60, 512, 511..]

    // then add possible durations
    if (extremwerte.size() > 0) {
      double startX = 0;
      double endX = 0;

      for (int i = 0; i < extremwerte.size(); i++) {
        endX = extremwerte.get(i)[0]; // nich beim Tiefpunkt sondern ungefaehr beim Naechsten ONSET!

        //				ret.add( durationOfOneNote * (endX-startX) );

        //				//ignoriere extremwerte am Ende, denn dies kommt sehr oft vor! also nur die ersten X%
        float X = (levels.length * 75.0f / 100);
        if (endX < X) {
          ret.add(durationOfOneNote * (endX - startX));
        }

        startX = endX; // -2;
      }

      // nur wenn mind. eine duration geaddet wurde noch zum Ende:
      if (ret.size() > 1) {
        endX = levels.length;
        ret.add(durationOfOneNote * (endX - startX));
      }
    }
    return ret;
  }
예제 #6
0
  private void addNoteOrRest(int midiKey, float noteDur) {
    // int noteLength = mapNoteDurationToBPM_8tel(noteDur);
    int noteLength = mapNoteDurationToBPM_16tel(noteDur);

    // ggf 2 Noten malen (mit bogen!) bei: 5 7 9 10 11 13 14 15 (bei 8tel:
    // 10 und 14)
    switch (noteLength) {
      case 5:
        convertToABC(midiKey, 4, midiKey == 0 ? "" : "-");
        convertToABC(midiKey, 1, "");
        break;
      case 7:
        convertToABC(midiKey, 6, midiKey == 0 ? "" : "-");
        convertToABC(midiKey, 1, "");
        break;
      case 9:
        convertToABC(midiKey, 8, midiKey == 0 ? "" : "-");
        convertToABC(midiKey, 1, "");
        break;
      case 10:
        convertToABC(midiKey, 8, midiKey == 0 ? "" : "-");
        convertToABC(midiKey, 2, "");
        break;
      case 11:
        convertToABC(midiKey, 8, midiKey == 0 ? "" : "-");
        convertToABC(midiKey, 3, "");
        break;
      case 13:
        convertToABC(midiKey, 12, midiKey == 0 ? "" : "-");
        convertToABC(midiKey, 1, "");
        break;
      case 14:
        convertToABC(midiKey, 12, midiKey == 0 ? "" : "-");
        convertToABC(midiKey, 2, "");
        break;
      case 15:
        convertToABC(midiKey, 12, midiKey == 0 ? "" : "-");
        convertToABC(midiKey, 3, "");
        break;

      default:
        convertToABC(midiKey, noteLength, "");
        break;
    }

    if (!model.isEvaluating()) model.updateScore(notesAsString);

    if (midiKey == 0) {
      if (!model.isEvaluating() && jAM.SYSOUT)
        // jAM.log("NoteCollectorWorker: ==> ENTSCHEIDUNG REST: ("+noteLength+") noteDur:
        // "+noteDur+" - based on: "
        // + midiKeysRests, false);
        System.err.println(
            timestamp()
                + " ==> ENTSCHEIDUNG REST: ("
                + noteLength
                + ") noteDur: "
                + noteDur
                + " - based on 'RESTS':"
                + midiKeysRests);
    } else {
      if (!model.isEvaluating() && jAM.SYSOUT) {
        // jAM.log("NoteCollectorWorker: ==> ENTSCHEIDUNG NOTE: "+midiKey+"("+noteLength+") noteDur:
        // "+noteDur+" - based on: "
        // + midiKeysNotes, false);
        String s = "";
        for (int i = 0; i < midiKeysAndLevels.size(); i++) {
          s += (midiKeysAndLevels.get(i)[0] + ",");
        }
        s += "\n";
        System.err.print(
            timestamp()
                + " ==> ENTSCHEIDUNG NOTE: "
                + midiKey
                + "("
                + noteLength
                + ") noteDur: "
                + noteDur
                + " - based on: "
                + s);
      }
    }
    // immer beide loeschen, sonst werden features gesammelt, welche schon vor langer zeit auftraten
    midiKeysAndLevels.clear();
    midiKeysRests.clear();

    // wir brauchen midiKey und Notenwert(zwischen 1-16)
    evaluationSammler.add(midiKey);
    evaluationSammler.add(noteLength);
  }
예제 #7
0
  /**
   * <b>This is the most important function of the collector pipeline</b> First we detect the pitch
   * of the audioFloatBuffer using YIN or MPM.<br>
   * Then the collector process starts, checking onsets and offsets based on midiKeys and dB,
   * collects<br>
   * and convert these pich vectors to abc-notes based on the bpm, samplerate buffersize and
   * bufferoverlap.<br>
   */
  private void detectPitchAndCollect(float[] audioFloatBuffer, double level) {
    int midiKey;
    String note;

    // TODO
    pitchInHertz = getBestPitch(audioFloatBuffer);

    // ---------- ENTSCHEIDUNG ----------
    // pitchInHertz=mpm_pitch;
    pitch = Pitch.getInstance(PitchUnit.HERTZ, (double) pitchInHertz);
    midiKey =
        (int)
            pitch.getPitch(
                PitchUnit.MIDI_KEY); // PitchConverter.hertzToMidiKey((double)pitchInHertz);

    if (model.plottingSelected()) {
      plotterYIN.setData(yin.getCurrentBuffer());
      plotterYIN.setInfoString("CURRENT PITCH: " + pitchInHertz + "Hz PROB: " + pitch_probability);

      plotterMPM.setData(mpm.getCurrentBuffer());
      plotterMPM.setInfoString("CURRENT PITCH: " + pitchInHertz + "Hz PROB: " + pitch_probability);

      plotterBUFFER.setData(audioFloatBuffer);
      plotterBUFFER.setInfoString("level: " + level);
    }

    // nach dem pitch erkannt wurde muss ggf. entsprechend dem Instrument
    // transponiert werden
    if (model.getTransposeRecIndex() == 1) {
      // Bb Clarinet: 2 halftonesteps up
      pitch.convertPitch(2);
      pitchInHertz = (float) pitch.getPitch(PitchUnit.HERTZ);
      midiKey = PitchConverter.hertzToMidiKey((double) pitchInHertz);
    }

    String[] arr = pitch.getBaseNote(pitchInHertz).split(" ");
    String note2 = arr[0];
    int oktave = Integer.parseInt(arr[1]);
    //
    note = pitch.noteName();
    String str = "";

    // formatted Info output to GUI
    str =
        pitchInHertz == -1
            ? "NO PITCH DETECTED"
            : note
                + " at "
                + String.format("%.5g%n", pitchInHertz)
                + "Hz - IDEAL: "
                + String.format("%.5g%n", pitch.getIdealFreq(note2, oktave))
                + "Hz - PROB: "
                + String.format("%.5g%n", pitch_probability)
                + "%";

    model.firePropertyChange(ControllerEngine.INFO_LABEL_PROPERTY, "", str);

    countMSprocessing += (audioFloatBuffer.length - overlap) / audioSampleRate * 1000.0f;

    float duration = (bufferSize - overlap) / audioSampleRate * 1000.0f;
    // finally:
    //		collect(audioFloatBuffer, midiKey, duration, level, note);

    // ------------------------- begin collector process -------------------------
    // ----- sammel ALLE erkannten Noten fuer statistiken -----
    if (midiKeySammlerInsgesamt.get(midiKey) != null) {
      midiKeySammlerInsgesamt.put(midiKey, midiKeySammlerInsgesamt.get(midiKey) + duration);
    } else {
      midiKeySammlerInsgesamt.put(midiKey, duration);
    }

    // TODO level neuer Ansatz ? zusaetzliche offset/Onset bedingung
    //		levels.add(level);
    //
    //		boolean minima=true;
    //		Vector<double[]>extremwerte = jAMUtils.detectExtremum(levels, delta, minima);
    //
    //		//sysout
    //		if(!model.isEvaluating() && extremwerte.size()==1) {  //kann so immer nur 1 finden
    ////			jAMUtils.printArray(levels);
    //			String str="";
    //			for (int i = 0; i < extremwerte.size(); i++) {
    //				str+= "(" + extremwerte.get(i)[0] + ","+extremwerte.get(i)[1] + "),";
    //			}
    //			System.out.println("============> EXTREM!!!: " + str + " at levels: " + levels);
    //			System.out.println("midiKey: " + midiKey);
    //
    //			levels.clear(); //wenn ein minima entdeckt: offset und dann leeren ?
    //		}
    /**
     * * wenn eine note zb 200ms lang, dann -> 16tel aber die 50 ms muessen beim NŠchsten Mal
     * ignoriert werden wenn dies pausen entspricht !!! sonst kann sein dass zB z(3) gemalt wird
     * obwohl z(2) !!!
     *
     * <p>if(msToIgnore>0 && !ONSET) { //MINIMUM_DURATION dur+=duration; if(dur<msToIgnore) return;
     * else { System.out.println( "============================================>>>>>> IGNORED: " +
     * dur + "ms OF DATA - msToIgnore: " + msToIgnore); dur=0; msToIgnore=0; } }
     */
    boolean silence = jAMUtils.isSilence(audioFloatBuffer, MINIMUM_LEVEL);
    // 1. OFFSET basierend auf neuer note!
    if ((ONSET && midiKey > 0 && !silence /* level > MINIMUM_LEVEL*/)
        && (midiKey != lastTakenMidiKey)) { // es kommt ne andere /Note/
      // System.out.println("POSSIBLE NEW NOTE: "+ midiKey);

      if (newNoteCount == 0) // die erste "andere" Note
      newNoteCount++;
      else if (midiKey == lastDetectedMidiKey) newNoteCount++;

      if (newNoteCount
          > (int)
              (MINIMUM_DURATION
                  / duration)) { // 60/25.01 --> 2 also mind.  3  nehmen! wie  sonst auch
        ONSET =
            false; // dann macht er jetzt unten ne Entscheidung und beim next Mal fŠngt er an die
                   // neue note zu collecten
        NEW_NOTE_ONSET = true; // TODO sinn?

        newNoteCount = 0;

        // sysout
        if (!model.isEvaluating() && jAM.SYSOUT)
          System.out.println(
              timestamp()
                  + " ================================================== OFFSET NEWNOTE: "
                  + midiKey
                  + " could be possible new note");
      }

      // 2. ONSET
    } else if (!ONSET && midiKey > 0 && !silence /*&& level > MINIMUM_LEVEL*/) {
      newNoteCount = 0;

      // gibts diese note schon? und kam sie beim letzten Mal?
      if (midiKeySammler.get(midiKey) != null && midiKey == lastDetectedMidiKey) {
        midiKeySammler.put(midiKey, midiKeySammler.get(midiKey) + duration);
        if (midiKeySammler.get(midiKey) > MINIMUM_DURATION) {
          //
          // sysout
          if (!model.isEvaluating() && jAM.SYSOUT)
            System.out.println(
                timestamp()
                    + " ================================================== ONSET - Entscheidung basiert auf: "
                    + midiKeySammler);

          ONSET = true;

          // wir kšnnen davon ausgehen dass "note" die lastTakenNote
          // wird, da sie alle Bedingungen fuer ein note-OFFSET erfŸllt
          // Problem: es kann sein dass zb 3 buffer A1 kommen, das is
          // ne new note bedingung
          // der naechst kommt dann hier rein und is aber diesmal A3
          // also lastTakenNote="A3" anstatt A1 ???
          lastTakenMidiKey = midiKey;

          midiKeySammler.clear();
        }
      } else {
        midiKeySammler.put(midiKey, duration);
      }

      // 3. OFFSET basierend auf Pause
    } else if (silence
        || midiKey
            == 0 /*|| level < MINIMUM_LEVEL*/) { // sonst ist alles ne Pause: kein pitch und auch
                                                 // level < MIN_LEVEL
      /**
       * gibts diese Pause schon? und kam sie beim letzten Mal? ob sie beim letzten Mal kam is
       * unwichtig es mŸssen die Pausen gezŠhlt werden! BSP: 60,60,60,0,0,0,0,55,0,0,0, --> die 55
       * MUSS mitgezaehlt werden ! ??? stimmt das?
       */
      if (midiKeySammler.get(0) != null && lastDetectedMidiKey == 0) {
        midiKeySammler.put(0, midiKeySammler.get(0) + duration);

        if (midiKeySammler.get(0) > MINIMUM_DURATION) {
          if (ONSET) {
            if (!model.isEvaluating() && jAM.SYSOUT)
              System.out.println(
                  timestamp()
                      + " ================================================== OFFSET - Entscheidung basiert auf: "
                      + midiKeySammler);

            ONSET = false; // jetzt ist wieder vorbei
            NEW_NOTE_ONSET = false;
          }
          midiKeySammler.clear();
        }
      } else {
        midiKeySammler.put(0, duration);
      }
    }

    // ----- wenn nun ONSET==true anfangen zu sammeln bis ONSET==false!
    if (ONSET) {
      if (midiKeysRests.size() > 0
          && !NEW_NOTE_ONSET) { // hier sind nun pausen in der off Phase gezaehlt worden
        detectRest(duration);
      } else { // sonst: sammle noten
        // hier midiKey und level speichern:
        midiKeysAndLevels.add(new Float[] {(float) midiKey, (float) level});
      }
    } else { // wenn ein OFFSET und noten sind vorhanden: entscheidung!
      if (midiKeysAndLevels.size() > 0) {
        detectNote(duration);
      } else
        // sonst sammple pausen
        midiKeysRests.add(midiKey);
    }

    lastDetectedMidiKey = midiKey;

    // TODO fft
    //		fft(audioFloatBuffer);

    // TODO sysout
    if (!model.isEvaluating() && jAM.SYSOUT) {
      if (pitchInHertz == -1)
        System.out.println(
            timestamp()
                + "\t"
                + "--> Rest: "
                + note
                + (note.length() == 2 ? "\t" : "")
                + "\t\t midiKey: "
                + midiKey
                + "\t RMS: "
                + String.format("%.2f", level)
                + "  Zustand: "
                + (ONSET ? "ONSET " : "OFFSET ")
                + (silence ? "SILENCE" : "NO SILENCE"));
      else
        System.out.println(
            timestamp()
                + "\t"
                + "--> Note: "
                + note
                + (note.length() == 2 ? "\t" : "")
                + "\t midiKey: "
                + midiKey
                + "\t RMS: "
                + String.format("%.2f", level)
                + "  Zustand: "
                + (ONSET ? "ONSET " : "OFFSET ")
                + (silence ? "SILENCE" : "NO SILENCE"));
    }
  }
예제 #8
0
  public void convertToABC(int midiKey, int notenWert, String bindeBogenSameNotes) {
    String NOTE = "";

    // wenn zB C10 erkannt oder irgendwas ausserhalb meines bereichs ->
    // Pause!
    if (midiKey < 36 || midiKey > 96) // von C2 bis C7
    NOTE = "z";
    else {
      // 1. Wenn midiKey in Tonleiter: map to right note (b ein
      // hoch(Bb->B), # ein runter(F#->F))
      boolean inTonleiter = jAMUtils.inTonleiter(midiKey, tonleiter);

      //			System.out.println("====>" + midiKey + FLAT_KEY + " - "+ inTonleiter);

      if (FLAT_KEY && inTonleiter && jAMUtils.ABC_NOTES_FLAT[midiKey].contains("_")) midiKey += 1;
      else if (!FLAT_KEY && inTonleiter && jAMUtils.ABC_NOTES_SHARP[midiKey].contains("^"))
        midiKey -= 1;

      //			System.out.println("====>" + ABC_NOTES_FLAT[midiKey].contains("_"));
      //			System.out.println("====>" + midiKey);

      // wenn nich in Tonleiter wird einfach sharp gemalt!
      NOTE = jAMUtils.ABC_NOTES_SHARP[midiKey];

      // old:
      // NOTE = FLAT_KEY ? ABC_NOTES_FLAT[midiKey] :
      // ABC_NOTES_SHARP[midiKey];
    }

    String PREFIX = ""; // zB "(" oder auch ")" dann: ")A4" kann evtl sein!
    String POSTFIX = ""; // zB ")" oder "|" oder "\n"

    lenge += notenWert;

    POSTFIX += bindeBogenSameNotes; // is entweder "" oder "-"

    // TODO stimmt das alles?
    if (lenge >= OBEN * 4 && takte < UNTEN) {
      // System.out.println("=========================================== EIN TAKT VORBEI");
      POSTFIX += "|";
      lenge = 0;
      takte++;

      model.firePropertyChange(ControllerEngine.SCROLL_DOWN_PROPERTY, -1, 100);

    } else if (lenge >= OBEN * 4 && takte == UNTEN) {
      // System.out.println("=========================================== 4 TAKTE VORBEI
      // ===========================================");
      POSTFIX += "\n";
      takte = 1;
      lenge = 0;
    }

    /*
     * imSelbenTakt(letzte UND! NOTE) && TODO ob im selben Takt wird
     * noch ignoriert!
     */
    // wenn letzte und diese unabghŠngig von #/b DIESELBE IST:
    if ((letzteNote.replace("_", "").equals(NOTE.replace("_", ""))
                || letzteNote.replace("^", "").equals(NOTE.replace("^", "")))
            &&
            // wenn letzte nun b oder # hatte und DIESE NICHT: NATURAL SIGN!
            (letzteNote.contains("^") && !NOTE.contains("^"))
        || (letzteNote.contains("_") && !NOTE.contains("_"))) PREFIX = "="; // natural sign

    notesAsString += (PREFIX + NOTE + notenWert + POSTFIX);
    letzteNote = NOTE;
  }