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

230
LINES

< > BotCompany Repo | #1033271 // ContinuousOscillators_v2 - with deleting recognized frequencies [dev.]

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

Transpiled version (11198L) is out of date.

1  
// TODO: declare audio ended so we can ignore any incomplete bass frequencies
2  
3  
sclass ContinuousOscillators_v2 extends Meta {
4  
  macro dprint { if (debug) printVars } bool debug;
5  
    
6  
  settable bool bassFirst;
7  
  settable bool printSubtractions;
8  
  int channels = 1;
9  
  settable double sampleRate = 48000;
10  
  double currentSample;
11  
  IAudioSample audio;              // integrated audio sample
12  
  SubtractedAudio subtractedAudio; // audio with deleted frequencies
13  
  settable bool keepRendered;
14  
  transient double[] rendered;
15  
  
16  
  bool renderUsingSine; // or square wave
17  
  bool realOnly, imagOnly; // for testing
18  
  double maxRelFreq = 0.5; // maximum oscillator frequency relative to sample rate
19  
  
20  
  event escalating(Oscillator o);
21  
22  
  long haarFeaturesChecked;
23  
24  
  persistable class Oscillator extends HasKey<Frequency> {
25  
    double currentPeriodStart;
26  
    // all recorded intensities
27  
    new TreeMap<DoubleRange, Channels<Complex>> intensities;
28  
    double interval; // length of period in samples
29  
    
30  
    *(Frequency f) {
31  
      super(f);
32  
      interval = frequency().interval()*sampleRate;
33  
    }
34  
    
35  
    Frequency frequency() { ret key(); }
36  
    double interval() { ret interval; }
37  
    
38  
    scaffolded void record_impl(DoubleRange r, Channels<Complex> c) {
39  
      intensities.put(r, c);
40  
      subtractMeasured(r, c);
41  
      currentPeriodStart = phase360();
42  
      if (!bassFirst) escalate();
43  
    }
44  
    
45  
    void escalate {
46  
      escalating(this);
47  
      rcallF(oscillators.higher(this), o -> { o?.process(currentPeriodStart); });
48  
    }
49  
    
50  
    void subtractMeasured(DoubleRange r, Channels<Complex> c) {
51  
      renderer from method;
52  
      if (printSubtractions) {
53  
        print("subtract " + r + " = " + c);
54  
        enableScaffolding(renderer);
55  
      }
56  
      renderer.subtractPeriodFrom(subtractedAudio, r, frequency(), c.getSingleton());
57  
      renderer().addPeriodTo(rendered, r, frequency(), c.getSingleton());
58  
    }
59  
    
60  
    scaffolded void process(double upTo) {
61  
      //printVars(+phase360(), +upTo);
62  
      while (phase360() <= upTo)
63  
        measure();
64  
      subtractedAudioSanityCheck();
65  
      escalate();
66  
    }
67  
    
68  
    scaffolded void measure() {
69  
      DoubleRange p1 = doubleRangeWithLength(currentPeriodStart, interval);
70  
      AudioHaarFeature haar = new(relevantAudio(), p1);
71  
      haarFeaturesChecked += 2;
72  
      record(this, p1, haar.getComplex());
73  
    }
74  
    
75  
    double phase360() { ret currentPeriodStart + interval(); }
76  
  } // end of Oscillator
77  
  
78  
  IAudioSample relevantAudio() { ret subtractedAudio; }
79  
  
80  
  TreeHasKeyMap<Frequency, Oscillator> oscillators;
81  
82  
  *() {}
83  
  *(IAudioSample audio, double *currentSample) { setAudio(audio); }
84  
  *(IAudioSample audio) { setAudio(audio); } // start at time 0
85  
86  
  void setFrequencies(Iterable<Frequency> frequencies) {
87  
    oscillators = new TreeHasKeyMap<Frequency, Oscillator>(map(f -> new Oscillator(f), filter(frequencies,
88  
      f -> between(f!, 1, sampleRate*maxRelFreq))));
89  
  }
90  
  
91  
  void setAudio(IAudioSample audio) {
92  
    this.audio = audio;
93  
    subtractedAudio = new SubtractedAudio(audio);
94  
    if (keepRendered) rendered = new double[iceil(audio.length())];
95  
    assertEquals("bounds", audio.bounds(), subtractedAudio.bounds());
96  
  }
97  
  
98  
  int nFrequencies() { ret oscillators.size(); }
99  
  
100  
  scaffolded void stepTo(DoubleRange timeRange) {
101  
    var o = first(oscillators);
102  
    o?.process(timeRange.end);
103  
  }
104  
  
105  
  void record(Oscillator o, DoubleRange r, Channels<Complex> c) {
106  
    if (o == null) ret;
107  
    o.record_impl(r, c);
108  
  }
109  
  
110  
  Frequency lowestFrequency() { ret oscillators.firstKey(); }
111  
  Frequency highestFrequency() { ret oscillators.lastKey(); }
112  
  
113  
  int minWindowSize() {
114  
    ret iceil(sampleRate*lowestFrequency().interval());
115  
  }
116  
  
117  
  S stats() { ret renderVars(+audio, +haarFeaturesChecked); }
118  
  
119  
  // how much horizontal area have we covered
120  
  L<Double> coverageByFreq() {
121  
    ret mapReversed(oscillators, o -> totalLengthOfDoubleRanges(keys(o.intensities)));
122  
  }
123  
  
124  
  IIntegralImage imageColumn(DoubleRange timeRange) {
125  
    int n = nFrequencies();
126  
    double[] col = rawImageColumn(timeRange);
127  
    double[] col2 = normalizeDoubles(col, 255);
128  
    ret bwIntegralImageFromFunction(1, n, (x, y) -> ifloor(col2[y]));
129  
  }
130  
  
131  
  double[] rawImageColumn(DoubleRange timeRange) {
132  
    int n = nFrequencies();
133  
    var l = reversedList(oscillators);
134  
    double[] col = new[n];
135  
    for y to n: {
136  
      TreeMap<DoubleRange, Channels<Complex>> l2 = l.get(y).intensities;
137  
      
138  
      // find right period
139  
      DoubleRange period = l2.floorKey(timeRange);
140  
      if (period == null) continue;
141  
      
142  
      double amplitude = l2.get(period).first().abs();
143  
      col[y] = amplitude;
144  
    }
145  
    
146  
    ret col;
147  
  }
148  
  
149  
  MakesBufferedImage image(DoubleRange r, double pixelsPerSecond) {
150  
    new L<IIntegralImage> images;
151  
    double len = r.length()/sampleRate;
152  
    int pixels = iround(len*pixelsPerSecond);
153  
    for i to pixels: {
154  
      DoubleRange range = doubleRange(
155  
        i/pixelsPerSecond*sampleRate+r.start(),
156  
        (i+1)/pixelsPerSecond*sampleRate+r.start());
157  
      //print(+range);
158  
      images.add(imageColumn(range));
159  
    }
160  
    
161  
    ret mergeBWImagesHorizontally(map toBWImage(images), spacing := 0);
162  
  }
163  
  
164  
  double[] toAudio(IntRange sampleRange, Iterable<Oscillator> oscillatorsToRender default oscillators) {
165  
    double[] samples = new[l(sampleRange)];
166  
    
167  
    class PerFreq {
168  
      Oscillator o;
169  
      DoubleRange period;
170  
      Channels<Complex> intensity;
171  
172  
      *(Oscillator *o) {
173  
        setPeriod(o.intensities.floorKey(toDoubleRange(sampleRange)));
174  
        //printVars("PerFreq", f := o.frequency(), +sampleRange, +period, firstPeriod := firstKey(o.intensities));
175  
      }
176  
      
177  
      void setPeriod(DoubleRange period) {
178  
        this.period = period;
179  
        intensity = period == null ?: o.intensities.get(period);
180  
      }
181  
      
182  
      void nextPeriod(double t) {
183  
        while (period != null && t >= period.end)
184  
          setPeriod(o.intensities.higherKey(period));
185  
      }
186  
    }
187  
    
188  
    L<PerFreq> perFreqs = map(oscillatorsToRender, o -> new PerFreq(o));
189  
    
190  
    for i over samples: {
191  
      double t = sampleRange.start+i; // t is time in samples
192  
      double sum = 0;
193  
      for (pf : perFreqs) {
194  
        pf.nextPeriod(t);
195  
        bool shouldPrint = toAudio_shouldPrint(i, l(samples));
196  
        
197  
        if (pf.intensity != null) {
198  
          sum += renderer().renderPeriodSample(pf.period, pf.o.frequency(), pf.intensity.first(), t, shouldPrint);
199  
        } else
200  
          if (shouldPrint)
201  
            printVars(f := pf.o.frequency(), +i, interval := pf.o.interval, +t, c := 0);
202  
        
203  
      }
204  
      
205  
      samples[i] = sum;
206  
    }
207  
  
208  
    ret samples;
209  
  }
210  
  
211  
  RenderFrequencySample renderer() {
212  
    ret renderUsingSine
213  
      ? new RenderFrequencySample_Sine
214  
      : new RenderFrequencySample_SquareWave;
215  
  }
216  
217  
  swappable bool toAudio_shouldPrint(int i, int n) { false; }
218  
  
219  
  void subtractedAudioSanityCheck() {
220  
    if (rendered == null) ret;
221  
    int n = l(rendered);
222  
    for i to n: {
223  
      reimmutable i;
224  
      assertDoublesVeryClose_warnOnly(-> "Sample " + i,
225  
        subtractedAudio.sampleSum(0, i, i+1),
226  
        audio.sampleSum(0, i, i+1)-rendered[i],
227  
        1e-5);
228  
    }
229  
  }
230  
}

Author comment

Began life as a copy of #1033245

download  show line numbers  debug dex  old transpilations   

Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1033271
Snippet name: ContinuousOscillators_v2 - with deleting recognized frequencies [dev.]
Eternal ID of this version: #1033271/52
Text MD5: 938ebeefa64e2df5833e25c076b7364a
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-18 11:56:37
Source code size: 7388 bytes / 230 lines
Pitched / IR pitched: No / No
Views / Downloads: 174 / 378
Version history: 51 change(s)
Referenced in: [show references]