SAL
A C++ library for spatial audio.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
audiobuffer.h
Go to the documentation of this file.
1 /*
2  Spatial Audio Library (SAL)
3  Copyright (c) 2013-2018, Enzo De Sena
4  All rights reserved.
5 
6  Authors: Enzo De Sena, enzodesena@gmail.com
7  */
8 
9 #ifndef SAL_AUDIOBUFFER_H
10 #define SAL_AUDIOBUFFER_H
11 
12 
13 #include "saltypes.h"
14 #include "pointwiseop.h"
15 #include "vectorop.h"
16 #include "digitalfilter.h"
17 #include <iostream>
18 
19 namespace sal {
20 
21 using mcl::Int;
22 
23 
24 class MonoBuffer;
25 
26 class Buffer {
27 public:
30  num_channels_(num_channels), num_samples_(num_samples),
31  owns_data_(true), temporary_vector_(std::vector<Sample>(num_samples, 0.0)) {
32  ASSERT(num_channels >= 0 && num_samples >= 0);
33  AllocateMemory();
34  }
35 
36  Buffer() : Buffer(0,0) {}
37 
47  Buffer(Sample** data_referenced,
48  const Int num_channels, const Int num_samples) noexcept :
49  num_channels_(num_channels), num_samples_(num_samples), owns_data_(false),
50  temporary_vector_(std::vector<Sample>(num_samples, 0.0)) {
51  data_ = data_referenced;
52  }
53 
54 
55  virtual Int num_channels() const noexcept { return num_channels_; }
56 
57  virtual Int num_samples() const noexcept { return num_samples_; }
58 
59  inline Sample GetSample(Int channel_id, Int sample_id) const noexcept {
60  ASSERT(channel_id >= 0 && channel_id < num_channels());
61  ASSERT(sample_id >= 0 && sample_id < num_samples());
62 
63  return data_[channel_id][sample_id];
64  }
65 
66  bool IsDataOwner() const noexcept { return owns_data_; }
67 
68  inline void SetSample(const Int channel_id, const Int sample_id,
69  const Sample sample) noexcept {
70  ASSERT(channel_id >= 0 && channel_id < num_channels());
71  ASSERT(sample_id >= 0 && sample_id < num_samples());
72 
73  data_[channel_id][sample_id] = sample;
74  }
75 
83  void SetSamples(const Int channel_id, const Int from_sample_id,
84  const Int num_samples, const Sample* samples) noexcept {
85  ASSERT(channel_id >= 0 && channel_id < num_channels());
86 
87  ASSERT(from_sample_id >= 0);
88  ASSERT(num_samples >= 0);
89  ASSERT((from_sample_id+num_samples) <= this->num_samples());
90 
91  for (Int sample_id=from_sample_id;
92  sample_id<(from_sample_id+num_samples);
93  ++sample_id) {
94  data_[channel_id][sample_id] = samples[sample_id-from_sample_id];
95  }
96  }
97 
98  void SetSamples(const Buffer& other) noexcept {
99  ASSERT(num_samples_ == other.num_samples_);
100  ASSERT(num_channels_ == other.num_channels_);
101  for (int chan_id=0; chan_id<num_channels_; ++chan_id) {
102  for (int sample_id=0; sample_id<num_samples_; ++sample_id) {
103  data_[chan_id][sample_id] = other.data_[chan_id][sample_id];
104  }
105  }
106  }
107 
115  void AddSamples(const Int channel_id,
116  const Int from_sample_id,
117  const Int num_samples,
118  const Sample* samples) noexcept {
119  ASSERT(channel_id >= 0 && channel_id < num_channels());
120  ASSERT(from_sample_id >= 0);
121  ASSERT(num_samples >= 0);
122  ASSERT((from_sample_id+num_samples) <= num_samples_);
123 
124  mcl::Add(samples,
125  &(data_[channel_id][from_sample_id]), num_samples,
126  &(data_[channel_id][from_sample_id]));
127  }
128 
131  void MultiplyAddSamples(const Int channel_id,
132  const Int from_sample_id,
133  const Int num_samples,
134  const Sample* samples,
135  const Sample constant) noexcept {
136  ASSERT(channel_id >= 0 && channel_id < num_channels());
137  ASSERT(from_sample_id >= 0);
138  ASSERT(num_samples >= 0);
139  ASSERT((from_sample_id+num_samples) <= num_samples_);
140  mcl::MultiplyAdd(samples, constant, &(data_[channel_id][from_sample_id]),
141  num_samples, &(data_[channel_id][from_sample_id]));
142  }
143 
144  void FilterAddSamples(const Int channel_id,
145  const Int from_sample_id,
146  const Int num_samples,
147  const Sample* samples,
148  mcl::DigitalFilter& filter) noexcept {
149  ASSERT(channel_id >= 0 && channel_id < num_channels());
150  ASSERT(from_sample_id >= 0);
151  ASSERT(num_samples >= 0);
152  ASSERT((from_sample_id+num_samples) <= num_samples_);
153  filter.Filter(samples, num_samples, temporary_vector_.data());
154  mcl::Add(temporary_vector_.data(),
155  &(data_[channel_id][from_sample_id]), num_samples,
156  &(data_[channel_id][from_sample_id]));
157  }
158 
159 
160  const Sample* GetReadPointer(const Int channel_id) const noexcept {
161  ASSERT(channel_id >= 0 && channel_id < num_channels());
162  return data_[channel_id];
163  }
164 
165  Sample* GetWritePointer(const Int channel_id) noexcept {
166  ASSERT(channel_id >= 0 && channel_id < num_channels());
167  return data_[channel_id];
168  }
169 
170  Sample** GetWritePointers() noexcept { return data_; }
171 
176  virtual void AddSamples(const Buffer& buffer) noexcept {
177  ASSERT(num_channels() == buffer.num_channels());
178  ASSERT(num_samples() == buffer.num_samples());
179 
180  for (Int chan_id = 0; chan_id<num_channels(); ++chan_id) {
181  mcl::Add(GetReadPointer(chan_id),
182  buffer.GetReadPointer(chan_id),
183  num_samples(),
184  GetWritePointer(chan_id));
185  }
186  }
187 
188  void SetFrame(const Int channel_id,
189  const Int frame_id,
190  const Int frame_length,
191  const Signal& signal) {
192  for (mcl::Int n=0; n<num_samples(); ++n) {
193  mcl::Int index = frame_id*frame_length + n;
194  if (index < (mcl::Int) signal.size()) {
195  SetSample(channel_id, n, signal[index]);
196  } else {
197  SetSample(channel_id, n, 0.0);
198  }
199  }
200  }
201 
202 
203  void PrintData() {
204  for (int chan_id=0; chan_id<num_channels_; ++chan_id) {
205  for (int sample_id=0; sample_id<num_samples_; ++sample_id) {
206  std::cout<<data_[chan_id][sample_id]<<" ";
207  }
208  std::cout<<std::endl;
209  }
210  }
211 
213  virtual void Reset() noexcept {
214  for (Int chan_id = 0; chan_id<num_channels(); ++chan_id) {
215  for (Int sample_id = 0; sample_id<num_samples(); ++sample_id) {
216  data_[chan_id][sample_id] = 0.0;
217  }
218  }
219  }
220 
225  };
226 
227 
228  Buffer(const Buffer& other) :
229  num_channels_(other.num_channels_), num_samples_(other.num_samples_),
230  owns_data_(other.owns_data_),
231  temporary_vector_(std::vector<Sample>(other.num_samples(), 0.0)) {
232  if (owns_data_) {
233  AllocateMemory();
234  SetSamples(other);
235  } else {
236  data_ = other.data_;
237  }
238  }
239 
245  Buffer& operator=(const Buffer& other) {
246  if (this != &other) {
247  if (owns_data_ && other.data_ == data_) { return *this; }
248 
249  if (owns_data_) { DeallocateMemory(); }
250 
251  num_channels_ = other.num_channels_;
252  num_samples_ = other.num_samples_;
253  owns_data_ = other.owns_data_;
254  temporary_vector_ = std::vector<Sample>(other.num_samples(), 0.0);
255 
256  if (owns_data_) {
257  AllocateMemory();
258  SetSamples(other);
259  } else {
260  data_ = other.data_;
261  }
262  }
263  return *this;
264  }
265 
266  virtual ~Buffer() {
267  if (owns_data_) { DeallocateMemory(); }
268  }
269 
270  static bool Test();
271 
272 private:
273  Sample** data_;
274  Int num_channels_;
275  Int num_samples_;
276  bool owns_data_;
277  std::vector<Sample> temporary_vector_; // Support vector for filter operation
278 
279  void AllocateMemory() {
280  data_ = new Sample*[num_channels_];
281  for (Int chan_id=0; chan_id<num_channels_; ++chan_id) {
282  data_[chan_id] = new Sample[num_samples_]();
283  }
284  }
285 
286  void DeallocateMemory() {
287  for (int chan_id=0; chan_id<num_channels_; ++chan_id) {
288  delete[] data_[chan_id];
289  }
290  delete[] data_;
291  data_ = nullptr;
292  }
293 };
294 
295 
296 class MonoBuffer : public Buffer {
297 public:
298  explicit MonoBuffer(const Int num_samples) noexcept :
299  Buffer(1, num_samples) {}
300 
301  MonoBuffer(Sample* data_referenced, const Int num_samples) noexcept :
302  Buffer(&data_referenced_, 1, num_samples),
303  data_referenced_(data_referenced) {}
304 
311  MonoBuffer(Buffer& referenced_buffer, const Int channel_id) noexcept :
312  Buffer(&(referenced_buffer.GetWritePointers()[channel_id]), 1,
313  referenced_buffer.num_samples()) {}
314 
317  void MultiplyAddSamples(const Int from_sample_id,
318  const Int num_samples,
319  const Sample* samples,
320  const Sample constant) noexcept {
322  samples, constant);
323  }
324 
325  inline void SetSample(const Int sample_id,
326  const Sample sample_value) noexcept {
327  Buffer::SetSample(kMonoChannel, sample_id, sample_value);
328  }
329 
330  using Buffer::SetSamples;
331 
332  void SetSamples(const Int from_sample_id, const Int num_samples,
333  const Sample* samples) noexcept {
335  samples);
336  }
337 
338  void SetFrame(const Int frame_id,
339  const Int frame_length,
340  const Signal& signal) {
341  Buffer::SetFrame(0, frame_id, frame_length, signal);
342  }
343 
344  inline Sample GetSample(const Int sample_id) const noexcept {
345  return Buffer::GetSample(kMonoChannel, sample_id);
346  }
347 
348  const Sample* GetReadPointer() const noexcept {
350  }
351 
352  Sample* GetWritePointer() noexcept {
354  }
355 
356  static MonoBuffer Unary(const Sample sample) noexcept {
357  MonoBuffer output(1);
358  output.SetSample(0, sample);
359  return output;
360  }
361 
362  using Buffer::AddSamples;
363 
364  void AddSamples(const Int from_sample_id,
365  const Int num_samples,
366  const Sample* samples) noexcept {
368  samples);
369  }
370 
371  virtual ~MonoBuffer() {}
372 private:
373  // We use in case of the MonoBuffer(Sample* data_referenced, const Int num_samples)
374  // constructor. We need this because taking &data_referenced as the Sample**
375  // would be taking the address of a temporary.
376  Sample* data_referenced_;
377 };
378 
379 class StereoBuffer : public Buffer {
380 public:
381  StereoBuffer(const Int num_samples) noexcept :
382  Buffer(2, num_samples) {}
383 
384  inline void SetLeftSample(const Int sample_id,
385  const Sample sample_value) noexcept {
386  Buffer::SetSample(kLeftChannel, sample_id, sample_value);
387  }
388 
389  inline void SetRightSample(const Int sample_id,
390  const Sample sample_value) noexcept {
391  Buffer::SetSample(kRightChannel, sample_id, sample_value);
392  }
393 
394  inline Sample GetLeftSample(const Int sample_id) const noexcept {
395  return Buffer::GetSample(kLeftChannel, sample_id);
396  }
397 
398  inline Sample GetRightSample(const Int sample_id) const noexcept {
399  return Buffer::GetSample(kRightChannel, sample_id);
400  }
401 
402  const Sample* GetLeftReadPointer() const noexcept {
404  }
405 
406  const Sample* GetRightReadPointer() const noexcept {
408  }
409 
412  }
413 
416  }
417 
418  void AddSamplesLeft(const Sample* samples,
419  const Int from_sample_id,
420  const Int num_samples_to_add) noexcept {
421  Buffer::AddSamples(kLeftChannel, from_sample_id,
422  num_samples_to_add, samples);
423  }
424 
425  void FilterAddSamplesLeft(const Int from_sample_id,
426  const Int num_samples,
427  const Sample* samples,
428  mcl::DigitalFilter& filter) noexcept {
429  Buffer::FilterAddSamples(kLeftChannel, from_sample_id,
430  num_samples, samples, filter);
431  }
432 
433  void FilterAddSamplesRight(const Int from_sample_id,
434  const Int num_samples,
435  const Sample* samples,
436  mcl::DigitalFilter& filter) noexcept {
437  Buffer::FilterAddSamples(kRightChannel, from_sample_id,
438  num_samples, samples, filter);
439  }
440 
441  void AddSamplesRight(const Sample* samples,
442  const Int from_sample_id,
443  const Int num_samples_to_add) noexcept {
444  Buffer::AddSamples(kRightChannel, from_sample_id,
445  num_samples_to_add, samples);
446  }
447 
448  virtual ~StereoBuffer() {}
449 };
450 
451 
452 
453 enum class HoaOrdering {
454  Fuma,
455  Acn
456 } ;
457 
458 class HoaBuffer : public Buffer {
459 private:
460  HoaOrdering ordering_;
461 
462 public:
463  HoaBuffer(const Int max_degree, const Int num_samples, const HoaOrdering ordering = HoaOrdering::Acn) :
464  Buffer(GetNumChannels(max_degree), num_samples),
465  ordering_(ordering) {}
466 
467  inline void SetSample(const Int order, const Int degree, const Int sample_id,
468  const Sample& sample_value) noexcept {
469  Buffer::SetSample(GetChannelId(order, degree, ordering_),
470  sample_id, sample_value);
471  }
472 
473  using Buffer::AddSamples;
474 
475  void AddSamples(const Int order, const Int degree,
476  const Int from_sample_id,
477  const Int num_samples,
478  const Sample* samples) {
479  Buffer::AddSamples(GetChannelId(order, degree, ordering_),
480  from_sample_id, num_samples, samples);
481  }
482 
485  void MultiplyAddSamples(const Int order, const Int degree,
486  const Int from_sample_id,
487  const Int num_samples,
488  const Sample* samples,
489  const Sample constant) noexcept {
490  Buffer::MultiplyAddSamples(GetChannelId(order, degree, ordering_),
491  from_sample_id, num_samples,
492  samples, constant);
493  }
494 
495  inline Sample GetSample(const Int order, const Int degree,
496  const Int sample_id) const noexcept {
497  return Buffer::GetSample(GetChannelId(order, degree, ordering_), sample_id);
498  }
499 
500  static Int GetChannelId(const Int order, const Int degree, const HoaOrdering ordering) {
501  ASSERT(order >= 0);
502  ASSERT(degree <= std::abs(order));
503 
504  switch (ordering) {
505  case HoaOrdering::Acn: {
506  return order*order + order + degree;
507  }
508  case HoaOrdering::Fuma: {
509  // see https://en.wikipedia.org/wiki/Ambisonic_data_exchange_format
510  if (order == 0) {
511  return 0;
512  } else if (order == 1) {
513  if (degree == 1) {
514  return 1;
515  } else if (degree == -1) {
516  return 2;
517  } else if (degree == 0) {
518  return 3;
519  }
520  } else {
521  const Int center_index = order*order;
522  Int channel_id = center_index;
523  Int this_degree = 0;
524  while (this_degree != degree) {
525  this_degree = (this_degree > 0) ? -this_degree : abs(this_degree)+1;
526  channel_id++;
527  }
528  return channel_id;
529  }
530 
531  break;
532  }
533  default: {
534  ASSERT(false);
535  return 0;
536  }
537  }
538  ASSERT(false);
539  return 0;
540  }
541 
542  static Int GetNumChannels(const Int max_order) {
543  ASSERT(max_order > 0);
544  return (max_order+1)*(max_order+1); // (N+1)^2
545  }
546 };
547 
548 } // End namespace
549 
550 #endif