1 ///
2 /// Class to abstract away details of audio output system is in use.
3 ///	@file		audiooutput.cpp - pianod2
4 ///	@author		Perette Barella
5 ///	@date		2015-02-26
6 ///	@copyright	Copyright (c) 2015-2017 Devious Fish. All rights reserved.
7 ///
8 
9 #include <config.h>
10 
11 #include <iostream>
12 
13 #include <climits>
14 #include <cstdint>
15 
16 #include "fundamentals.h"
17 #include "utility.h"
18 #include "audiooutput.h"
19 
20 #pragma GCC diagnostic push
21 #pragma GCC diagnostic ignored "-Wdocumentation"
22 
23 #ifdef WITH_LIBSDL
24 #include "libsdloutput.h"
25 #endif
26 #ifdef WITH_LIBAO
27 #include "libaooutput.h"
28 #endif
29 #ifdef WITH_GSTREAMER
30 #include "gstreamplayer.h"
31 #endif
32 #ifdef WITH_LIBAVDEVICE
33 #include "avdeviceoutput.h"
34 extern "C" {
35 #include <libavdevice/avdevice.h>
36 }
37 // Libav doesn't provide these macros.  Yet another source of irritation it provides.
38 #ifndef AV_VERSION_MAJOR
39 #define AV_VERSION_MAJOR(x) ((x) >> 16)
40 #define AV_VERSION_MINOR(x) (((x) >> 8) & 0xff)
41 #define AV_VERSION_MICRO(x) ((x) & 0xff)
42 #endif
43 #endif
44 
45 #pragma GCC diagnostic pop
46 
47 using namespace std;
48 
49 namespace Audio {
50 
51 #if CHAR_BIT == 8
52     enum
53     {
54         B4_LITTLE_ENDIAN = 0x03020100ul,
55         B4_BIG_ENDIAN = 0x00010203ul,
56         B4_PDP_ENDIAN = 0x01000302ul,
57         B2_LITTLE_ENDIAN = 0x0100,
58         B2_BIG_ENDIAN = 0x0001
59     };
60 #else
61 #error "unsupported char size"
62 #endif
63 
64     /// Constants to determine byte ordering on-the-fly.
65     /// Credit to Cristoph
66     /// @see http://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine
67     static const union {
68         unsigned char bytes[4];
69         uint32_t value;
70     } B4_host_order = { { 0, 1, 2, 3 } };
71     /// Constants to determine byte ordering of 2-byte values.
72     static const union {
73         unsigned char bytes[2];
74         uint16_t value;
75     } B2_host_order = { { 0, 1 } };
76 
77     /** Return the byte ordering scheme, translating
78         "native" ordering into big or little or an exception.
79         @return Big or Little endian. */
realArrangement() const80     SampleArrangement AudioFormat::realArrangement () const {
81         if (arrangement != SampleArrangement::Native) {
82             return arrangement;
83         }
84         return nativeArrangement (bits);
85     };
86 
87     /** Determine whether the byte ordering matches the native ordering. */
isNativeArrangement() const88     bool AudioFormat::isNativeArrangement () const {
89         if (arrangement == SampleArrangement::Native || bits == 8)
90             return true;
91         try {
92             return (arrangement == nativeArrangement (bits));
93         } catch (...) {
94             return false;
95         }
96     }
97 
98     /** Determine the byte ordering of the machine. */
nativeArrangement(int bits)99     SampleArrangement AudioFormat::nativeArrangement (int bits) {
100         if (bits == 32) {
101             if (B4_host_order.value == B4_LITTLE_ENDIAN)
102                 return SampleArrangement::Little;
103             if (B4_host_order.value == B4_BIG_ENDIAN)
104                 return SampleArrangement::Big;
105         } else {
106             if (B2_host_order.value == B2_LITTLE_ENDIAN)
107                 return SampleArrangement::Little;
108             if (B2_host_order.value == B2_BIG_ENDIAN)
109                 return SampleArrangement::Big;
110         }
111         throw AudioException ("Weird host byte ordering");
112     }
113 
114 
115 
116     /** Check if audio settings are valid.
117         @param settings Where to send the audio output.
118         @return True if valid, false otherwise.
119         @throw AudioException or a derivative if opening/setting up
120         the device failed. */
isValidOutput(const AudioSettings & settings)121     bool Output::isValidOutput(const AudioSettings &settings) {
122 #if (defined(WITH_FFMPEG) || defined(WITH_GSTREAMER)) && defined(WITH_AVFOUNDATION)
123         if (strcasecmp (settings.output_library, "avfoundation") == 0)
124             return true;
125 #endif
126 #ifndef WITH_AVFOUNDATION
127         unique_ptr <Output> output { getOutput(settings, AudioFormat {}) };
128         return (output && output);
129 #endif
130         return true;
131     }
132 
133 
134     /** Check if audio settings are valid.
135      @param settings Where to send the audio output.
136      @return True if valid, false otherwise.
137      @throw AudioException or a derivative if opening/setting up
138      the device failed. */
outputCanCrossfade(const AudioSettings & settings)139     bool Output::outputCanCrossfade (const AudioSettings &settings) {
140 #if (defined(WITH_FFMPEG) || defined(WITH_GSTREAMER)) && defined(WITH_AVFOUNDATION)
141         if (strcasecmp (settings.output_library, "avfoundation") == 0)
142             return true;
143 #endif
144 #ifndef WITH_AVFOUNDATION
145         unique_ptr <Output> output { getOutput(settings, AudioFormat {}) };
146         unique_ptr <Output> output_2 { getOutput (settings, AudioFormat {}) };
147         return output && output_2;
148 #endif
149         return true;
150     }
151 
152 
153     /** Factory gets whatever kind of output is best, or
154         requested by the audio settings.
155         @param settings Where to send the audio output.
156         @param format The form in which the audio data will arrive.
157         @return A ready-to-go output device, or a nullptr if there is no
158         matching device.
159         @throw AudioException or a derivative if opening/setting up
160         the device failed. */
getOutput(const AudioSettings & settings,const AudioFormat & format)161     Output *Output::getOutput (const AudioSettings &settings,
162                                const AudioFormat &format) {
163 #ifdef WITH_LIBSDL
164         if (settings.output_library.empty() ||
165             strcasecmp (settings.output_library, "libsdl") == 0) {
166             return new LibsdlOutput (settings, format);
167         }
168 #endif
169 #ifdef WITH_LIBAO
170         if (settings.output_library.empty() ||
171             strcasecmp (settings.output_library, "libao") == 0) {
172             return new LibaoOutput (settings, format);
173         }
174 #endif
175 #ifdef WITH_GSTREAMER
176         if (settings.output_library.empty() ||
177             strcasecmp (settings.output_library, "gstreamer") == 0) {
178             return new GstreamerOutput (settings, format);
179         }
180 #endif
181 #ifdef WITH_LIBAVDEVICE
182         if (settings.output_library.empty() ||
183             strcasecmp (settings.output_library, "libavdevice") == 0) {
184             return new AvDeviceOutput (settings, format);
185         }
186 #endif
187         return nullptr;
188     };
189 
190     /** Report audio libraries in use, and their versions.
191         @param verbose If > 0, include more detail. */
reportLibrariesAndVersions(int verbose)192     void Output::reportLibrariesAndVersions (int verbose) {
193         using namespace std;
194 
195         cerr << "  Audio outputs:\n";
196 #ifdef WITH_LIBSDL
197         {
198             SDL_version compiled;
199 
200             SDL_VERSION(&compiled);
201             cerr << "    libsdl\n"
202             << "      compile version " << (unsigned) compiled.major << '.' << (unsigned) compiled.minor << '.' << (unsigned) compiled.patch << '\n';
203 #ifdef HAVE_SDL_GETVERSION
204             SDL_version linked;
205             SDL_GetVersion(&linked);
206             cerr << "      runtime version " << (unsigned) linked.major << '.' << (unsigned) linked.minor << '.' << (unsigned) linked.patch << '\n';
207 #endif
208         }
209 #endif
210 #ifdef WITH_LIBAO
211         cerr << "    libao\n";
212 #endif
213 #ifdef WITH_LIBAVDEVICE
214         {
215             unsigned compiled = LIBAVDEVICE_BUILD;
216             unsigned linked = avdevice_version();
217 
218             cerr << "    libavdevice\n"
219             << "      compile version " << AV_VERSION_MAJOR (compiled) << '.' << AV_VERSION_MINOR (compiled) << '.' << AV_VERSION_MICRO (compiled) << '\n'
220             << "      runtime version " << AV_VERSION_MAJOR (linked) << '.' << AV_VERSION_MINOR (linked) << '.' << AV_VERSION_MICRO (linked) << '\n';
221             if (verbose) {
222                 cerr << "      configuration " << avdevice_configuration() << '\n';
223             }
224         }
225 #endif
226 #ifdef WITH_GSTREAMER
227         (void) verbose;
228         cerr << "    gstreamer\n";
229 #endif
230     };
231 
232 
233 
234 
235 
236     /** Initialize the audio libraries on startup, if required. */
Initializer()237     Initializer::Initializer () {
238 #ifdef WITH_LIBSDL
239         if (SDL_Init (SDL_INIT_AUDIO) != 0) {
240             throw InitializationException ("libsdl: SDL_Init", SDL_GetError());
241         }
242 #endif
243 #ifdef WITH_LIBAO
244         ao_initialize ();
245 #endif
246     };
247 
248     /** Uninitialize the audio libraries prior to shutdown. */
~Initializer()249     Initializer::~Initializer () {
250 #ifdef WITH_LIBAO
251         ao_shutdown ();
252 #endif
253 #ifdef WITH_LIBSDL
254         SDL_Quit();
255 #endif
256     };
257 
258 }
259 
260