Ring Daemon 16.0.0
Loading...
Searching...
No Matches
jacklayer.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#ifdef HAVE_CONFIG_H
19#include "config.h"
20#endif
21
22#include "jacklayer.h"
23
24#include "logger.h"
25#include "audio/resampler.h"
27#include "audio/ringbuffer.h"
28#include "audio/audioloop.h"
29#include "manager.h"
30
31#include <unistd.h>
32
33#include <cassert>
34#include <climits>
35
36/* TODO
37 * implement shutdown callback
38 * auto connect optional
39 */
40
41namespace jami {
42
43namespace {
44void
45connectPorts(jack_client_t* client, int portType, const std::vector<jack_port_t*>& ports)
46{
48 for (unsigned i = 0; physical_ports[i]; ++i) {
49 if (i >= ports.size())
50 break;
51 const char* port = jack_port_name(ports[i]);
54 JAMI_ERR("Unable to connect %s to %s", port, physical_ports[i]);
55 break;
56 }
57 } else {
59 JAMI_ERR("Unable to connect port %s to %s", physical_ports[i], port);
60 break;
61 }
62 }
63 }
65}
66
67bool
69{
70 // XXX 512 is arbitrary
71 return jack_ringbuffer_read_space(rb) > 512;
72}
73} // namespace
74
75void
76JackLayer::playback()
77{
79 auto format = audioFormat_;
81 if (auto toPlay = getPlayback(format, writeSpace())) {
82 write(*toPlay);
83 }
84}
85
86void
87JackLayer::capture()
88{
89 if (auto buf = read())
90 mainRingBuffer_->put(std::move(buf));
91}
92
93size_t
94JackLayer::writeSpace()
95{
96 if (out_ringbuffers_.empty())
97 return 0;
98 size_t toWrite {std::numeric_limits<size_t>::max()};
99 for (unsigned i = 0; i < out_ringbuffers_.size(); ++i) {
100 toWrite = std::min(toWrite, jack_ringbuffer_write_space(out_ringbuffers_[i]));
101 }
102 return std::min<size_t>(toWrite / sizeof(float), audioFormat_.sample_rate / 25);
103}
104
105void
106JackLayer::write(const AudioFrame& buffer)
107{
108 auto num_samples = buffer.pointer()->nb_samples;
109 auto num_bytes = num_samples * sizeof(float);
110 auto channels = std::min<size_t>(out_ringbuffers_.size(), buffer.pointer()->ch_layout.nb_channels);
111 for (size_t i = 0; i < channels; ++i) {
112 jack_ringbuffer_write(out_ringbuffers_[i],
113 (const char*) buffer.pointer()->extended_data[i],
114 num_bytes);
115 }
116}
117
118std::unique_ptr<AudioFrame>
119JackLayer::read()
120{
121 if (in_ringbuffers_.empty())
122 return {};
123
124 size_t toRead {std::numeric_limits<size_t>::max()};
125 for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
126 toRead = std::min(toRead, jack_ringbuffer_read_space(in_ringbuffers_[i]));
127 }
128 if (not toRead)
129 return {};
130
131 auto format = audioInputFormat_;
133 auto buffer = std::make_unique<AudioFrame>(format, toRead / sizeof(jack_default_audio_sample_t));
134
135 for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
136 jack_ringbuffer_read(in_ringbuffers_[i],
137 (char*) buffer->pointer()->extended_data[i],
138 toRead);
139 }
140 return buffer;
141}
142
143/* This thread can lock, do whatever it wants, and read from/write to the jack
144 * ring buffers
145 * XXX: Access to shared state (i.e. member variables) should be synchronized if needed */
146void
147JackLayer::ringbuffer_worker()
148{
149 flushMain();
150 flushUrgent();
151
152 while (true) {
153 std::unique_lock lock(ringbuffer_thread_mutex_);
154
155 // may have changed, we don't want to wait for a notification we won't get
157 return;
158
159 // FIXME this is all kinds of evil
160 std::this_thread::sleep_for(std::chrono::milliseconds(20));
161
162 capture();
163 playback();
164
165 // wait until process() signals more data
166 // FIXME: this checks for spurious wakes, but the predicate
167 // is rather arbitrary. We should wait until ring has/needs data
168 // and jack has/needs data.
169 data_ready_.wait(lock, [&] {
170 return status_ != Status::Started or ringbuffer_ready_for_read(in_ringbuffers_[0]);
171 });
172 }
173}
174
175void
177 std::vector<jack_port_t*>& ports,
178 bool playback,
179 std::vector<jack_ringbuffer_t*>& ringbuffers)
180{
181 const char** physical_ports = jack_get_ports(client,
182 NULL,
183 NULL,
184 playback ? JackPortIsInput
186 for (unsigned i = 0; physical_ports[i]; ++i) {
187 if (i == 2)
188 break;
189 char port_name[32] = {0};
190 if (playback)
191 snprintf(port_name, sizeof(port_name), "out_%d", i + 1);
192 else
193 snprintf(port_name, sizeof(port_name), "in_%d", i + 1);
194 port_name[sizeof(port_name) - 1] = '\0';
196 port_name,
199 0);
200 if (port == nullptr)
201 throw std::runtime_error("Unable to register JACK output port");
202 ports.push_back(port);
203
204 static const unsigned RB_SIZE = 16384;
206 if (rb == nullptr)
207 throw std::runtime_error("Unable to create JACK ringbuffer");
209 throw std::runtime_error("Unable to lock JACK ringbuffer in memory");
210 ringbuffers.push_back(rb);
211 }
213}
214
216 : AudioLayer(p)
217 , captureClient_(nullptr)
218 , playbackClient_(nullptr)
219{
220 playbackClient_ = jack_client_open(PACKAGE_NAME,
222 NULL);
223 if (!playbackClient_)
224 throw std::runtime_error("Unable to open JACK client");
225
226 captureClient_ = jack_client_open(PACKAGE_NAME,
228 NULL);
229 if (!captureClient_)
230 throw std::runtime_error("Unable to open JACK client");
231
232 jack_set_process_callback(captureClient_, process_capture, this);
233 jack_set_process_callback(playbackClient_, process_playback, this);
234
235 createPorts(playbackClient_, out_ports_, true, out_ringbuffers_);
236 createPorts(captureClient_, in_ports_, false, in_ringbuffers_);
237
238 const auto playRate = jack_get_sample_rate(playbackClient_);
239 const auto captureRate = jack_get_sample_rate(captureClient_);
240
241 audioInputFormat_ = {captureRate, (unsigned) in_ringbuffers_.size()};
242 hardwareFormatAvailable(AudioFormat(playRate, out_ringbuffers_.size()));
244 jack_on_shutdown(playbackClient_, onShutdown, this);
245}
246
248{
249 stopStream();
250
251 for (auto p : out_ports_)
252 jack_port_unregister(playbackClient_, p);
253 for (auto p : in_ports_)
254 jack_port_unregister(captureClient_, p);
255
256 if (jack_client_close(playbackClient_))
257 JAMI_ERR("Unable to close JACK client");
258 if (jack_client_close(captureClient_))
259 JAMI_ERR("Unable to close JACK client");
260
261 for (auto r : out_ringbuffers_)
263 for (auto r : in_ringbuffers_)
265}
266
267void
268JackLayer::updatePreference(AudioPreference& /*pref*/, int /*index*/, AudioDeviceType /*type*/)
269{}
270
271std::vector<std::string>
272JackLayer::getCaptureDeviceList() const
273{
274 return std::vector<std::string>();
275}
276
277std::vector<std::string>
278JackLayer::getPlaybackDeviceList() const
279{
280 return std::vector<std::string>();
281}
282
283int
284JackLayer::getAudioDeviceIndex(const std::string& /*name*/, AudioDeviceType /*type*/) const
285{
286 return 0;
287}
288
289std::string
290JackLayer::getAudioDeviceName(int /*index*/, AudioDeviceType /*type*/) const
291{
292 return "";
293}
294
295int
296JackLayer::getIndexCapture() const
297{
298 return 0;
299}
300
301int
302JackLayer::getIndexPlayback() const
303{
304 return 0;
305}
306
307int
308JackLayer::getIndexRingtone() const
309{
310 return 0;
311}
312
313int
314JackLayer::process_capture(jack_nframes_t frames, void* arg)
315{
316 JackLayer* context = static_cast<JackLayer*>(arg);
317
318 for (unsigned i = 0; i < context->in_ringbuffers_.size(); ++i) {
319 // read from input
321 jack_port_get_buffer(context->in_ports_[i], frames));
322
323 const size_t bytes_to_read = frames * sizeof(*in_buffers);
324 size_t bytes_to_rb = jack_ringbuffer_write(context->in_ringbuffers_[i],
325 (char*) in_buffers,
327
328 // fill the rest with silence
330 // TODO: set some flag for underrun?
331 JAMI_WARN("Dropped %lu bytes", bytes_to_read - bytes_to_rb);
332 }
333 }
334
335 /* Tell the ringbuffer thread there is work to do. If it is already
336 * running, the lock will not be available. We are unable to wait
337 * here in the process() thread, but we don't need to signal
338 * in that case, because the ringbuffer thread will read all the
339 * data queued before waiting again. */
340 if (context->ringbuffer_thread_mutex_.try_lock()) {
341 context->data_ready_.notify_one();
342 context->ringbuffer_thread_mutex_.unlock();
343 }
344
345 return 0;
346}
347
348int
349JackLayer::process_playback(jack_nframes_t frames, void* arg)
350{
351 JackLayer* context = static_cast<JackLayer*>(arg);
352
353 for (unsigned i = 0; i < context->out_ringbuffers_.size(); ++i) {
354 // write to output
356 jack_port_get_buffer(context->out_ports_[i], frames));
357
358 const size_t bytes_to_write = frames * sizeof(*out_buffers);
359 size_t bytes_from_rb = jack_ringbuffer_read(context->out_ringbuffers_[i],
360 (char*) out_buffers,
362
363 // fill the rest with silence
365 const size_t frames_read = bytes_from_rb / sizeof(*out_buffers);
367 }
368 }
369
370 return 0;
371}
372
376void JackLayer::startStream(AudioDeviceType)
377{
378 std::lock_guard lock(mutex_);
379 if (status_ != Status::Idle)
380 return;
382
383 if (jack_activate(playbackClient_) or jack_activate(captureClient_)) {
384 JAMI_ERR("Unable to activate JACK client");
385 return;
386 }
387 ringbuffer_thread_ = std::thread(&JackLayer::ringbuffer_worker, this);
388 connectPorts(playbackClient_, JackPortIsInput, out_ports_);
389 connectPorts(captureClient_, JackPortIsOutput, in_ports_);
390}
391
392void
393JackLayer::onShutdown(void* /* data */)
394{
395 JAMI_WARN("JACK server shutdown");
396 // FIXME: handle this safely
397}
398
402void JackLayer::stopStream(AudioDeviceType)
403{
404 std::lock_guard lock(mutex_);
406 return;
408 data_ready_.notify_one();
409
410 if (jack_deactivate(playbackClient_) or jack_deactivate(captureClient_)) {
411 JAMI_ERR("Unable to deactivate JACK client");
412 }
413
414 if (ringbuffer_thread_.joinable())
415 ringbuffer_thread_.join();
416
417 flushMain();
418 flushUrgent();
419}
420
421} // namespace jami
Loop on a sound file.
std::shared_ptr< RingBuffer > mainRingBuffer_
Buffers for audio processing.
Definition audiolayer.h:254
std::mutex mutex_
Lock for the entire audio layer.
Definition audiolayer.h:283
std::atomic< Status > status_
Whether or not the audio layer's playback stream is started.
Definition audiolayer.h:260
std::shared_ptr< AudioFrame > getPlayback(AudioFormat format, size_t samples)
Definition audiolayer.h:207
void notifyIncomingCall()
Emit an audio notification (beep) on incoming calls.
void hardwareFormatAvailable(AudioFormat playback, size_t bufSize=0)
Callback to be called by derived classes when the audio output is opened.
void flushUrgent()
Flush urgent buffer.
void hardwareInputFormatAvailable(AudioFormat capture)
Set the input format on necessary objects.
AudioFormat audioFormat_
Sample Rate that should be sent to the sound card.
Definition audiolayer.h:266
void flushMain()
Flush main buffer.
AudioFormat audioInputFormat_
Sample Rate for input.
Definition audiolayer.h:271
JackLayer(const AudioPreference &)
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_WARN(...)
Definition logger.h:217
void createPorts(jack_client_t *client, std::vector< jack_port_t * > &ports, bool playback, std::vector< jack_ringbuffer_t * > &ringbuffers)
void emitSignal(Args... args)
Definition ring_signal.h:64
libjami::AudioFrame AudioFrame
AudioDeviceType
Definition audiolayer.h:58
Structure to hold sample rate and channel number associated with audio data.
AVSampleFormat sampleFormat