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