OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
sound_sampler.hpp
1#pragma once
2#include <ossia/dataflow/graph_node.hpp>
3#include <ossia/dataflow/nodes/sound.hpp>
4#include <ossia/dataflow/nodes/sound_utils.hpp>
5
6namespace ossia::nodes
7{
8
9struct sound_sampler
10{
11 void set_start(std::size_t v) { start = v; }
12
13 void set_upmix(std::size_t v) { upmix = v; }
14
15 [[nodiscard]] std::size_t channels() const { return m_data.size(); }
16
17 [[nodiscard]] std::size_t duration() const
18 {
19 return m_data.empty() ? 0 : m_data[0].size();
20 }
21
22 void transport(time_value date)
23 {
24 info->m_resampler.transport(to_sample(date, m_dataSampleRate));
25 }
26
27 // Used for testing only
28 void set_sound(audio_array data)
29 {
30 m_handle = std::make_shared<audio_data>();
31 m_handle->data = std::move(data);
32 m_data.clear();
33 {
34 m_dataSampleRate = 44100;
35 m_data.assign(m_handle->data.begin(), m_handle->data.end());
36 info->m_resampler.reset(
37 0, audio_stretch_mode::None, m_handle->data.size(), m_dataSampleRate);
38 }
39 }
40
41 void set_sound(const audio_handle& hdl, int channels, int sampleRate)
42 {
43 m_handle = hdl;
44 m_data.clear();
45 if(hdl)
46 {
47 m_dataSampleRate = sampleRate;
48 m_data.assign(m_handle->data.begin(), m_handle->data.end());
49 }
50 }
51
52 template <typename T>
53 void fetch_audio(
54 const int64_t start, const int64_t samples_to_write,
55 T** const audio_array) const noexcept
56 {
57 read_audio_from_buffer(
58 m_data, start, samples_to_write, info->m_start_offset_samples,
59 info->m_loop_duration_samples, info->m_loops, audio_array);
60 }
61
62 template <typename T>
63 void fetch_audio_backward(
64 const int64_t start, const int64_t samples_to_write,
65 T** const audio_array) const noexcept
66 {
67 read_audio_from_buffer_backward(
68 m_data, start, samples_to_write, info->m_start_offset_samples,
69 info->m_loop_duration_samples, info->m_loops, audio_array);
70 }
71
72 void run(const ossia::token_request& t, ossia::exec_state_facade e) noexcept
73 {
74 if(m_data.empty())
75 return;
76
77 const std::size_t chan = m_data.size();
78 const std::size_t len = m_data[0].size();
79 ossia::audio_port& ap = *audio_out;
80 ap.set_channels(std::max(this->upmix, chan));
81
82 const auto [samples_to_read, samples_to_write]
83 = snd::sample_info(e.bufferSize(), e.modelToSamples(), t);
84 if(samples_to_read == 0)
85 return;
86 if(samples_to_write <= 0)
87 return;
88
89 assert(samples_to_write > 0);
90
91 const auto samples_offset = t.physical_start(e.modelToSamples());
92
93 // Handle transport for both forward and backward playback
94 if(t.forward())
95 {
96 if(t.prev_date < info->m_prev_date)
97 {
98 // Sentinel: we never played.
99 if(info->m_prev_date == ossia::time_value{ossia::time_value::infinite_min})
100 {
101 if(t.prev_date != 0_tv)
102 {
103 transport(t.prev_date);
104 }
105 else
106 {
107 // Otherwise we don't need transport, everything is already at 0
108 info->m_prev_date = 0_tv;
109 }
110 }
111 else
112 {
113 transport(t.prev_date);
114 }
115 }
116 }
117 else
118 {
119 // Backward playback transport handling
120 if(t.prev_date > info->m_prev_date)
121 {
122 // Sentinel: we never played.
123 if(info->m_prev_date == ossia::time_value{ossia::time_value::infinite_min})
124 {
125 transport(t.prev_date);
126 }
127 else
128 {
129 transport(t.prev_date);
130 }
131 }
132 }
133
134 for(std::size_t i = 0; i < chan; ++i)
135 {
136 ap.channel(i).resize(e.bufferSize());
137 }
138
139 const double stretch_ratio = info->update_stretch(t, e);
140 const double abs_stretch_ratio = std::abs(stretch_ratio);
141
142 // Resample (handles both forward and backward internally)
143 info->m_resampler.run(
144 *this, t, e, stretch_ratio, chan, len, samples_to_read, samples_to_write,
145 samples_offset, ap);
146
147 const bool start_discontinuous = t.start_discontinuous || (m_last_stretch > 70.);
148 const bool end_discontinuous = t.end_discontinuous || (abs_stretch_ratio > 70.);
149 if(abs_stretch_ratio > 70. && m_last_stretch > 70.)
150 {
151 [[unlikely]];
152 for(std::size_t i = 0; i < chan; i++)
153 {
154 ossia::snd::do_zero(ap.channel(i), samples_offset, samples_to_write);
155 }
156 }
157 else
158 {
159 [[likely]];
160 for(std::size_t i = 0; i < chan; i++)
161 {
162 ossia::snd::do_fade(
163 start_discontinuous, end_discontinuous, ap.channel(i), samples_offset,
164 samples_to_write);
165 }
166 }
167
168 ossia::snd::perform_upmix(this->upmix, chan, ap);
169 ossia::snd::perform_start_offset(this->start, ap);
170
171 info->m_prev_date = t.date;
172 m_last_stretch = abs_stretch_ratio;
173 }
174
175 sound_processing_info* info{};
176 ossia::audio_port* audio_out{};
177
178 audio_span<float> m_data{};
179
180 std::size_t start{};
181 std::size_t upmix{};
182
183 std::size_t m_dataSampleRate{};
184 audio_handle m_handle{};
185 double m_last_stretch{1.0};
186};
187}
The time_value class.
Definition ossia/editor/scenario/time_value.hpp:30