Libraryless. Compilation Failed (11097L/66K).
1 | sclass PianoSampler { |
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 | |
11 | long haarFeaturesChecked; |
12 | |
13 | persistable class Oscillator extends HasKey<Frequency> { |
14 | double currentPeriodStart; |
15 | // all recorded intensities |
16 | new TreeMap<DoubleRange, Channels<Complex>> intensities; |
17 | double interval; // in samples |
18 | |
19 | *(Frequency f) { |
20 | super(f); |
21 | interval = frequency().interval()*sampleRate; |
22 | } |
23 | |
24 | Frequency frequency() { ret key(); } |
25 | double interval() { ret interval; } |
26 | |
27 | void record_impl(DoubleRange r, Channels<Complex> c) { |
28 | intensities.put(r, c); |
29 | } |
30 | |
31 | void advancePeriodToRange(DoubleRange timeRange) { |
32 | double end = currentPeriodStart + interval; |
33 | int phaseShifts = ifloor((timeRange.start-end)/interval); |
34 | if (phaseShifts = 0) ret; |
35 | setStart(currentPeriodStart + phaseShifts*interval); |
36 | } |
37 | |
38 | // we span a total range of interval*1.25 because of the sin/cos thing |
39 | void measure(DoubleRange timeRange) { |
40 | DoubleRange p1 = doubleRangeWithLength(phaseMinus90(), interval); |
41 | p1 = intersectDoubleRanges(p1, timeRange); |
42 | double re = 0; |
43 | if (!isEmpty(p1)) { |
44 | AudioHaarFeature haar = new(audio, p1); |
45 | re = haar!; |
46 | } |
47 | |
48 | DoubleRange p2 = doubleRangeWithLength(currentPeriodStart, interval); |
49 | p2 = intersectDoubleRanges(p2, timeRange); |
50 | double im = 0; |
51 | if (!isEmpty(p2)) { |
52 | AudioHaarFeature haar = new(audio, p2); |
53 | im = haar!; |
54 | } |
55 | |
56 | record(this, p1 /*TODO*/, complexChannels(re, im)); |
57 | haarFeaturesChecked += 2; |
58 | } |
59 | |
60 | void dropMeasurementsLeftOf(double t) { |
61 | while ping (true) { |
62 | var r = firstKey(intensities); |
63 | if (r != null && r.end() <= t) |
64 | popFirst(intensities); |
65 | else |
66 | break; |
67 | } |
68 | } |
69 | |
70 | void initial(double currentSample) { |
71 | setStart(currentSample + startPhase()); |
72 | } |
73 | |
74 | void moveTo(double time) { |
75 | currentPeriodStart = time; |
76 | double next = phase360(); |
77 | dprint("moveTo", +this, +next); |
78 | completionsForSample.put(next, this); |
79 | } |
80 | |
81 | double startPhase() { ret phase90(); } |
82 | |
83 | double quarterPhase() { ret interval()*.25; } |
84 | |
85 | double phaseMinus90() { ret currentPeriodStart - interval()*.25; } |
86 | double phase0() { ret currentPeriodStart; } |
87 | double phase90() { ret currentPeriodStart + interval()*.25; } |
88 | double phase180() { ret currentPeriodStart + interval()*.5; } |
89 | double phase270() { ret currentPeriodStart + interval()*.75; } |
90 | double phase360() { ret currentPeriodStart + interval(); } |
91 | double phase450() { ret currentPeriodStart + interval()*1.25; } |
92 | } // end of Oscillator |
93 | |
94 | TreeHasKeyMap<Frequency, Oscillator> oscillators = new TreeHasKeyMap<Frequency, Oscillator>(map(f -> new Oscillator(f), pianoFrequencies88())); |
95 | new TreeMultiMap<Double, Oscillator> completionsForSample; |
96 | |
97 | *() {} |
98 | *(IAudioSample audio, double *currentSample) { setAudio(audio); } |
99 | *(IAudioSample audio) { setAudio(audio); } // start at time 0 |
100 | |
101 | void setAudio(IAudioSample audio) { |
102 | this.audio = audio; |
103 | window = optCast SlidingWindow(audio); |
104 | } |
105 | |
106 | void verboseOnRecordAdded() { |
107 | onRecordAdded(lambda1 print); |
108 | } |
109 | |
110 | int nFrequencies() { |
111 | ret oscillators.size(); |
112 | } |
113 | |
114 | void startOscillators { |
115 | for (o : oscillators) |
116 | o.initial(currentSample); |
117 | } |
118 | |
119 | void stepTo(DoubleRange timeRange) { |
120 | while ping (true) { |
121 | Double t = firstKey(completionsForSample); |
122 | if (t != null && t <= timeRange.start) { |
123 | var list = completionsForSample.getAndClear(t); |
124 | dprint(+t, +list); |
125 | for (Oscillator o : list) { |
126 | o.measure(timeRange); |
127 | o.relocate(t); |
128 | } |
129 | } else |
130 | break; |
131 | } |
132 | } |
133 | |
134 | void record(Oscillator o, DoubleRange r, Channels<Complex> c) { |
135 | if (o == null) ret; |
136 | o.record_impl(r, c); |
137 | onRecordAdded(new RecordAdded(o, r, c)); |
138 | } |
139 | |
140 | record RecordAdded(Oscillator oscillator, DoubleRange period, Channels<Complex> intensity) {} |
141 | |
142 | transient event interface OnRecordAdded as onRecordAdded shipping RecordAdded; |
143 | |
144 | Frequency lowestFrequency() { ret oscillators.firstKey(); } |
145 | Frequency highestFrequency() { ret oscillators.lastKey(); } |
146 | |
147 | int minWindowSize() { |
148 | ret iceil(sampleRate*lowestFrequency().interval()); |
149 | } |
150 | |
151 | void makeSmallestWindow(IQuerySound sound) { |
152 | this.sound = sound; |
153 | setAudio(new SlidingWindow(channels, sampleRate, sound, 0, minWindowSize()); |
154 | } |
155 | |
156 | double windowSize() { |
157 | ret window.length(); |
158 | } |
159 | |
160 | S stats() { ret renderVars(+audio, +haarFeaturesChecked); } |
161 | |
162 | IIntegralImage imageColumn(DoubleRange timeRange) { |
163 | int n = nFrequencies(); |
164 | var l = reversedList(oscillators); |
165 | double[] col = new[n]; |
166 | for y to n: { |
167 | var l2 = l.get(y).intensities; |
168 | new Average avg; |
169 | forEach(l2, (r, val) -> { |
170 | double amplitude = val.first().abs(); |
171 | |
172 | // if only a part is in the time range |
173 | double actualDuration = l(intersectDoubleRanges(r, timeRange)); |
174 | |
175 | avg.add(amplitude, actualDuration); |
176 | }); |
177 | col[y] = avg!; |
178 | } |
179 | double[] col2 = normalizeDoubles(col, 255); |
180 | |
181 | ret bwIntegralImageFromFunction(1, n, (x, y) -> ifloor(col2[y])); |
182 | } |
183 | |
184 | // how much horizontal area have we covered |
185 | L<Double> coverageByFreq() { |
186 | ret mapReversed(oscillators, o -> totalLengthOfDoubleRanges(keys(o.intensities))); |
187 | } |
188 | |
189 | void stepRight(double shift) { |
190 | assertNotNull(+window); |
191 | var r = window.bounds(); |
192 | window.shiftRight(max(1, ifloor(shift))); |
193 | |
194 | for (o : oscillators) o.dropMeasurementsLeftOf(window.start()); |
195 | stepTo(window.end()); |
196 | } |
197 | |
198 | DoubleRange windowBounds() { ret window.bounds(); } |
199 | } |
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: | 287 / 600 |
Version history: | 57 change(s) |
Referenced in: | [show references] |