Line data Source code
1 : #include "audio/backend/jack_backend.h"
2 :
3 : #include <iostream>
4 : #include <string>
5 :
6 : #include "audio/engine/i_audio_engine.h"
7 :
8 : namespace Amplitron {
9 :
10 : #ifdef WITH_JACK
11 2 : static int jack_process(jack_nframes_t nframes, void *arg) {
12 2 : auto *backend = static_cast<JackBackend *>(arg);
13 2 : if (!backend || !backend->get_engine()) return 0;
14 :
15 1 : auto *in_port = backend->get_in_port();
16 1 : auto *out_port = backend->get_out_port();
17 1 : if (!in_port || !out_port) return 0;
18 :
19 1 : float *in = static_cast<float *>(jack_port_get_buffer(in_port, nframes));
20 1 : float *out = static_cast<float *>(jack_port_get_buffer(out_port, nframes));
21 1 : if (in && out) {
22 1 : backend->get_engine()->process_audio(in, out, static_cast<int>(nframes));
23 : }
24 :
25 1 : return 0;
26 : }
27 : #endif
28 :
29 5 : JackBackend::JackBackend() = default;
30 10 : JackBackend::~JackBackend() { shutdown(); }
31 :
32 6 : bool JackBackend::initialize(IAudioEngine *engine) {
33 6 : if (initialized_) return true;
34 5 : engine_ = engine;
35 :
36 : #ifdef WITH_JACK
37 5 : jack_status_t status = static_cast<jack_status_t>(0);
38 5 : client_ = jack_client_open("Amplitron", JackNoStartServer, &status);
39 5 : if (!client_) {
40 2 : std::string status_details = "";
41 2 : if (status & JackFailure) status_details += "JackFailure ";
42 2 : if (status & JackInvalidOption) status_details += "JackInvalidOption ";
43 2 : if (status & JackNameNotUnique) status_details += "JackNameNotUnique ";
44 2 : if (status & JackServerStarted) status_details += "JackServerStarted ";
45 2 : if (status & JackServerFailed) status_details += "JackServerFailed ";
46 2 : if (status & JackServerError) status_details += "JackServerError ";
47 2 : if (status & JackNoSuchClient) status_details += "JackNoSuchClient ";
48 2 : if (status & JackLoadFailure) status_details += "JackLoadFailure ";
49 2 : if (status & JackInitFailure) status_details += "JackInitFailure ";
50 2 : if (status & JackShmFailure) status_details += "JackShmFailure ";
51 2 : if (status & JackVersionError) status_details += "JackVersionError ";
52 2 : if (status & JackBackendError) status_details += "JackBackendError ";
53 2 : if (status & JackClientZombie) status_details += "JackClientZombie ";
54 :
55 2 : std::cerr << "[Amplitron] JACK: could not open JACK server (is jackd running?). Status: 0x"
56 2 : << std::hex << static_cast<int>(status) << std::dec;
57 2 : if (!status_details.empty()) {
58 0 : std::cerr << " (" << status_details << ")";
59 : }
60 2 : std::cerr << std::endl;
61 2 : }
62 : #endif
63 :
64 5 : initialized_ = true;
65 5 : return true;
66 : }
67 :
68 11 : void JackBackend::shutdown() {
69 11 : stop();
70 : #ifdef WITH_JACK
71 11 : if (client_) {
72 3 : jack_client_close(client_);
73 3 : client_ = nullptr;
74 3 : in_port_ = nullptr;
75 3 : out_port_ = nullptr;
76 3 : ports_registered_ = false;
77 : }
78 : #endif
79 11 : initialized_ = false;
80 11 : }
81 :
82 6 : bool JackBackend::start() {
83 6 : if (!initialized_ || running_) return false;
84 :
85 : #ifdef WITH_JACK
86 6 : if (!client_) return false;
87 :
88 4 : if (!ports_registered_) {
89 3 : in_port_ = jack_port_register(client_, "in_1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
90 3 : out_port_ =
91 3 : jack_port_register(client_, "out_1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
92 3 : if (!in_port_ || !out_port_) {
93 1 : std::cerr << "[Amplitron] JACK: failed to register in/out ports." << std::endl;
94 1 : return false;
95 : }
96 2 : jack_set_process_callback(client_, jack_process, this);
97 2 : ports_registered_ = true;
98 : }
99 :
100 3 : if (jack_activate(client_)) {
101 1 : std::cerr << "[Amplitron] JACK: failed to activate JACK client." << std::endl;
102 1 : return false;
103 : }
104 :
105 2 : running_ = true;
106 2 : return true;
107 : #else
108 : return false;
109 : #endif
110 : }
111 :
112 19 : void JackBackend::stop() {
113 : #ifdef WITH_JACK
114 19 : if (client_ && running_) {
115 2 : jack_deactivate(client_);
116 : }
117 : #endif
118 19 : running_ = false;
119 19 : }
120 :
121 1 : std::vector<AudioDeviceInfo> JackBackend::get_input_devices() const {
122 4 : return {{0, "JACK in_1", 1, 0, static_cast<double>(get_sample_rate()), false}};
123 3 : }
124 :
125 1 : std::vector<AudioDeviceInfo> JackBackend::get_output_devices() const {
126 4 : return {{0, "JACK out_1", 0, 1, static_cast<double>(get_sample_rate()), false}};
127 3 : }
128 :
129 2 : bool JackBackend::set_input_device(int device_index) { return device_index == 0; }
130 :
131 2 : bool JackBackend::set_output_device(int device_index) { return device_index == 0; }
132 :
133 3 : std::string JackBackend::get_input_device_name() const { return "JACK in_1"; }
134 :
135 3 : std::string JackBackend::get_output_device_name() const { return "JACK out_1"; }
136 :
137 4 : int JackBackend::get_sample_rate() const {
138 : #ifdef WITH_JACK
139 4 : if (client_) {
140 2 : return static_cast<int>(jack_get_sample_rate(client_));
141 : }
142 : #endif
143 2 : return engine_ ? engine_->get_sample_rate() : 48000;
144 : }
145 :
146 2 : int JackBackend::get_buffer_size() const {
147 : #ifdef WITH_JACK
148 2 : if (client_) {
149 2 : return static_cast<int>(jack_get_buffer_size(client_));
150 : }
151 : #endif
152 0 : return engine_ ? engine_->get_buffer_size() : 512;
153 : }
154 :
155 : } // namespace Amplitron
|