1 /*************************************************************************/
2 /* audio_driver_pulseaudio.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30 #include "audio_driver_pulseaudio.h"
31
32 #ifdef PULSEAUDIO_ENABLED
33
34 #include <pulse/error.h>
35
36 #include "globals.h"
37
init()38 Error AudioDriverPulseAudio::init() {
39
40 active = false;
41 thread_exited = false;
42 exit_thread = false;
43 pcm_open = false;
44 samples_in = NULL;
45 samples_out = NULL;
46
47 mix_rate = GLOBAL_DEF("audio/mix_rate", 44100);
48 output_format = OUTPUT_STEREO;
49 channels = 2;
50
51 pa_sample_spec spec;
52 spec.format = PA_SAMPLE_S16LE;
53 spec.channels = channels;
54 spec.rate = mix_rate;
55
56 int latency = GLOBAL_DEF("audio/output_latency", 25);
57 buffer_size = closest_power_of_2(latency * mix_rate / 1000);
58
59 pa_buffer_attr attr;
60 // set to appropriate buffer size from global settings
61 attr.tlength = buffer_size;
62 // set them to be automatically chosen
63 attr.prebuf = (uint32_t)-1;
64 attr.maxlength = (uint32_t)-1;
65 attr.minreq = (uint32_t)-1;
66
67 int error_code;
68 pulse = pa_simple_new(NULL, // default server
69 "Godot", // application name
70 PA_STREAM_PLAYBACK,
71 NULL, // default device
72 "Sound", // stream description
73 &spec,
74 NULL, // use default channel map
75 &attr, // use buffering attributes from above
76 &error_code);
77
78 if (pulse == NULL) {
79 fprintf(stderr, "PulseAudio ERR: %s\n", pa_strerror(error_code));
80 ERR_FAIL_COND_V(pulse == NULL, ERR_CANT_OPEN);
81 }
82
83 samples_in = memnew_arr(int32_t, buffer_size * channels);
84 samples_out = memnew_arr(int16_t, buffer_size * channels);
85
86 mutex = Mutex::create();
87 thread = Thread::create(AudioDriverPulseAudio::thread_func, this);
88
89 return OK;
90 }
91
get_latency()92 float AudioDriverPulseAudio::get_latency() {
93
94 if (latency == 0) { //only do this once since it's approximate anyway
95 int error_code;
96 pa_usec_t palat = pa_simple_get_latency(pulse, &error_code);
97 latency = double(palat) / 1000000.0;
98 }
99
100 return latency;
101 }
102
thread_func(void * p_udata)103 void AudioDriverPulseAudio::thread_func(void *p_udata) {
104
105 AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata;
106
107 while (!ad->exit_thread) {
108 if (!ad->active) {
109 for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) {
110 ad->samples_out[i] = 0;
111 }
112
113 } else {
114 ad->lock();
115
116 ad->audio_server_process(ad->buffer_size, ad->samples_in);
117
118 ad->unlock();
119
120 for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) {
121 ad->samples_out[i] = ad->samples_in[i] >> 16;
122 }
123 }
124
125 // pa_simple_write always consumes the entire buffer
126
127 int error_code;
128 int byte_size = ad->buffer_size * sizeof(int16_t) * ad->channels;
129 if (pa_simple_write(ad->pulse, ad->samples_out, byte_size, &error_code) < 0) {
130 // can't recover here
131 fprintf(stderr, "PulseAudio failed and can't recover: %s\n", pa_strerror(error_code));
132 ad->active = false;
133 ad->exit_thread = true;
134 break;
135 }
136 }
137
138 ad->thread_exited = true;
139 }
140
start()141 void AudioDriverPulseAudio::start() {
142
143 active = true;
144 }
145
get_mix_rate() const146 int AudioDriverPulseAudio::get_mix_rate() const {
147
148 return mix_rate;
149 }
150
get_output_format() const151 AudioDriverSW::OutputFormat AudioDriverPulseAudio::get_output_format() const {
152
153 return output_format;
154 }
155
lock()156 void AudioDriverPulseAudio::lock() {
157
158 if (!thread || !mutex)
159 return;
160 mutex->lock();
161 }
162
unlock()163 void AudioDriverPulseAudio::unlock() {
164
165 if (!thread || !mutex)
166 return;
167 mutex->unlock();
168 }
169
finish()170 void AudioDriverPulseAudio::finish() {
171
172 if (!thread)
173 return;
174
175 exit_thread = true;
176 Thread::wait_to_finish(thread);
177
178 if (pulse)
179 pa_simple_free(pulse);
180
181 if (samples_in) {
182 memdelete_arr(samples_in);
183 memdelete_arr(samples_out);
184 };
185
186 memdelete(thread);
187 if (mutex) {
188 memdelete(mutex);
189 mutex = NULL;
190 }
191
192 thread = NULL;
193 }
194
AudioDriverPulseAudio()195 AudioDriverPulseAudio::AudioDriverPulseAudio() {
196
197 mutex = NULL;
198 thread = NULL;
199 pulse = NULL;
200 latency = 0;
201 }
202
~AudioDriverPulseAudio()203 AudioDriverPulseAudio::~AudioDriverPulseAudio() {
204 }
205
206 #endif
207