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 void transport(time_value date, const ossia::tick_transport_info& tinfo)
28 {
29 info->m_resampler.transport(info->file_sample_for_model_time(
30 date, tinfo.current_tempo, m_dataSampleRate));
31 }
32
33 // Used for testing only
34 void set_sound(audio_array data)
35 {
36 m_handle = std::make_shared<audio_data>();
37 m_handle->data = std::move(data);
38 m_data.clear();
39 {
40 m_dataSampleRate = 44100;
41 m_data.assign(m_handle->data.begin(), m_handle->data.end());
42 info->m_resampler.reset(
43 0, audio_stretch_mode::None, m_handle->data.size(), m_dataSampleRate);
44 }
45 }
46
47 void set_sound(const audio_handle& hdl, int channels, int sampleRate)
48 {
49 m_handle = hdl;
50 m_data.clear();
51 if(hdl)
52 {
53 m_dataSampleRate = sampleRate;
54 m_data.assign(m_handle->data.begin(), m_handle->data.end());
55 }
56 }
57
58 template <typename T>
59 void fetch_audio(
60 const int64_t start, const int64_t samples_to_write,
61 T** const audio_array) const noexcept
62 {
63 read_audio_from_buffer(
64 m_data, start, samples_to_write, info->m_start_offset_samples,
65 info->m_loop_duration_samples, info->m_loops, audio_array);
66 }
67
68 template <typename T>
69 void fetch_audio_backward(
70 const int64_t start, const int64_t samples_to_write,
71 T** const audio_array) const noexcept
72 {
73 read_audio_from_buffer_backward(
74 m_data, start, samples_to_write, info->m_start_offset_samples,
75 info->m_loop_duration_samples, info->m_loops, audio_array);
76 }
77
78 void run(const ossia::token_request& t, ossia::exec_state_facade e) noexcept
79 {
80 if(m_data.empty())
81 return;
82
83 const std::size_t chan = m_data.size();
84 const std::size_t len = m_data[0].size();
85 ossia::audio_port& ap = *audio_out;
86 ap.set_channels(std::max(this->upmix, chan));
87
88 const auto [samples_to_read, samples_to_write]
89 = snd::sample_info(e.bufferSize(), e.modelToSamples(), t);
90 if(samples_to_read == 0)
91 return;
92 if(samples_to_write <= 0)
93 return;
94
95 assert(samples_to_write > 0);
96
97 const auto samples_offset = t.physical_start(e.modelToSamples());
98
99 if(t.forward())
100 {
101 if(t.prev_date < info->m_prev_date)
102 {
103 // First run after add_time_process() left the stretcher already
104 // primed; calling transport() again would reset it.
105 if(info->m_prev_date == ossia::time_value{ossia::time_value::infinite_min})
106 info->m_prev_date = t.prev_date;
107 else
108 transport(t.prev_date);
109 }
110 }
111 else
112 {
113 if(t.prev_date > info->m_prev_date)
114 {
115 if(info->m_prev_date == ossia::time_value{ossia::time_value::infinite_min})
116 info->m_prev_date = t.prev_date;
117 else
118 transport(t.prev_date);
119 }
120 }
121
122 for(std::size_t i = 0; i < chan; ++i)
123 {
124 ap.channel(i).resize(e.bufferSize());
125 }
126
127 const double stretch_ratio = info->update_stretch(t, e);
128 const double abs_stretch_ratio = std::abs(stretch_ratio);
129
130 info->m_resampler.run(
131 *this, t, e, stretch_ratio, chan, len, samples_to_read, samples_to_write,
132 samples_offset, ap);
133
134 const bool start_discontinuous = t.start_discontinuous || (m_last_stretch > 70.);
135 const bool end_discontinuous = t.end_discontinuous || (abs_stretch_ratio > 70.);
136 if(abs_stretch_ratio > 70. && m_last_stretch > 70.)
137 {
138 [[unlikely]];
139 for(std::size_t i = 0; i < chan; i++)
140 {
141 ossia::snd::do_zero(ap.channel(i), samples_offset, samples_to_write);
142 }
143 }
144 else
145 {
146 [[likely]];
147 for(std::size_t i = 0; i < chan; i++)
148 {
149 ossia::snd::do_fade(
150 start_discontinuous, end_discontinuous, ap.channel(i), samples_offset,
151 samples_to_write);
152 }
153 }
154
155 ossia::snd::perform_upmix(this->upmix, chan, ap);
156 ossia::snd::perform_start_offset(this->start, ap);
157
158 info->m_prev_date = t.date;
159 m_last_stretch = abs_stretch_ratio;
160 }
161
162 sound_processing_info* info{};
163 ossia::audio_port* audio_out{};
164
165 audio_span<float> m_data{};
166
167 std::size_t start{};
168 std::size_t upmix{};
169
170 std::size_t m_dataSampleRate{};
171 audio_handle m_handle{};
172 double m_last_stretch{1.0};
173};
174}
The time_value class.
Definition ossia/editor/scenario/time_value.hpp:30