Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

199
LINES

< > BotCompany Repo | #1032970 // PianoSampler [dev.]

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Compilation Failed (11097L/66K).

sclass PianoSampler {
  macro dprint { if (debug) printVars } bool debug;
    
  int channels = 1;
  double sampleRate = 48000;
  double currentSample;
  IQuerySound sound;
  IAudioSample audio;
  SlidingWindow window;
  
  long haarFeaturesChecked;

  persistable class Oscillator extends HasKey<Frequency> {
    double currentPeriodStart;
    // all recorded intensities
    new TreeMap<DoubleRange, Channels<Complex>> intensities;
    double interval; // in samples
    
    *(Frequency f) {
      super(f);
      interval = frequency().interval()*sampleRate;
    }
    
    Frequency frequency() { ret key(); }
    double interval() { ret interval; }
    
    void record_impl(DoubleRange r, Channels<Complex> c) {
      intensities.put(r, c);
    }
    
    void advancePeriodToRange(DoubleRange timeRange) {
      double end = currentPeriodStart + interval;
      int phaseShifts = ifloor((timeRange.start-end)/interval);
      if (phaseShifts = 0) ret;
      setStart(currentPeriodStart + phaseShifts*interval);
    }
    
    // we span a total range of interval*1.25 because of the sin/cos thing
    void measure(DoubleRange timeRange) {
      DoubleRange p1 = doubleRangeWithLength(phaseMinus90(), interval);
      p1 = intersectDoubleRanges(p1, timeRange);
      double re = 0;
      if (!isEmpty(p1)) {
        AudioHaarFeature haar = new(audio, p1);
        re = haar!;
      }
      
      DoubleRange p2 = doubleRangeWithLength(currentPeriodStart, interval);
      p2 = intersectDoubleRanges(p2, timeRange);
      double im = 0;
      if (!isEmpty(p2)) {
        AudioHaarFeature haar = new(audio, p2);
        im = haar!;
      }
      
      record(this, p1 /*TODO*/, complexChannels(re, im));
      haarFeaturesChecked += 2;
    }
    
    void dropMeasurementsLeftOf(double t) {
      while ping (true) {
        var r = firstKey(intensities);
        if (r != null && r.end() <= t)
          popFirst(intensities);
        else
          break;
      }
    }
    
    void initial(double currentSample) {
      setStart(currentSample + startPhase());
    }
    
    void moveTo(double time) {
      currentPeriodStart = time;
      double next = phase360();
      dprint("moveTo", +this, +next);
      completionsForSample.put(next, this);
    }
    
    double startPhase() { ret phase90(); }
    
    double quarterPhase() { ret interval()*.25; }
    
    double phaseMinus90() { ret currentPeriodStart - interval()*.25; }
    double phase0() { ret currentPeriodStart; }
    double phase90() { ret currentPeriodStart + interval()*.25; }
    double phase180() { ret currentPeriodStart + interval()*.5; }
    double phase270() { ret currentPeriodStart + interval()*.75; }
    double phase360() { ret currentPeriodStart + interval(); }
    double phase450() { ret currentPeriodStart + interval()*1.25; }
  } // end of Oscillator
  
  TreeHasKeyMap<Frequency, Oscillator> oscillators = new TreeHasKeyMap<Frequency, Oscillator>(map(f -> new Oscillator(f), pianoFrequencies88()));
  new TreeMultiMap<Double, Oscillator> completionsForSample;
  
  *() {}
  *(IAudioSample audio, double *currentSample) { setAudio(audio); }
  *(IAudioSample audio) { setAudio(audio); } // start at time 0
  
  void setAudio(IAudioSample audio) {
    this.audio = audio;
    window = optCast SlidingWindow(audio);
  }
  
  void verboseOnRecordAdded() {
    onRecordAdded(lambda1 print);
  }
  
  int nFrequencies() {
    ret oscillators.size();
  }
  
  void startOscillators {
    for (o : oscillators)
      o.initial(currentSample);
  }
  
  void stepTo(DoubleRange timeRange) {
    while ping (true) {
      Double t = firstKey(completionsForSample);
      if (t != null && t <= timeRange.start) {
        var list = completionsForSample.getAndClear(t);
        dprint(+t, +list);
        for (Oscillator o : list) {
          o.measure(timeRange);
          o.relocate(t);
        }
      } else
        break;
    }
  }
  
  void record(Oscillator o, DoubleRange r, Channels<Complex> c) {
    if (o == null) ret;
    o.record_impl(r, c);
    onRecordAdded(new RecordAdded(o, r, c));
  }
  
  record RecordAdded(Oscillator oscillator, DoubleRange period, Channels<Complex> intensity) {}
  
  transient event interface OnRecordAdded as onRecordAdded shipping RecordAdded;
  
  Frequency lowestFrequency() { ret oscillators.firstKey(); }
  Frequency highestFrequency() { ret oscillators.lastKey(); }
  
  int minWindowSize() {
    ret iceil(sampleRate*lowestFrequency().interval());
  }
  
  void makeSmallestWindow(IQuerySound sound) {
    this.sound = sound;
    setAudio(new SlidingWindow(channels, sampleRate, sound, 0, minWindowSize());
  }
  
  double windowSize() {
    ret window.length();
  }
  
  S stats() { ret renderVars(+audio, +haarFeaturesChecked); }
  
  IIntegralImage imageColumn(DoubleRange timeRange) {
    int n = nFrequencies();
    var l = reversedList(oscillators);
    double[] col = new[n];
    for y to n: {
      var l2 = l.get(y).intensities;
      new Average avg;
      forEach(l2, (r, val) -> {
        double amplitude = val.first().abs();
        
        // if only a part is in the time range
        double actualDuration = l(intersectDoubleRanges(r, timeRange));
        
        avg.add(amplitude, actualDuration);
      });
      col[y] = avg!;
    }
    double[] col2 = normalizeDoubles(col, 255);
      
    ret bwIntegralImageFromFunction(1, n, (x, y) -> ifloor(col2[y]));
  }
  
  // how much horizontal area have we covered
  L<Double> coverageByFreq() {
    ret mapReversed(oscillators, o -> totalLengthOfDoubleRanges(keys(o.intensities)));
  }
  
  void stepRight(double shift) {
    assertNotNull(+window);
    var r = window.bounds();
    window.shiftRight(max(1, ifloor(shift)));

    for (o : oscillators) o.dropMeasurementsLeftOf(window.start());
    stepTo(window.end());
  }
  
  DoubleRange windowBounds() { ret window.bounds(); }
}

download  show line numbers  debug dex  old transpilations   

Travelled to 4 computer(s): bhatertpkbcr, ekrmjmnbrukm, mowyntqkapby, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1032970
Snippet name: PianoSampler [dev.]
Eternal ID of this version: #1032970/58
Text MD5: 16206552e3383e53a76f46517a272ed0
Transpilation MD5: b1d20b360e5c5cceb4e23436d38322ed
Author: stefan
Category: javax / audio analysis
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-10-11 15:53:09
Source code size: 6085 bytes / 199 lines
Pitched / IR pitched: No / No
Views / Downloads: 207 / 494
Version history: 57 change(s)
Referenced in: [show references]