Ring Daemon
Loading...
Searching...
No Matches
jacklayer.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2026 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], (const char*) buffer.pointer()->extended_data[i], num_bytes);
113 }
114}
115
116std::unique_ptr<AudioFrame>
117JackLayer::read()
118{
119 if (in_ringbuffers_.empty())
120 return {};
121
122 size_t toRead {std::numeric_limits<size_t>::max()};
123 for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
124 toRead = std::min(toRead, jack_ringbuffer_read_space(in_ringbuffers_[i]));
125 }
126 if (not toRead)
127 return {};
128
129 auto format = audioInputFormat_;
131 auto buffer = std::make_unique<AudioFrame>(format, toRead / sizeof(jack_default_audio_sample_t));
132
133 for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
134 jack_ringbuffer_read(in_ringbuffers_[i], (char*) buffer->pointer()->extended_data[i], toRead);
135 }
136 return buffer;
137}
138
139/* This thread can lock, do whatever it wants, and read from/write to the jack
140 * ring buffers
141 * XXX: Access to shared state (i.e. member variables) should be synchronized if needed */
142void
143JackLayer::ringbuffer_worker()
144{
145 flushMain();
146 flushUrgent();
147
148 while (true) {
149 std::unique_lock lock(ringbuffer_thread_mutex_);
150
151 // may have changed, we don't want to wait for a notification we won't get
153 return;
154
155 // FIXME this is all kinds of evil
156 std::this_thread::sleep_for(std::chrono::milliseconds(20));
157
158 capture();
159 playback();
160
161 // wait until process() signals more data
162 // FIXME: this checks for spurious wakes, but the predicate
163 // is rather arbitrary. We should wait until ring has/needs data
164 // and jack has/needs data.
165 data_ready_.wait(lock,
166 [&] { return status_ != Status::Started or ringbuffer_ready_for_read(in_ringbuffers_[0]); });
167 }
168}
169
170void
172 std::vector<jack_port_t*>& ports,
173 bool playback,
174 std::vector<jack_ringbuffer_t*>& ringbuffers)
175{
176 const char** physical_ports = jack_get_ports(client,
177 NULL,
178 NULL,
180 for (unsigned i = 0; physical_ports[i]; ++i) {
181 if (i == 2)
182 break;
183 char port_name[32] = {0};
184 if (playback)
185 snprintf(port_name, sizeof(port_name), "out_%d", i + 1);
186 else
187 snprintf(port_name, sizeof(port_name), "in_%d", i + 1);
188 port_name[sizeof(port_name) - 1] = '\0';
190 port_name,
193 0);
194 if (port == nullptr)
195 throw std::runtime_error("Unable to register JACK output port");
196 ports.push_back(port);
197
198 static const unsigned RB_SIZE = 16384;
200 if (rb == nullptr)
201 throw std::runtime_error("Unable to create JACK ringbuffer");
203 throw std::runtime_error("Unable to lock JACK ringbuffer in memory");
204 ringbuffers.push_back(rb);
205 }
207}
208
210 : AudioLayer(p)
211 , captureClient_(nullptr)
212 , playbackClient_(nullptr)
213{
215 if (!playbackClient_)
216 throw std::runtime_error("Unable to open JACK client");
217
219 if (!captureClient_)
220 throw std::runtime_error("Unable to open JACK client");
221
222 jack_set_process_callback(captureClient_, process_capture, this);
223 jack_set_process_callback(playbackClient_, process_playback, this);
224
225 createPorts(playbackClient_, out_ports_, true, out_ringbuffers_);
226 createPorts(captureClient_, in_ports_, false, in_ringbuffers_);
227
228 const auto playRate = jack_get_sample_rate(playbackClient_);
229 const auto captureRate = jack_get_sample_rate(captureClient_);
230
231 audioInputFormat_ = {captureRate, (unsigned) in_ringbuffers_.size()};
232 hardwareFormatAvailable(AudioFormat(playRate, out_ringbuffers_.size()));
234 jack_on_shutdown(playbackClient_, onShutdown, this);
235}
236
238{
239 stopStream();
240
241 for (auto p : out_ports_)
242 jack_port_unregister(playbackClient_, p);
243 for (auto p : in_ports_)
244 jack_port_unregister(captureClient_, p);
245
246 if (jack_client_close(playbackClient_))
247 JAMI_ERR("Unable to close JACK client");
248 if (jack_client_close(captureClient_))
249 JAMI_ERR("Unable to close JACK client");
250
251 for (auto r : out_ringbuffers_)
253 for (auto r : in_ringbuffers_)
255}
256
257void
258JackLayer::updatePreference(AudioPreference& /*pref*/, int /*index*/, AudioDeviceType /*type*/)
259{}
260
261std::vector<std::string>
262JackLayer::getCaptureDeviceList() const
263{
264 return std::vector<std::string>();
265}
266
267std::vector<std::string>
268JackLayer::getPlaybackDeviceList() const
269{
270 return std::vector<std::string>();
271}
272
273int
274JackLayer::getAudioDeviceIndex(const std::string& /*name*/, AudioDeviceType /*type*/) const
275{
276 return 0;
277}
278
279std::string
280JackLayer::getAudioDeviceName(int /*index*/, AudioDeviceType /*type*/) const
281{
282 return "";
283}
284
285int
286JackLayer::getIndexCapture() const
287{
288 return 0;
289}
290
291int
292JackLayer::getIndexPlayback() const
293{
294 return 0;
295}
296
297int
298JackLayer::getIndexRingtone() const
299{
300 return 0;
301}
302
303int
304JackLayer::process_capture(jack_nframes_t frames, void* arg)
305{
306 JackLayer* context = static_cast<JackLayer*>(arg);
307
308 for (unsigned i = 0; i < context->in_ringbuffers_.size(); ++i) {
309 // read from input
311 jack_port_get_buffer(context->in_ports_[i], frames));
312
313 const size_t bytes_to_read = frames * sizeof(*in_buffers);
314 size_t bytes_to_rb = jack_ringbuffer_write(context->in_ringbuffers_[i], (char*) in_buffers, bytes_to_read);
315
316 // fill the rest with silence
318 // TODO: set some flag for underrun?
319 JAMI_WARN("Dropped %lu bytes", bytes_to_read - bytes_to_rb);
320 }
321 }
322
323 /* Tell the ringbuffer thread there is work to do. If it is already
324 * running, the lock will not be available. We are unable to wait
325 * here in the process() thread, but we don't need to signal
326 * in that case, because the ringbuffer thread will read all the
327 * data queued before waiting again. */
328 if (context->ringbuffer_thread_mutex_.try_lock()) {
329 context->data_ready_.notify_one();
330 context->ringbuffer_thread_mutex_.unlock();
331 }
332
333 return 0;
334}
335
336int
337JackLayer::process_playback(jack_nframes_t frames, void* arg)
338{
339 JackLayer* context = static_cast<JackLayer*>(arg);
340
341 for (unsigned i = 0; i < context->out_ringbuffers_.size(); ++i) {
342 // write to output
344 jack_port_get_buffer(context->out_ports_[i], frames));
345
346 const size_t bytes_to_write = frames * sizeof(*out_buffers);
347 size_t bytes_from_rb = jack_ringbuffer_read(context->out_ringbuffers_[i], (char*) out_buffers, bytes_to_write);
348
349 // fill the rest with silence
351 const size_t frames_read = bytes_from_rb / sizeof(*out_buffers);
353 }
354 }
355
356 return 0;
357}
358
362void
363JackLayer::startStream(AudioDeviceType)
364{
365 std::lock_guard lock(mutex_);
366 if (status_ != Status::Idle)
367 return;
369
370 if (jack_activate(playbackClient_) or jack_activate(captureClient_)) {
371 JAMI_ERR("Unable to activate JACK client");
372 return;
373 }
374 ringbuffer_thread_ = std::thread(&JackLayer::ringbuffer_worker, this);
375 connectPorts(playbackClient_, JackPortIsInput, out_ports_);
376 connectPorts(captureClient_, JackPortIsOutput, in_ports_);
377}
378
379void
380JackLayer::onShutdown(void* /* data */)
381{
382 JAMI_WARN("JACK server shutdown");
383 // FIXME: handle this safely
384}
385
389void
390JackLayer::stopStream(AudioDeviceType)
391{
392 std::lock_guard lock(mutex_);
394 return;
396 data_ready_.notify_one();
397
398 if (jack_deactivate(playbackClient_) or jack_deactivate(captureClient_)) {
399 JAMI_ERR("Unable to deactivate JACK client");
400 }
401
402 if (ringbuffer_thread_.joinable())
403 ringbuffer_thread_.join();
404
405 flushMain();
406 flushUrgent();
407}
408
409} // namespace jami
Loop on a sound file.
std::shared_ptr< RingBuffer > mainRingBuffer_
Buffers for audio processing.
Definition audiolayer.h:267
std::mutex mutex_
Lock for the entire audio layer.
Definition audiolayer.h:296
std::atomic< Status > status_
Whether or not the audio layer's playback stream is started.
Definition audiolayer.h:273
std::shared_ptr< AudioFrame > getPlayback(AudioFormat format, size_t samples)
Definition audiolayer.h:220
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:279
void flushMain()
Flush main buffer.
AudioFormat audioInputFormat_
Sample Rate for input.
Definition audiolayer.h:284
JackLayer(const AudioPreference &)
#define JAMI_ERR(...)
Definition logger.h:230
#define JAMI_WARN(...)
Definition logger.h:229
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 jami_signal.h:64
AudioDeviceType
Definition audiolayer.h:57
libjami::AudioFrame AudioFrame
Structure to hold sample rate and channel number associated with audio data.
AVSampleFormat sampleFormat