Beatmup
signal_fragment.cpp
Go to the documentation of this file.
1 /*
2  Beatmup image and signal processing library
3  Copyright (C) 2019, lnstadrum
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "signal_fragment.h"
20 #include <math.h>
21 
22 using namespace Beatmup;
23 using namespace Audio;
24 
25 /**
26  Measures dynamics from samples for a single channel in a multiplexed stream
27  \param startSample Pointer to the first sample the measurement starts from
28  \param stopSample Pointer to the last sample where the measurement stops
29  \param stride Step size in samples
30  \param min Discovered minima value; the passed value is not reset
31  \param max Discovered maxima value; the passed value is not reset
32 */
33 template<typename sample> inline void measureMultiplexedChannelDynamics(
34  const sample* startSample,
35  const sample* stopSample,
36  const unsigned char stride,
37  sample& min,
38  sample& max
39 ) {
40  const sample *in = startSample;
41  sample vMin = *in, vMax = *in;
42  in += stride;
43  while (in < stopSample) {
44  vMin.x = std::min(vMin.x, in->x);
45  vMax.x = std::max(vMax.x, in->x);
46  in += stride;
47  }
48  min.x = std::min(min.x, vMin.x);
49  max.x = std::max(max.x, vMax.x);
50 }
51 
52 
54  if (prev) {
55  delete prev;
56  prev = nullptr;
57  }
58  if (minmax)
59  free(minmax);
60  size = 0;
61 }
62 
63 
64 void SignalFragment::DynamicsLookup::configureTree(unsigned char channelCount, int levelCount, int fineStepSize, int coarserStepSize) {
65  this->channelCount = channelCount;
66  if (levelCount == 1) {
67  // fine level
68  if (prev)
69  delete prev;
70  stepTime = step = fineStepSize;
71  prev = nullptr;
72  }
73  else if (levelCount > 0) {
74  if (!prev)
75  prev = new DynamicsLookup();
76  step = coarserStepSize;
77  prev->configureTree(channelCount, levelCount - 1, fineStepSize, coarserStepSize);
78  stepTime = step * prev->stepTime;
79  }
80 }
81 
82 
83 template<typename sample> inline void SignalFragment::DynamicsLookup::updateTree(const sample* data, int sampleCount) {
84  // if there is a finer scale ...
85  if (prev) {
86  // ... send sample data to it recursively
87  prev->updateTree(data, sampleCount);
88  // update the current level: reallocate table first
89  int newSize = ceili(prev->size, step);
90  if (newSize != size) {
91  minmax = realloc(minmax, newSize * 2 * channelCount * sizeof(sample));
92  size = newSize;
93  }
94  // fill it then
95  sample *out = (sample*)minmax;
96  const int skip = 2 * channelCount;
97  for (int block = 0; block < prev->size; block += step) {
98  const int blockSize = std::min(step, prev->size - block);
99  // channel multiplexing
100  for (int ch = 0; ch < channelCount; ch++) {
101  sample *in = (sample*)prev->minmax + block * skip + 2 * ch, *stop = in + blockSize * skip;
102  sample vMin = in[0], vMax = in[1];
103  in += skip;
104  while (in < stop) {
105  vMin = std::min(vMin, in[0]);
106  vMax = std::max(vMax, in[1]);
107  in += skip;
108  }
109  *(out++) = vMin;
110  *(out++) = vMax;
111  }
112  }
113  }
114  // update finest scale level: reallocate table first
115  else {
116  int newSize = ceili(sampleCount, step);
117  if (newSize != size) {
118  minmax = realloc(minmax, newSize * 2 * channelCount * sizeof(sample));
119  size = newSize;
120  }
121  // fill it then
122  sample *out = (sample*)minmax;
123  for (int time = 0; time < sampleCount; time += step) {
124  const int blockSize = std::min(step, sampleCount - time);
125  const sample *start = data + time * channelCount, *stop = start + blockSize * channelCount;
126  // channel demultiplexing
127  for (int ch = 0; ch < channelCount; ch++) {
128  sample min{ sample::MAX_VALUE }, max{ sample::MIN_VALUE };
130  out[0] = min;
131  out[1] = max;
132  out += 2;
133  }
134  }
135  }
136 }
137 
142 
143 
144 template<typename sample> void SignalFragment::DynamicsLookup::measure(dtime time0, dtime time1, sample* min, sample* max, const void* data) const {
145  int b0, b1; // bounding block indices
146  // if there is a finer level, take the inset and ask for the remaining parts this finer level
147  if (prev) {
148  b0 = ceili(time0, stepTime);
149  b1 = time1 / stepTime;
150  dtime
151  t0 = b0 * stepTime,
152  t1 = b1 * stepTime;
153  if (t0 >= t1) {
154  // this level is too coarse
155  prev->measure<sample>(time0, time1, min, max, data);
156  return;
157  }
158 
159  if (time0 < t0)
160  prev->measure<sample>(time0, t0, min, max, data);
161  if (t1 < time1)
162  prev->measure<sample>(t1, time1, min, max, data);
163  }
164 
165  // if not, but if there is some sample data, take an inset and measure leftovers using the sample data
166  else if (data) {
167  b0 = ceili(time0, stepTime);
168  b1 = time1 / stepTime;
169  // if there are some blocks (more than one)
170  if (b0 < b1) {
171  dtime
172  t0 = b0 * stepTime,
173  t1 = b1 * stepTime;
174  if (time0 < t0 || t1 < time1) {
175  sample *pMin = min, *pMax = max;
176  for (int ch = 0; ch < channelCount; ch++, pMin++, pMax++) {
177  if (time0 < t0)
179  (sample*)data + time0 * channelCount + ch,
180  (sample*)data + t0 * channelCount + ch,
181  channelCount,
182  *pMin, *pMax
183  );
184  if (t1 < time1)
186  (sample*)data + t1 * channelCount + ch,
187  (sample*)data + time1 * channelCount + ch,
188  channelCount,
189  *pMin, *pMax
190  );
191  }
192  }
193  }
194  // the time borders are both within the same block
195  else {
196  for (int ch = 0; ch < channelCount; ch++, min++, max++)
198  (sample*)data + time0 * channelCount + ch,
199  (sample*)data + time1 * channelCount + ch,
200  channelCount,
201  *min, *max
202  );
203  return;
204  }
205  }
206 
207  // otherwise take the outset
208  else {
209  b0 = time0 / stepTime;
210  b1 = std::max(b0+1, ceili(time1, stepTime));
211  }
212 
213  BEATMUP_ASSERT_DEBUG(b1 <= size);
214 
215  // scan selected block set
216  const int skip = 2 * channelCount;
217  for (int chSkip = 0; chSkip < skip; chSkip += 2) {
218  sample *ptr = (sample*)minmax + skip*b0 + chSkip, *stop = (sample*)minmax + skip*b1 + chSkip;
219  while (ptr < stop) {
220  min->x = std::min(ptr[0].x, min->x);
221  max->x = std::max(ptr[1].x, max->x);
222  ptr += skip;
223  }
224  min++;
225  max++;
226  }
227 }
228 
229 template void SignalFragment::DynamicsLookup::measure(dtime time0, dtime time1, sample8* min, sample8* max, const void* data) const;
230 template void SignalFragment::DynamicsLookup::measure(dtime time0, dtime time1, sample16* min, sample16* max, const void* data) const;
231 template void SignalFragment::DynamicsLookup::measure(dtime time0, dtime time1, sample32* min, sample32* max, const void* data) const;
232 template void SignalFragment::DynamicsLookup::measure(dtime time0, dtime time1, sample32f* min, sample32f* max, const void* data) const;
233 
234 
236  disposeTree();
237 }
238 
239 
242 {
244  sampleCount = samples;
246 }
247 
248 
251  memcpy(copy->data(), data(), getSizeBytes());
252  copy->plot.fineLevelStep = plot.fineLevelStep;
253  copy->plot.coarserLevelStep = plot.coarserLevelStep;
254  // do not copy the lookup; it will be recomputed when needed
255  copy->plot.lookup.disposeTree();
256  return copy;
257 }
258 
259 
261  memchr(data(), 0, getSizeBytes());
262 }
263 
264 
267  channelCount,
271  );
272 
273  switch (format) {
274  case Int8:
276  break;
277  case Int16:
279  break;
280  case Int32:
282  break;
283  case Float32:
285  break;
286  default:
287  Insanity::insanity("Unknown sample format");
288  }
289 }
290 
291 
292 template<typename sample> void SignalFragment::measureDynamics(int time0, int time1, sample* min, sample* max, Signal::Meter::MeasuringMode mode) {
293  BEATMUP_ASSERT_DEBUG(time0 <= time1);
294  BEATMUP_ASSERT_DEBUG(AUDIO_SAMPLE_SIZE[format] == sizeof(sample));
295 
296  const bool useLookup =
299 
300  if (useLookup)
301  RuntimeError::check(plot.lookup.isReady(), "Dynamics lookup is not ready");
302 
303  const void* sampleData = nullptr;
304 
305  // precise measurement
306  if (
309  ) {
310  sampleData = data();
311  }
312 
313  // use lookup
314  if (useLookup)
315  plot.lookup.measure(time0, time1, min, max, (sample*)sampleData);
316 
317  // don't use lookup
318  else {
319  BEATMUP_ASSERT_DEBUG(sampleData);
320  for (int ch = 0; ch < channelCount; ch++, min++, max++)
322  (sample*)sampleData + time0*channelCount + ch,
323  (sample*)sampleData + time1*channelCount + ch,
324  channelCount,
325  *min, *max
326  );
327  }
328 }
329 
Aligned memory buffer.
Definition: memory.h:27
datatype * ptr(int offset=0)
Definition: memory.h:46
Data structure allowing to plot efficiently audio signal graphs.
void updateTree(const sample *data, int sampleCount)
Updates tree from raw sample data.
void configureTree(unsigned char channelCount, int levelCount, int fineStepSize, int coarserStepSize)
Sets up the tree structure.
DynamicsLookup * prev
previous (finer scale) level
void measure(dtime time0, dtime time1, sample *min, sample *max, const void *data) const
Measures dynamics from time0 to time1 in each channels separately.
int size
buffer size in points in one channel, i.e., size in bytes is 2*channelCount*sizeof(magnitude)*size
void * minmax
(2*size) points per channel, channel-wise multiplexed, interleaved minima and maxima
void disposeTree()
Frees the current level and all previous.
unsigned char blockSize
size in bytes of a channelwise-multiplexed sample (block containing 1 sample per channel)
SignalFragment(AudioSampleFormat format, unsigned char channels, int samples)
struct Beatmup::Audio::SignalFragment::Plot plot
void measureDynamics(int time0, int time1, sample *min, sample *max, Signal::Meter::MeasuringMode mode)
Measures dynamics from time0 to time1 in each channels separately.
unsigned char channelCount
number of channels
virtual SignalFragment * clone() const
MeasuringMode
Specifies how to compute signal dynamics (minima and maxima in a given period of time)
Definition: signal.h:76
@ approximateUsingLookup
Find an approximated range using a lookup tree.
@ preciseUsingLookupAndSamples
Use lookup and then precise the measurement using sample data.
@ preciseUsingSamples
Just run across all samples.
int sampleCount
number of samples within this frame
Definition: fragment.h:39
const int getSampleCount() const
Definition: fragment.h:44
static void insanity(const char *message)
Definition: exception.h:136
static void check(const bool condition, const std::string &message)
Definition: exception.h:64
#define BEATMUP_ASSERT_DEBUG(C)
Definition: exception.h:163
const int AUDIO_SAMPLE_SIZE[]
int dtime
discrete time
Definition: basic_types.h:37
AudioSampleFormat
Format of audio samples.
@ Int8
signed integer, 8 bit per sample
@ Int32
signed integer, 32 bit per sample
@ Float32
floating point, 32 bit per sample
@ Int16
signed integer, 16 bit per sample
CustomPoint< numeric > min(const CustomPoint< numeric > &a, const CustomPoint< numeric > &b)
Definition: geometry.h:724
CustomPoint< numeric > max(const CustomPoint< numeric > &a, const CustomPoint< numeric > &b)
Definition: geometry.h:728
void measureMultiplexedChannelDynamics(const sample *startSample, const sample *stopSample, const unsigned char stride, sample &min, sample &max)
Measures dynamics from samples for a single channel in a multiplexed stream.
#define ceili(x, y)
integer division x/y with ceiling
Definition: utils.hpp:21
JNIEnv jobject jint jint jint channels
JNIEnv jlong jint t1
Beatmup::AbstractBitmap * copy
jlong jint start
jlong jlong jint time
jobject jlong jint x
JNIEnv jlong jint out
JNIEnv jlong jint mode
jlong jobject size
jlong jint jfloat step