Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "alsaaudiodriver.h"
23 :
24 : #define ALSA_PCM_NEW_HW_PARAMS_API
25 : #include <alsa/asoundlib.h>
26 :
27 : #include <fcntl.h>
28 : #include <unistd.h>
29 : #include <string.h>
30 : #include <math.h>
31 : #include <pthread.h>
32 :
33 : #include "translation.h"
34 : #include "log.h"
35 : #include "runtime.h"
36 :
37 : using namespace muse;
38 : using namespace muse::audio;
39 :
40 : namespace {
41 : struct ALSAData
42 : {
43 : float* buffer = nullptr;
44 : snd_pcm_t* alsaDeviceHandle = nullptr;
45 : unsigned long samples = 0;
46 : int channels = 0;
47 : bool audioProcessingDone = false;
48 : pthread_t threadHandle = 0;
49 : IAudioDriver::Callback callback;
50 : void* userdata = nullptr;
51 : };
52 :
53 : static ALSAData* s_alsaData{ nullptr };
54 : static muse::audio::IAudioDriver::Spec s_format;
55 :
56 0 : static void* alsaThread(void* aParam)
57 : {
58 0 : muse::runtime::setThreadName("audio_driver");
59 0 : ALSAData* data = static_cast<ALSAData*>(aParam);
60 :
61 0 : int ret = snd_pcm_wait(data->alsaDeviceHandle, 1000);
62 0 : IF_ASSERT_FAILED(ret > 0) {
63 0 : return nullptr;
64 : }
65 :
66 0 : while (!data->audioProcessingDone)
67 : {
68 0 : uint8_t* stream = (uint8_t*)data->buffer;
69 0 : int len = data->samples * data->channels * sizeof(float);
70 :
71 0 : data->callback(stream, len);
72 :
73 0 : snd_pcm_sframes_t pcm = snd_pcm_writei(data->alsaDeviceHandle, data->buffer, data->samples);
74 0 : if (pcm != -EPIPE) {
75 : } else {
76 0 : snd_pcm_prepare(data->alsaDeviceHandle);
77 : }
78 : }
79 :
80 0 : LOGI() << "exit";
81 0 : return nullptr;
82 : }
83 :
84 0 : static void alsaCleanup()
85 : {
86 0 : if (!s_alsaData) {
87 0 : return;
88 : }
89 :
90 0 : s_alsaData->audioProcessingDone = true;
91 0 : if (s_alsaData->threadHandle) {
92 0 : pthread_join(s_alsaData->threadHandle, nullptr);
93 : }
94 0 : if (nullptr != s_alsaData->alsaDeviceHandle) {
95 0 : snd_pcm_drain(s_alsaData->alsaDeviceHandle);
96 0 : snd_pcm_close(s_alsaData->alsaDeviceHandle);
97 : }
98 :
99 0 : if (nullptr != s_alsaData->buffer) {
100 0 : delete[] s_alsaData->buffer;
101 : }
102 :
103 0 : delete s_alsaData;
104 0 : s_alsaData = nullptr;
105 : }
106 : }
107 :
108 0 : AlsaAudioDriver::AlsaAudioDriver()
109 : {
110 0 : }
111 :
112 0 : AlsaAudioDriver::~AlsaAudioDriver()
113 : {
114 0 : alsaCleanup();
115 0 : }
116 :
117 0 : void AlsaAudioDriver::init()
118 : {
119 0 : m_devicesListener.startWithCallback([this]() {
120 0 : return availableOutputDevices();
121 : });
122 :
123 0 : m_devicesListener.devicesChanged().onNotify(this, [this]() {
124 0 : m_availableOutputDevicesChanged.notify();
125 0 : });
126 0 : }
127 :
128 0 : std::string AlsaAudioDriver::name() const
129 : {
130 0 : return "ALSA";
131 : }
132 :
133 0 : AudioDeviceID AlsaAudioDriver::defaultDevice() const
134 : {
135 0 : return DEFAULT_DEVICE_ID;
136 : }
137 :
138 0 : bool AlsaAudioDriver::open(const Spec& spec, Spec* activeSpec)
139 : {
140 0 : IF_ASSERT_FAILED(!spec.deviceId.empty()) {
141 0 : return false;
142 : }
143 :
144 0 : s_alsaData = new ALSAData();
145 0 : s_alsaData->samples = spec.output.samplesPerChannel;
146 0 : s_alsaData->channels = spec.output.audioChannelCount;
147 0 : s_alsaData->callback = spec.callback;
148 :
149 : snd_pcm_t* handle;
150 0 : int rc = snd_pcm_open(&handle, spec.deviceId.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
151 0 : if (rc < 0) {
152 0 : LOGE() << "Unable to open device: " << spec.deviceId << ", err code: " << rc;
153 0 : return false;
154 : }
155 :
156 0 : s_alsaData->alsaDeviceHandle = handle;
157 :
158 : snd_pcm_hw_params_t* params;
159 0 : snd_pcm_hw_params_alloca(¶ms);
160 0 : snd_pcm_hw_params_any(handle, params);
161 :
162 0 : snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
163 0 : snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_FLOAT_LE);
164 0 : snd_pcm_hw_params_set_channels(handle, params, spec.output.audioChannelCount);
165 :
166 0 : unsigned int aSamplerate = spec.output.sampleRate;
167 0 : unsigned int val = aSamplerate;
168 0 : int dir = 0;
169 0 : rc = snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
170 0 : if (rc < 0) {
171 0 : LOGE() << "Unable to set sample rate: " << val << ", err code: " << rc;
172 0 : return false;
173 : }
174 :
175 0 : rc = snd_pcm_hw_params_set_buffer_size_near(handle, params, &s_alsaData->samples);
176 0 : if (rc < 0) {
177 0 : LOGE() << "Unable to set buffer size: " << s_alsaData->samples << ", err code: " << rc;
178 : }
179 :
180 0 : rc = snd_pcm_hw_params(handle, params);
181 0 : if (rc < 0) {
182 0 : LOGE() << "Unable to set params, err code: " << rc;
183 0 : return false;
184 : }
185 :
186 0 : snd_pcm_hw_params_get_rate(params, &val, &dir);
187 0 : aSamplerate = val;
188 :
189 0 : s_alsaData->buffer = new float[s_alsaData->samples * s_alsaData->channels];
190 : //_alsaData->sampleBuffer = new short[_alsaData->samples * _alsaData->channels];
191 :
192 0 : s_format = spec;
193 0 : s_format.output.sampleRate = aSamplerate;
194 0 : m_activeSpecChanged.send(s_format);
195 :
196 0 : if (activeSpec) {
197 0 : *activeSpec = s_format;
198 : }
199 :
200 0 : s_alsaData->threadHandle = 0;
201 0 : int ret = pthread_create(&s_alsaData->threadHandle, NULL, alsaThread, (void*)s_alsaData);
202 :
203 0 : if (0 != ret) {
204 0 : LOGE() << "Unable to create audio thread, err code: " << ret;
205 0 : return false;
206 : }
207 :
208 0 : LOGI() << "Connected to " << spec.deviceId
209 0 : << " with bufferSize " << s_format.output.samplesPerChannel
210 0 : << ", sampleRate " << s_format.output.sampleRate
211 0 : << ", channels: " << s_format.output.audioChannelCount;
212 :
213 0 : return true;
214 : }
215 :
216 0 : void AlsaAudioDriver::close()
217 : {
218 0 : alsaCleanup();
219 0 : }
220 :
221 0 : bool AlsaAudioDriver::isOpened() const
222 : {
223 0 : return s_alsaData != nullptr;
224 : }
225 :
226 0 : const AlsaAudioDriver::Spec& AlsaAudioDriver::activeSpec() const
227 : {
228 0 : return s_format;
229 : }
230 :
231 0 : async::Channel<IAudioDriver::Spec> AlsaAudioDriver::activeSpecChanged() const
232 : {
233 0 : return m_activeSpecChanged;
234 : }
235 :
236 0 : AudioDeviceList AlsaAudioDriver::availableOutputDevices() const
237 : {
238 0 : AudioDeviceList devices;
239 0 : devices.push_back({ DEFAULT_DEVICE_ID, muse::trc("audio", "System default") });
240 :
241 0 : return devices;
242 0 : }
243 :
244 0 : async::Notification AlsaAudioDriver::availableOutputDevicesChanged() const
245 : {
246 0 : return m_availableOutputDevicesChanged;
247 : }
248 :
249 0 : std::vector<samples_t> AlsaAudioDriver::availableOutputDeviceBufferSizes() const
250 : {
251 0 : std::vector<samples_t> result;
252 :
253 0 : samples_t n = MAXIMUM_BUFFER_SIZE;
254 0 : while (n >= MINIMUM_BUFFER_SIZE) {
255 0 : result.push_back(n);
256 0 : n /= 2;
257 : }
258 :
259 0 : std::sort(result.begin(), result.end());
260 :
261 0 : return result;
262 0 : }
263 :
264 0 : std::vector<sample_rate_t> AlsaAudioDriver::availableOutputDeviceSampleRates() const
265 : {
266 : // ALSA API is not of any help to get sample rates supported by the driver.
267 : // (snd_pcm_hw_params_get_rate_[min|max] will return 1 to 384000 Hz)
268 : // So just returning a sensible hard-coded list.
269 : return {
270 : 44100,
271 : 48000,
272 : 88200,
273 : 96000,
274 0 : };
275 : }
|