OSSIA
Open Scenario System for Interactive Application
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 
6 namespace ossia::nodes
7 {
8 
9 struct 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  void run(const ossia::token_request& t, ossia::exec_state_facade e) noexcept
63  {
64  if(m_data.empty())
65  return;
66 
67  // TODO do the backwards play head
68  if(!t.forward())
69  return;
70 
71  const std::size_t chan = m_data.size();
72  const std::size_t len = m_data[0].size();
73  ossia::audio_port& ap = *audio_out;
74  ap.set_channels(std::max(this->upmix, chan));
75 
76  const auto [samples_to_read, samples_to_write]
77  = snd::sample_info(e.bufferSize(), e.modelToSamples(), t);
78  if(samples_to_read == 0)
79  return;
80  if(samples_to_write <= 0)
81  return;
82 
83  assert(samples_to_write > 0);
84 
85  const auto samples_offset = t.physical_start(e.modelToSamples());
86  if(t.tempo > 0)
87  {
88  if(t.prev_date < info->m_prev_date)
89  {
90  // Sentinel: we never played.
91  if(info->m_prev_date == ossia::time_value{ossia::time_value::infinite_min})
92  {
93  if(t.prev_date != 0_tv)
94  {
95  transport(t.prev_date);
96  }
97  else
98  {
99  // Otherwise we don't need transport, everything is already at 0
100  info->m_prev_date = 0_tv;
101  }
102  }
103  else
104  {
105  transport(t.prev_date);
106  }
107  }
108 
109  for(std::size_t i = 0; i < chan; ++i)
110  {
111  ap.channel(i).resize(e.bufferSize());
112  }
113 
114  double stretch_ratio = info->update_stretch(t, e);
115 
116  // Resample
117  info->m_resampler.run(
118  *this, t, e, stretch_ratio, chan, len, samples_to_read, samples_to_write,
119  samples_offset, ap);
120 
121  for(std::size_t i = 0; i < chan; i++)
122  {
123  ossia::snd::do_fade(
124  t.start_discontinuous, t.end_discontinuous, ap.channel(i), samples_offset,
125  samples_to_write);
126  }
127 
128  ossia::snd::perform_upmix(this->upmix, chan, ap);
129  ossia::snd::perform_start_offset(this->start, ap);
130 
131  info->m_prev_date = t.date;
132  }
133  }
134 
135  sound_processing_info* info{};
136  ossia::audio_port* audio_out{};
137 
138  audio_span<float> m_data{};
139 
140  std::size_t start{};
141  std::size_t upmix{};
142 
143  std::size_t m_dataSampleRate{};
144  audio_handle m_handle{};
145 };
146 }
constexpr OSSIA_INLINE 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
The time_value class.
Definition: ossia/editor/scenario/time_value.hpp:28