OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
alsa_protocol.hpp
1#pragma once
2#if __has_include( \
3 <alsa/asoundlib.h>) && __has_include(<zita-alsa-pcmi-ardour.h>) && !defined(__EMSCRIPTEN__)
4
5#include <ossia/audio/audio_engine.hpp>
6#include <ossia/dataflow/float_to_sample.hpp>
7#include <ossia/dataflow/sample_to_float.hpp>
9#include <ossia/detail/pod_vector.hpp>
10#include <ossia/detail/thread.hpp>
11
12#include <alsa/asoundlib.h>
13#include <kfr/base/conversion.hpp>
14
15#include <cmath>
16
17#include <atomic>
18#include <string_view>
19#include <thread>
20#include <vector>
21
22#include <zita-alsa-pcmi-ardour.h>
23
24#define OSSIA_AUDIO_ALSA 1
25
26namespace ossia
27{
28class alsa_engine final : public audio_engine
29{
30public:
31 alsa_engine(
32 std::string card_in, std::string card_out, int inputs, int outputs, int rate,
33 int bs)
34 {
35 m_client = std::make_unique<Alsa_pcmi>(
36 card_in.c_str(), card_out.c_str(), nullptr, rate, bs, 2, 2,
37 Alsa_pcmi::DEBUG_ALL);
38
39 if(m_client->state() != 0)
40 {
41 throw std::runtime_error("Could not open ALSA");
42 }
43
44 effective_sample_rate = rate;
45 effective_buffer_size = bs;
46 effective_inputs = std::min(inputs, (int)m_client->ncapt());
47 effective_outputs = std::min(outputs, (int)m_client->nplay());
48 m_thread = std::thread{[this] {
49 init_thread();
50 thread_duplex();
51 }};
52 set_thread_realtime(m_thread);
53
54 m_activated = true;
55 }
56
57 void stop() override
58 {
59 audio_engine::stop();
60
61 if(m_client)
62 {
63 m_stop_token.store(false, std::memory_order_relaxed);
64 assert(m_thread.joinable());
65 m_thread.join();
66
67 m_client.reset();
68 }
69
70 m_activated = false;
71 }
72
73 ~alsa_engine() override { stop(); }
74
75 bool running() const override
76 {
77 if(!m_client)
78 return false;
79 return m_activated;
80 }
81
82 void thread_duplex()
83 {
84 auto& client = *this->m_client.get();
85
86 std::vector<float> buf_capture;
87 buf_capture.resize(effective_buffer_size * effective_inputs);
88 std::vector<float> buf_capture_deint;
89 buf_capture_deint.resize(effective_buffer_size * effective_inputs);
90 std::vector<float*> ptrs_capture;
91 for(int i = 0; i < effective_inputs; i++)
92 ptrs_capture.push_back(buf_capture_deint.data() + i * effective_buffer_size);
93
94 std::vector<float> buf_playback;
95 buf_playback.resize(effective_buffer_size * effective_outputs);
96 std::vector<float> buf_playback_deint;
97 buf_playback_deint.resize(effective_buffer_size * effective_outputs);
98 std::vector<float*> ptrs_playback;
99 for(int i = 0; i < effective_outputs; i++)
100 ptrs_playback.push_back(buf_playback_deint.data() + i * effective_buffer_size);
101
102 client.pcm_start();
103
104 int k = 0;
105 while(m_stop_token.load(std::memory_order_relaxed))
106 {
107 k = client.pcm_wait();
108
109 while(k >= effective_buffer_size)
110 {
111 client.capt_init(effective_buffer_size);
112 for(int c = 0; c < effective_inputs; c++)
113 client.capt_chan(
114 c, buf_capture.data() + c, effective_buffer_size, effective_inputs);
115 client.capt_done(effective_buffer_size);
116
117 kfr::deinterleave(
118 ptrs_capture.data(), buf_capture.data(), effective_inputs,
119 effective_buffer_size);
120
121 std::fill_n(
122 buf_playback_deint.data(), effective_buffer_size * effective_outputs, 0);
123
124 process(ptrs_capture.data(), ptrs_playback.data(), effective_buffer_size);
125
126 kfr::interleave(
127 buf_playback.data(), (const float**)ptrs_playback.data(), effective_outputs,
128 effective_buffer_size);
129
130 client.play_init(effective_buffer_size);
131 for(int c = 0; c < effective_outputs; c++)
132 client.play_chan(
133 c, buf_playback.data() + c, effective_buffer_size, effective_outputs);
134
135 for(std::size_t i = effective_outputs; i < client.nplay(); i++)
136 client.clear_chan(i, effective_buffer_size);
137
138 client.play_done(effective_buffer_size);
139
140 k -= effective_buffer_size;
141 }
142 }
143
144 client.pcm_stop();
145 }
146
147private:
148 void init_thread()
149 {
150 ossia::set_thread_name("ossia audio 0");
151 ossia::set_thread_pinned(thread_type::Audio, 0);
152 }
153
154 void process(float** float_input, float** float_output, uint64_t nframes)
155 {
156 auto& self = *static_cast<alsa_engine*>(this);
157 self.tick_start();
158
159 const auto inputs = this->effective_inputs;
160 const auto outputs = this->effective_outputs;
161 if(self.stop_processing)
162 {
163 self.tick_clear();
164 return;
165 }
166
167 using namespace std::chrono;
168 ossia::audio_tick_state ts{
169 float_input,
170 float_output,
171 (int)inputs,
172 (int)outputs,
173 nframes,
174 duration_cast<nanoseconds>(m_last_time - m_start_time).count() / 1e9};
175 self.audio_tick(ts);
176
177 self.tick_end();
178 m_last_time = clk::now();
179 }
180
181 std::unique_ptr<Alsa_pcmi> m_client;
182 std::thread m_thread;
183 using clk = std::chrono::steady_clock;
184 clk::time_point m_start_time{};
185 clk::time_point m_last_time{};
186 std::atomic_bool m_stop_token{true};
187 std::atomic_bool m_activated{};
188};
189}
190
191#endif
Definition git_info.h:7