OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
sound_utils.hpp
1#pragma once
2#include <ossia/dataflow/nodes/media.hpp>
4
5#include <algorithm>
6
7namespace ossia
8{
9
10// Reverse audio samples in-place for backward playback
11template <typename T>
12static void reverse_audio_buffer(T** audio_array, std::size_t channels, int64_t samples) noexcept
13{
14 for(std::size_t i = 0; i < channels; i++)
15 {
16 std::reverse(audio_array[i], audio_array[i] + samples);
17 }
18}
19
20// Read audio from buffer going backwards (for reverse playback)
21// 'start' is the position we're reading FROM (going towards 0)
22// Returns samples in forward order (caller should reverse if needed for stretchers)
23template <typename T>
24static void read_audio_from_buffer_backward(
25 const audio_span<float>& data, const int64_t start, const int64_t samples_to_write,
26 const int64_t start_offset, const int64_t loop_duration, const bool loops,
27 T** const audio_array) noexcept
28{
29 const auto channels = data.size();
30 if(channels == 0)
31 return;
32 const int64_t file_duration = data[0].size();
33
34 if(loops && loop_duration > 0)
35 {
36 // Looping backward: when we hit 0, we wrap to loop_duration
37 for(std::size_t i = 0; i < channels; i++)
38 {
39 auto& src = data[i];
40 T* dst = audio_array[i];
41
42 for(int64_t k = 0; k < samples_to_write; k++)
43 {
44 // Going backwards: start - k, with wrapping
45 int64_t raw_pos = start - k;
46
47 // Wrap around using modulo (handle negative values)
48 int64_t wrapped_pos = ((raw_pos % loop_duration) + loop_duration) % loop_duration;
49 int64_t pos = start_offset + wrapped_pos;
50
51 if(pos >= 0 && pos < file_duration)
52 dst[k] = src[pos];
53 else
54 dst[k] = 0;
55 }
56 }
57 }
58 else
59 {
60 // No looping: just read backwards, zero-fill if we go past the beginning
61 for(std::size_t i = 0; i < channels; i++)
62 {
63 const auto& src = data[i];
64 T* dst = audio_array[i];
65
66 for(int64_t k = 0; k < samples_to_write; k++)
67 {
68 int64_t pos = start_offset + start - k;
69
70 if(pos >= 0 && pos < file_duration)
71 dst[k] = src[pos];
72 else
73 dst[k] = 0;
74 }
75 }
76 }
77}
78
79template <typename T>
80static void read_audio_from_buffer(
81 const audio_span<float>& data, const int64_t start, const int64_t samples_to_write,
82 const int64_t start_offset, const int64_t loop_duration, const bool loops,
83 T** const audio_array) noexcept
84{
85 const auto channels = data.size();
86 if(channels == 0)
87 return;
88 const int64_t file_duration = data[0].size();
89
90 if(loops)
91 {
92 // Case where we won't loop around at all in that buffer
93 if(start + samples_to_write < loop_duration)
94 {
95 const int64_t read_start = start_offset + start;
96
97 // Absolute best case where we can just copy directly the whole buffer.
98 // read_start can be negative when the transport crosses zero, hence the
99 // lower bound: without it this indexes before the start of the sample.
100 if(read_start >= 0 && read_start + samples_to_write < file_duration)
101 {
102 for(std::size_t i = 0; i < channels; i++)
103 {
104 auto& src = data[i];
105 T* dst = audio_array[i];
106
107 for(int64_t k = 0; k < samples_to_write; k++)
108 {
109 dst[k] = src[read_start + k];
110 }
111 }
112 }
113 else
114 {
115 // Here we must check for both ends of the file
116 for(std::size_t i = 0; i < channels; i++)
117 {
118 auto& src = data[i];
119 T* dst = audio_array[i];
120
121 for(int64_t k = 0; k < samples_to_write; k++)
122 {
123 int64_t pos = read_start + k;
124 if(pos >= 0 && pos < file_duration)
125 dst[k] = src[pos];
126 else
127 dst[k] = 0;
128 }
129 }
130 }
131 }
132 else
133 {
134 // Here we may loop within that buffer
135 for(std::size_t i = 0; i < channels; i++)
136 {
137 auto& src = data[i];
138 T* dst = audio_array[i];
139
140 for(int64_t k = 0; k < samples_to_write; k++)
141 {
142 // Floor-modulo: the built-in % keeps the sign of the dividend, so a
143 // negative (start + k) would wrap to a negative index.
144 const int64_t raw_pos = start + k;
145 const int64_t wrapped_pos
146 = ((raw_pos % loop_duration) + loop_duration) % loop_duration;
147 const int64_t pos = start_offset + wrapped_pos;
148 if(pos >= 0 && pos < file_duration)
149 dst[k] = src[pos];
150 else
151 dst[k] = 0;
152 }
153 }
154 }
155 }
156 else
157 {
158 for(std::size_t i = 0; i < channels; i++)
159 {
160 const auto& src = data[i];
161 T* dst = audio_array[i];
162
163 const int64_t read_start = start + start_offset;
164
165 if(read_start >= 0 && file_duration >= read_start + samples_to_write)
166 {
167 // Absolute best case where we can copy the whole buffer
168 for(int64_t k = 0, pos = read_start; k < samples_to_write; k++, pos++)
169 {
170 dst[k] = src[pos];
171 }
172 }
173 else
174 {
175 // This buffer straddles one or both ends of the file: zero-fill the
176 // part before sample 0 (the transport can be before the start when it
177 // crosses zero) and the part past the end.
178 const int64_t lead = ossia::clamp(-read_start, (int64_t)0, samples_to_write);
179 const int64_t max
180 = ossia::clamp(file_duration - read_start, lead, samples_to_write);
181 for(int64_t k = 0; k < lead; k++)
182 {
183 dst[k] = 0;
184 }
185 for(int64_t k = lead, pos = read_start + lead; k < max; k++, pos++)
186 {
187 dst[k] = src[pos];
188 }
189 for(int64_t k = max; k < samples_to_write; k++)
190 {
191 dst[k] = 0;
192 }
193 }
194 }
195 }
196}
197}
Definition git_info.h:7
OSSIA_INLINE constexpr T clamp(T d, const T min, const T max) noexcept
clamp Returns the value bounded by a min and a max
Definition math.hpp:154
OSSIA_INLINE constexpr auto max(const T a, const U b) noexcept -> typename std::conditional<(sizeof(T) > sizeof(U)), T, U >::type
max function tailored for values
Definition math.hpp:96