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

227
LINES

< > BotCompany Repo | #1033245 // ContinuousOscillators [dev.]

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

Libraryless. Click here for Pure Java version (11208L/67K).

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

Author comment

Began life as a copy of #1032970

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1033245
Snippet name: ContinuousOscillators [dev.]
Eternal ID of this version: #1033245/84
Text MD5: aaff9cf861799e1a08918cea099f79b7
Transpilation MD5: 697ec689cfcf22ffc1a4243c942e8541
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 03:23:13
Source code size: 7291 bytes / 227 lines
Pitched / IR pitched: No / No
Views / Downloads: 209 / 444
Version history: 83 change(s)
Referenced in: [show references]