Audio Processing Framework (APF) version 0.5.0
combine_channels.h
Go to the documentation of this file.
1/******************************************************************************
2 Copyright (c) 2012-2016 Institut für Nachrichtentechnik, Universität Rostock
3 Copyright (c) 2006-2012 Quality & Usability Lab
4 Deutsche Telekom Laboratories, TU Berlin
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23*******************************************************************************/
24
25// https://AudioProcessingFramework.github.io/
26
29
30#ifndef APF_COMBINE_CHANNELS_H
31#define APF_COMBINE_CHANNELS_H
32
33#include <vector>
34#include <cassert> // for assert()
35#include <stdexcept> // for std::logic_error
36#include <algorithm> // for std::transform(), std::copy(), std::fill()
37
38#include <functional> // for std::bind()
39#include <type_traits> // for std::remove_reference
40
41#include "apf/iterator.h" // for *_iterator, make_*_iterator(), cast_proxy_const
42#include "apf/misc.h" // for CRTP
43
44namespace apf
45{
46
47namespace CombineChannelsResult
48{
49 enum type
50 {
51 nothing = 0,
52 constant = 1,
53 change = 2,
54 fade_in = 3,
55 fade_out = 4
56 };
57}
58
71template<typename Derived, typename ListProxy, typename Out>
72class CombineChannelsBase : public CRTP<Derived>
73{
74 protected:
75 using T = typename std::iterator_traits<typename std::remove_reference<
76 ListProxy>::type::value_type::iterator>::value_type;
77
78 public:
82 template<typename L>
83 CombineChannelsBase(L& in, Out& out)
84 : _in(in)
85 , _out(out)
86 {}
87
92 template<typename F>
93 void process(F f)
94 {
95 // We pass f by value because this is common in STL-like algorithms.
96 // After select() is called, it is passed to case_one() and case_two() as
97 // non-const reference to avoid a further copy.
98
99 _accumulate = false;
100
101 this->derived().before_the_loop();
102
103 for (auto& item: _in)
104 {
105 using namespace CombineChannelsResult;
106
107 switch (_selection = f.select(item))
108 {
109 case nothing:
110 continue; // jump to next list item
111
112 case constant:
113 this->derived().case_one(item, f);
114 break;
115
116 case change:
117 case fade_in:
118 case fade_out:
119 this->derived().case_two(item, f);
120 break;
121
122 default:
123 throw std::runtime_error("Predicate must return 0, 1 or 2!");
124 }
125 }
126
127 this->derived().after_the_loop();
128
129 if (!_accumulate)
130 {
131 std::fill(_out.begin(), _out.end(), T());
132 }
133 }
134
135 void before_the_loop() {}
136
137 template<typename ItemType, typename F>
138 void case_one(const ItemType&, F&)
139 {
140 throw std::logic_error("CombineChannelsBase: case 1 not implemented!");
141 }
142
143 template<typename ItemType, typename F>
144 void case_two(const ItemType&, F&)
145 {
146 throw std::logic_error("CombineChannelsBase: case 2 not implemented!");
147 }
148
149 void after_the_loop() {}
150
151 private:
152 ListProxy _in;
153
154 protected:
155 template<typename ItemType>
156 void _case_one_copy(const ItemType& item)
157 {
158 if (_accumulate)
159 {
160 std::copy(item.begin(), item.end()
161 , make_accumulating_iterator(_out.begin()));
162 }
163 else
164 {
165 std::copy(item.begin(), item.end(), _out.begin());
166 _accumulate = true;
167 }
168 }
169
170 template<typename ItemType, typename FunctionType>
171 void _case_one_transform(const ItemType& item, FunctionType& f)
172 {
173 if (_accumulate)
174 {
175 std::transform(item.begin(), item.end()
176 , make_accumulating_iterator(_out.begin()), f);
177 }
178 else
179 {
180 std::transform(item.begin(), item.end(), _out.begin(), f);
181 _accumulate = true;
182 }
183 }
184
185 Out& _out;
186 CombineChannelsResult::type _selection;
187 bool _accumulate;
188};
189
192template<typename L, typename Out>
194 CombineChannelsCopy<L, Out>, L, Out>
195{
196 private:
198
199 public:
200 CombineChannelsCopy(const L& in, Out& out) : _base(in, out) {}
201
202 template<typename ItemType, typename F>
203 void case_one(const ItemType& item, F&)
204 {
205 this->_case_one_copy(item);
206 }
207
208 // Case 2 is not implemented and shall not be used!
209};
210
213template<typename L, typename Out>
215 CombineChannels<L, Out>, L, Out>
216{
217 private:
219
220 public:
221 CombineChannels(const L& in, Out& out) : _base(in, out) {}
222
223 template<typename ItemType, typename F>
224 void case_one(const ItemType& item, F& f)
225 {
226 this->_case_one_transform(item, f);
227 }
228
229 // Case 2 is not implemented and shall not be used!
230};
231
234template<typename L, typename Out>
236 CombineChannelsInterpolation<L, Out>, L, Out>
237{
238 private:
239 using _base
241 using typename _base::T;
242 using _base::_selection;
243 using _base::_accumulate;
244 using _base::_out;
245
246 public:
247 CombineChannelsInterpolation(const L& in, Out& out) : _base(in, out) {}
248
249 template<typename ItemType, typename F>
250 void case_one(const ItemType& item, F& f)
251 {
252 this->_case_one_transform(item, f);
253 }
254
255 template<typename ItemType, typename F>
256 void case_two(const ItemType& item, F& f)
257 {
258 assert(_selection == CombineChannelsResult::change);
259
260 if (_accumulate)
261 {
262 std::transform(item.begin(), item.end(), index_iterator<T>()
263 , make_accumulating_iterator(_out.begin()), f);
264 }
265 else
266 {
267 std::transform(item.begin(), item.end(), index_iterator<T>()
268 , _out.begin(), f);
269 _accumulate = true;
270 }
271 }
272};
273
274struct fade_out_tag {};
275
278template<typename Derived, typename L, typename Out, typename Crossfade>
280{
281 private:
283 using typename _base::T;
284 using _base::_accumulate;
285 using _base::_out;
286
287 public:
288 CombineChannelsCrossfadeBase(const L& in, Out& out, const Crossfade& fade)
289 : _base(in, out)
290 , _fade_out_buffer(fade.size())
291 , _fade_in_buffer(fade.size())
292 , _crossfade_data(fade)
293 {}
294
295 void before_the_loop()
296 {
297 _accumulate_fade_in = _accumulate_fade_out = false;
298 }
299
300 void after_the_loop()
301 {
302 if (_accumulate_fade_out)
303 {
304 if (_accumulate)
305 {
306 std::transform(_fade_out_buffer.begin(), _fade_out_buffer.end()
307 , _crossfade_data.fade_out_begin()
308 , make_accumulating_iterator(_out.begin())
309 , std::multiplies<T>());
310 }
311 else
312 {
313 std::transform(_fade_out_buffer.begin(), _fade_out_buffer.end()
314 , _crossfade_data.fade_out_begin()
315 , _out.begin()
316 , std::multiplies<T>());
317 _accumulate = true;
318 }
319 }
320 if (_accumulate_fade_in)
321 {
322 if (_accumulate)
323 {
324 std::transform(_fade_in_buffer.begin(), _fade_in_buffer.end()
325 , _crossfade_data.fade_in_begin()
326 , make_accumulating_iterator(_out.begin())
327 , std::multiplies<T>());
328 }
329 else
330 {
331 std::transform(_fade_in_buffer.begin(), _fade_in_buffer.end()
332 , _crossfade_data.fade_in_begin()
333 , _out.begin()
334 , std::multiplies<T>());
335 _accumulate = true;
336 }
337 }
338 }
339
340 protected:
341 bool _accumulate_fade_in, _accumulate_fade_out;
342 std::vector<T> _fade_out_buffer, _fade_in_buffer;
343
344 private:
345 const Crossfade& _crossfade_data;
346};
347
350template<typename L, typename Out, typename Crossfade>
352 CombineChannelsCrossfadeCopy<L, Out, Crossfade>, L, Out, Crossfade>
353{
354 private:
356 L, Out, Crossfade>, L, Out, Crossfade>;
357
358 using _base::_fade_out_buffer;
359 using _base::_fade_in_buffer;
360 using _base::_accumulate_fade_in;
361 using _base::_accumulate_fade_out;
362 using _base::_selection;
363
364 public:
365 CombineChannelsCrossfadeCopy(const L& in, Out& out, const Crossfade& fade)
366 : _base(in, out, fade)
367 {}
368
369 template<typename ItemType, typename F>
370 void case_one(const ItemType& item, F&)
371 {
372 this->_case_one_copy(item);
373 }
374
375 template<typename ItemType, typename F>
376 void case_two(ItemType& item, F& f)
377 {
378 if (_selection != CombineChannelsResult::fade_in)
379 {
380 if (_accumulate_fade_out)
381 {
382 std::copy(item.begin(), item.end()
383 , make_accumulating_iterator(_fade_out_buffer.begin()));
384 }
385 else
386 {
387 std::copy(item.begin(), item.end(), _fade_out_buffer.begin());
388 _accumulate_fade_out = true;
389 }
390 }
391 if (_selection != CombineChannelsResult::fade_out)
392 {
393 f.update();
394
395 if (_accumulate_fade_in)
396 {
397 std::copy(item.begin(), item.end()
398 , make_accumulating_iterator(_fade_in_buffer.begin()));
399 }
400 else
401 {
402 std::copy(item.begin(), item.end(), _fade_in_buffer.begin());
403 _accumulate_fade_in = true;
404 }
405 }
406 }
407};
408
411template<typename L, typename Out, typename Crossfade>
413 CombineChannelsCrossfade<L, Out, Crossfade>, L, Out, Crossfade>
414{
415 private:
417 L, Out, Crossfade>, L, Out, Crossfade>;
418 using _base::_selection;
419 using _base::_accumulate_fade_in;
420 using _base::_accumulate_fade_out;
421
422 public:
423 CombineChannelsCrossfade(const L& in, Out& out, const Crossfade& fade)
424 : _base(in, out, fade)
425 {}
426
427 template<typename ItemType, typename F>
428 void case_one(const ItemType& item, F& f)
429 {
430 this->_case_one_transform(item, f);
431 }
432
433 template<typename ItemType, typename F>
434 void case_two(ItemType& item, F& f)
435 {
436 if (_selection != CombineChannelsResult::fade_in)
437 {
438 if (_accumulate_fade_out)
439 {
440 std::transform(item.begin(), item.end()
441 , make_accumulating_iterator(this->_fade_out_buffer.begin())
442 , std::bind(f, std::placeholders::_1, fade_out_tag()));
443 }
444 else
445 {
446 std::transform(item.begin(), item.end()
447 , this->_fade_out_buffer.begin()
448 , std::bind(f, std::placeholders::_1, fade_out_tag()));
449 _accumulate_fade_out = true;
450 }
451 }
452 if (_selection != CombineChannelsResult::fade_out)
453 {
454 f.update();
455
456 if (_accumulate_fade_in)
457 {
458 std::transform(item.begin(), item.end()
459 , make_accumulating_iterator(this->_fade_in_buffer.begin()), f);
460 }
461 else
462 {
463 std::transform(item.begin(), item.end()
464 , this->_fade_in_buffer.begin(), f);
465 _accumulate_fade_in = true;
466 }
467 }
468 }
469};
470
473template<typename T>
475{
476 private:
477 using iterator_type
479
480 public:
481 using iterator = typename std::vector<T>::const_iterator;
482 using reverse_iterator = typename std::vector<T>::const_reverse_iterator;
483
484 raised_cosine_fade(size_t block_size)
485 : _crossfade_data(
487 , math::raised_cosine<T>(static_cast<T>(2 * block_size))),
488 // block_size + 1 because we also use it in reverse order
489 iterator_type(index_iterator<T>(static_cast<T>(block_size + 1))))
490 , _size(block_size)
491 {}
492
493 iterator fade_out_begin() const { return _crossfade_data.begin(); }
494 reverse_iterator fade_in_begin() const { return _crossfade_data.rbegin(); }
495 size_t size() const { return _size; }
496
497 private:
498 const std::vector<T> _crossfade_data;
499 const size_t _size;
500};
501
502} // namespace apf
503
504#endif
Curiously Recurring Template Pattern (CRTP) base class.
Definition: misc.h:43
Base class for CombineChannels*.
void process(F f)
Do the actual combining.
CombineChannelsBase(L &in, Out &out)
Constructor.
Combine channels: accumulate.
Base class for CombineChannelsCrossfade*.
Combine channels: crossfade and accumulate.
Combine channels: transform, crossfade and accumulate.
Combine channels: interpolate and accumulate.
Combine channels: transform and accumulate.
Iterator with a built-in number.
Definition: iterator.h:986
Raised cosine (function object).
Definition: math.h:249
Crossfade using a raised cosine.
Iterator adaptor with a function call at dereferenciation.
Definition: iterator.h:877
accumulating_iterator< I > make_accumulating_iterator(I base_iterator)
Helper function to create an accumulating_iterator.
Definition: iterator.h:505
Several more or less useful iterators and some macros.
Miscellaneous helper classes.
Audio Processing Framework.
Definition: iterator.h:61