1 /*
2  * Audio driver handler. This file is part of Shairport.
3  * Copyright (c) James Laird 2013
4  * Modifications (c) Mike Brady 2014 -- 2019
5  * All rights reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person
8  * obtaining a copy of this software and associated documentation
9  * files (the "Software"), to deal in the Software without
10  * restriction, including without limitation the rights to use,
11  * copy, modify, merge, publish, distribute, sublicense, and/or
12  * sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 #include "audio.h"
29 #include "common.h"
30 #include "config.h"
31 #include <stdio.h>
32 #include <string.h>
33 
34 #ifdef CONFIG_JACK
35 extern audio_output audio_jack;
36 #endif
37 #ifdef CONFIG_SNDIO
38 extern audio_output audio_sndio;
39 #endif
40 #ifdef CONFIG_AO
41 extern audio_output audio_ao;
42 #endif
43 #ifdef CONFIG_SOUNDIO
44 extern audio_output audio_soundio;
45 #endif
46 #ifdef CONFIG_PA
47 extern audio_output audio_pa;
48 #endif
49 #ifdef CONFIG_ALSA
50 extern audio_output audio_alsa;
51 #endif
52 #ifdef CONFIG_DUMMY
53 extern audio_output audio_dummy;
54 #endif
55 #ifdef CONFIG_PIPE
56 extern audio_output audio_pipe;
57 #endif
58 #ifdef CONFIG_STDOUT
59 extern audio_output audio_stdout;
60 #endif
61 
62 static audio_output *outputs[] = {
63 #ifdef CONFIG_ALSA
64     &audio_alsa,
65 #endif
66 #ifdef CONFIG_SNDIO
67     &audio_sndio,
68 #endif
69 #ifdef CONFIG_PA
70     &audio_pa,
71 #endif
72 #ifdef CONFIG_JACK
73     &audio_jack,
74 #endif
75 #ifdef CONFIG_AO
76     &audio_ao,
77 #endif
78 #ifdef CONFIG_SOUNDIO
79     &audio_soundio,
80 #endif
81 #ifdef CONFIG_PIPE
82     &audio_pipe,
83 #endif
84 #ifdef CONFIG_STDOUT
85     &audio_stdout,
86 #endif
87 #ifdef CONFIG_DUMMY
88     &audio_dummy,
89 #endif
90     NULL};
91 
audio_get_output(const char * name)92 audio_output *audio_get_output(const char *name) {
93   audio_output **out;
94 
95   // default to the first
96   if (!name)
97     return outputs[0];
98 
99   for (out = outputs; *out; out++)
100     if (!strcasecmp(name, (*out)->name))
101       return *out;
102 
103   return NULL;
104 }
105 
audio_ls_outputs(void)106 void audio_ls_outputs(void) {
107   audio_output **out;
108 
109   printf("Available audio backends:\n");
110   for (out = outputs; *out; out++)
111     printf("    %s%s\n", (*out)->name, out == outputs ? " (default)" : "");
112 
113   for (out = outputs; *out; out++) {
114     printf("\n");
115     if ((*out)->help) {
116       printf("Settings and options for the audio backend \"%s\":\n", (*out)->name);
117       (*out)->help();
118     } else {
119       printf("There are no settings or options for the audio backend \"%s\".\n", (*out)->name);
120     }
121   }
122 }
123 
parse_general_audio_options(void)124 void parse_general_audio_options(void) {
125   /* this must be called after the output device has been initialised, so that the default values
126    * are set before any options are chosen */
127   int value;
128   double dvalue;
129   const char *str = 0;
130   if (config.cfg != NULL) {
131 
132     /* Get the desired buffer size setting (deprecated). */
133     if (config_lookup_int(config.cfg, "general.audio_backend_buffer_desired_length", &value)) {
134       if ((value < 0) || (value > 66150)) {
135         inform("The setting general.audio_backend_buffer_desired_length is deprecated. "
136                "Use alsa.audio_backend_buffer_desired_length_in_seconds instead.");
137         die("Invalid audio_backend_buffer_desired_length value: \"%d\". It "
138             "should be between 0 and "
139             "66150, default is %d",
140             value, (int)(config.audio_backend_buffer_desired_length * 44100));
141       } else {
142         inform("The setting general.audio_backend_buffer_desired_length is deprecated. "
143                "Use general.audio_backend_buffer_desired_length_in_seconds instead.");
144         config.audio_backend_buffer_desired_length = 1.0 * value / 44100;
145       }
146     }
147 
148     /* Get the desired buffer size setting in seconds. */
149     if (config_lookup_float(config.cfg, "general.audio_backend_buffer_desired_length_in_seconds",
150                             &dvalue)) {
151       if (dvalue < 0) {
152         die("Invalid audio_backend_buffer_desired_length_in_seconds value: \"%f\". It "
153             "should be 0.0 or greater."
154             " The default is %.3f seconds",
155             dvalue, config.audio_backend_buffer_desired_length);
156       } else {
157         config.audio_backend_buffer_desired_length = dvalue;
158       }
159     }
160 
161     /* Get the minimum buffer size for fancy interpolation setting in seconds. */
162     if (config_lookup_float(config.cfg,
163                             "general.audio_backend_buffer_interpolation_threshold_in_seconds",
164                             &dvalue)) {
165       if ((dvalue < 0) || (dvalue > config.audio_backend_buffer_desired_length)) {
166         die("Invalid audio_backend_buffer_interpolation_threshold_in_seconds value: \"%f\". It "
167             "should be between 0 and "
168             "audio_backend_buffer_desired_length_in_seconds of %.3f, default is %.3f seconds",
169             dvalue, config.audio_backend_buffer_desired_length,
170             config.audio_backend_buffer_interpolation_threshold_in_seconds);
171       } else {
172         config.audio_backend_buffer_interpolation_threshold_in_seconds = dvalue;
173       }
174     }
175 
176     /* Get the latency offset (deprecated). */
177     if (config_lookup_int(config.cfg, "general.audio_backend_latency_offset", &value)) {
178       if ((value < -66150) || (value > 66150)) {
179         inform("The setting general.audio_backend_latency_offset is deprecated. "
180                "Use general.audio_backend_latency_offset_in_seconds instead.");
181         die("Invalid  audio_backend_latency_offset value: \"%d\". It "
182             "should be between -66150 and +66150, default is 0",
183             value);
184       } else {
185         inform("The setting general.audio_backend_latency_offset is deprecated. "
186                "Use general.audio_backend_latency_offset_in_seconds instead.");
187         config.audio_backend_latency_offset = 1.0 * value / 44100;
188       }
189     }
190 
191     /* Get the latency offset in seconds. */
192     if (config_lookup_float(config.cfg, "general.audio_backend_latency_offset_in_seconds",
193                             &dvalue)) {
194       config.audio_backend_latency_offset = dvalue;
195     }
196 
197     /* Check if the length of the silent lead-in ia set to \"auto\". */
198     if (config_lookup_string(config.cfg, "general.audio_backend_silent_lead_in_time", &str)) {
199       if (strcasecmp(str, "auto") == 0) {
200         config.audio_backend_silent_lead_in_time_auto = 1;
201       } else {
202         if (config.audio_backend_silent_lead_in_time_auto == 1)
203           warn("Invalid audio_backend_silent_lead_in_time \"%s\". It should be \"auto\" or the "
204                "lead-in time in seconds. "
205                "It remains set to \"auto\". Note: numbers should not be placed in quotes.",
206                str);
207         else
208           warn("Invalid output rate \"%s\". It should be \"auto\" or the lead-in time in seconds. "
209                "It remains set to %f. Note: numbers should not be placed in quotes.",
210                str, config.audio_backend_silent_lead_in_time);
211       }
212     }
213 
214     /* Get the desired length of the silent lead-in. */
215     if (config_lookup_float(config.cfg, "general.audio_backend_silent_lead_in_time", &dvalue)) {
216       if ((dvalue < 0.0) || (dvalue > 4)) {
217         if (config.audio_backend_silent_lead_in_time_auto == 1)
218           warn("Invalid audio_backend_silent_lead_in_time \"%f\". It "
219                "must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. "
220                "The setting remains at \"auto\".",
221                dvalue);
222         else
223           warn("Invalid audio_backend_silent_lead_in_time \"%f\". It "
224                "must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. "
225                "It remains set to %f.",
226                dvalue, config.audio_backend_silent_lead_in_time);
227       } else {
228         config.audio_backend_silent_lead_in_time = dvalue;
229         config.audio_backend_silent_lead_in_time_auto = 0;
230       }
231     }
232   }
233 }
234