1 /*************************************************************************/
2 /*  audio_driver_alsa.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_alsa.h"
31 
32 #ifdef ALSA_ENABLED
33 
34 #include "globals.h"
35 #include <errno.h>
36 
init()37 Error AudioDriverALSA::init() {
38 
39 	active = false;
40 	thread_exited = false;
41 	exit_thread = false;
42 	pcm_open = false;
43 	samples_in = NULL;
44 	samples_out = NULL;
45 
46 	mix_rate = GLOBAL_DEF("audio/mix_rate", 44100);
47 	output_format = OUTPUT_STEREO;
48 	channels = 2;
49 
50 	int status;
51 	snd_pcm_hw_params_t *hwparams;
52 	snd_pcm_sw_params_t *swparams;
53 
54 #define CHECK_FAIL(m_cond)                                       \
55 	if (m_cond) {                                                \
56 		fprintf(stderr, "ALSA ERR: %s\n", snd_strerror(status)); \
57 		snd_pcm_close(pcm_handle);                               \
58 		ERR_FAIL_COND_V(m_cond, ERR_CANT_OPEN);                  \
59 	}
60 
61 	//todo, add
62 	//6 chans - "plug:surround51"
63 	//4 chans - "plug:surround40";
64 
65 	status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
66 
67 	ERR_FAIL_COND_V(status < 0, ERR_CANT_OPEN);
68 
69 	snd_pcm_hw_params_alloca(&hwparams);
70 
71 	status = snd_pcm_hw_params_any(pcm_handle, hwparams);
72 	CHECK_FAIL(status < 0);
73 
74 	status = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
75 	CHECK_FAIL(status < 0);
76 
77 	//not interested in anything else
78 	status = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE);
79 	CHECK_FAIL(status < 0);
80 
81 	//todo: support 4 and 6
82 	status = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2);
83 	CHECK_FAIL(status < 0);
84 
85 	status = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &mix_rate, NULL);
86 	CHECK_FAIL(status < 0);
87 
88 	int latency = GLOBAL_DEF("audio/output_latency", 25);
89 	buffer_size = closest_power_of_2(latency * mix_rate / 1000);
90 
91 	// set buffer size from project settings
92 	status = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size);
93 	CHECK_FAIL(status < 0);
94 
95 	// make period size 1/8
96 	period_size = buffer_size >> 3;
97 	status = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &period_size, NULL);
98 	CHECK_FAIL(status < 0);
99 
100 	unsigned int periods = 2;
101 	status = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &periods, NULL);
102 	CHECK_FAIL(status < 0);
103 
104 	status = snd_pcm_hw_params(pcm_handle, hwparams);
105 	CHECK_FAIL(status < 0);
106 
107 	//snd_pcm_hw_params_free(&hwparams);
108 
109 	snd_pcm_sw_params_alloca(&swparams);
110 
111 	status = snd_pcm_sw_params_current(pcm_handle, swparams);
112 	CHECK_FAIL(status < 0);
113 
114 	status = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, period_size);
115 	CHECK_FAIL(status < 0);
116 
117 	status = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
118 	CHECK_FAIL(status < 0);
119 
120 	status = snd_pcm_sw_params(pcm_handle, swparams);
121 	CHECK_FAIL(status < 0);
122 
123 	samples_in = memnew_arr(int32_t, period_size * channels);
124 	samples_out = memnew_arr(int16_t, period_size * channels);
125 
126 	snd_pcm_nonblock(pcm_handle, 0);
127 
128 	mutex = Mutex::create();
129 	thread = Thread::create(AudioDriverALSA::thread_func, this);
130 
131 	return OK;
132 };
133 
thread_func(void * p_udata)134 void AudioDriverALSA::thread_func(void *p_udata) {
135 
136 	AudioDriverALSA *ad = (AudioDriverALSA *)p_udata;
137 
138 	while (!ad->exit_thread) {
139 		if (!ad->active) {
140 			for (unsigned int i = 0; i < ad->period_size * ad->channels; i++) {
141 				ad->samples_out[i] = 0;
142 			};
143 		} else {
144 			ad->lock();
145 
146 			ad->audio_server_process(ad->period_size, ad->samples_in);
147 
148 			ad->unlock();
149 
150 			for (unsigned int i = 0; i < ad->period_size * ad->channels; i++) {
151 				ad->samples_out[i] = ad->samples_in[i] >> 16;
152 			}
153 		};
154 
155 		int todo = ad->period_size;
156 		int total = 0;
157 
158 		while (todo) {
159 			if (ad->exit_thread)
160 				break;
161 			uint8_t *src = (uint8_t *)ad->samples_out;
162 			int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo);
163 
164 			if (wrote < 0) {
165 				if (ad->exit_thread)
166 					break;
167 
168 				if (wrote == -EAGAIN) {
169 					//can't write yet (though this is blocking..)
170 					usleep(1000);
171 					continue;
172 				}
173 				wrote = snd_pcm_recover(ad->pcm_handle, wrote, 0);
174 				if (wrote < 0) {
175 					//absolute fail
176 					fprintf(stderr, "ALSA failed and can't recover: %s\n", snd_strerror(wrote));
177 					ad->active = false;
178 					ad->exit_thread = true;
179 					break;
180 				}
181 				continue;
182 			};
183 
184 			total += wrote;
185 			todo -= wrote;
186 		};
187 	};
188 
189 	ad->thread_exited = true;
190 };
191 
start()192 void AudioDriverALSA::start() {
193 
194 	active = true;
195 };
196 
get_mix_rate() const197 int AudioDriverALSA::get_mix_rate() const {
198 
199 	return mix_rate;
200 };
201 
get_output_format() const202 AudioDriverSW::OutputFormat AudioDriverALSA::get_output_format() const {
203 
204 	return output_format;
205 };
lock()206 void AudioDriverALSA::lock() {
207 
208 	if (!thread || !mutex)
209 		return;
210 	mutex->lock();
211 };
unlock()212 void AudioDriverALSA::unlock() {
213 
214 	if (!thread || !mutex)
215 		return;
216 	mutex->unlock();
217 };
218 
finish()219 void AudioDriverALSA::finish() {
220 
221 	if (!thread)
222 		return;
223 
224 	exit_thread = true;
225 	Thread::wait_to_finish(thread);
226 
227 	if (pcm_open)
228 		snd_pcm_close(pcm_handle);
229 
230 	if (samples_in) {
231 		memdelete_arr(samples_in);
232 		memdelete_arr(samples_out);
233 	};
234 
235 	memdelete(thread);
236 	if (mutex)
237 		memdelete(mutex);
238 	thread = NULL;
239 };
240 
AudioDriverALSA()241 AudioDriverALSA::AudioDriverALSA() {
242 
243 	mutex = NULL;
244 	thread = NULL;
245 	pcm_handle = NULL;
246 };
247 
~AudioDriverALSA()248 AudioDriverALSA::~AudioDriverALSA(){
249 
250 };
251 
252 #endif
253