1 /* 2 __ 3 / _| 4 __ _ _ _ _ __ ___ _ __ __ _ | |_ ___ ___ ___ 5 / _` | | | | '__/ _ \| '__/ _` | | _/ _ \/ __/ __| 6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \ 7 \__,_|\__,_|_| \___/|_| \__,_| |_| \___/|___/___/ 8 9 Copyright (C) 2018-2019 Aurora Free Open Source Software. 10 11 This file is part of the Aurora Free Open Source Software. This 12 organization promote free and open source software that you can 13 redistribute and/or modify under the terms of the GNU Lesser General 14 Public License Version 3 as published by the Free Software Foundation or 15 (at your option) any later version approved by the Aurora Free Open Source 16 Software Organization. The license is available in the package root path 17 as 'LICENSE' file. Please review the following information to ensure the 18 GNU Lesser General Public License version 3 requirements will be met: 19 https://www.gnu.org/licenses/lgpl.html . 20 21 Alternatively, this file may be used under the terms of the GNU General 22 Public License version 3 or later as published by the Free Software 23 Foundation. Please review the following information to ensure the GNU 24 General Public License requirements will be met: 25 http://www.gnu.org/licenses/gpl-3.0.html. 26 27 NOTE: All products, services or anything associated to trademarks and 28 service marks used or referenced on this file are the property of their 29 respective companies/owners or its subsidiaries. Other names and brands 30 may be claimed as the property of others. 31 32 For more info about intellectual property visit: aurorafoss.org or 33 directly send an email to: contact (at) aurorafoss.org . 34 */ 35 36 module aurorafw.audio.backend; 37 38 import aurorafw.audio.sndfile; 39 import aurorafw.audio.soundio; 40 import aurorafw.core.debugmanager; 41 import aurorafw.core.logger; 42 43 import std.conv : text, to; 44 45 import std.stdio; 46 import std.string; 47 48 class SoundioException : Throwable { 49 this(SoundIoError error) { 50 super("SoundIo error: " ~ to!string(soundio_strerror(error))); 51 } 52 } 53 54 class SNDFileException : Throwable { 55 this(int error) { 56 super("SNDFile error: " ~ to!string(sf_error_number(error))); 57 } 58 } 59 60 class AudioDevice { 61 this() { 62 SoundIo* soundio = AudioBackend.getInstance().soundio; 63 this(soundio_get_output_device(soundio, soundio_default_output_device_index(soundio))); 64 } 65 66 this(SoundIoDevice* device) { 67 catchSOUNDIOProblem(cast(SoundIoError)device.probe_error); 68 69 this.device = device; 70 } 71 72 ~this() { 73 soundio_device_unref(device); 74 } 75 76 @property const string name() { 77 return to!string(device.name.fromStringz); 78 } 79 80 @property const int maxInputChannels() { 81 return isInputDevice() ? device.current_layout.channel_count : 0; 82 } 83 84 @property const int maxOutputChannels() { 85 return isOutputDevice() ? device.current_layout.channel_count : 0; 86 } 87 88 @property const double currentLatency() { 89 return device.software_latency_current; 90 } 91 92 @property const double minLatency() { 93 return device.software_latency_min; 94 } 95 96 @property const double maxLatency() { 97 return device.software_latency_max; 98 } 99 100 @property const int sampleRate() { 101 return device.sample_rate_current; 102 } 103 104 @property const bool isInputDevice() { 105 return device.aim == SoundIoDeviceAim.SoundIoDeviceAimInput; 106 } 107 108 @property const bool isOutputDevice() { 109 return device.aim == SoundIoDeviceAim.SoundIoDeviceAimOutput; 110 } 111 112 @property const bool isDefaultInputDevice() { 113 SoundIo* soundio = cast(SoundIo*)device.soundio; 114 SoundIoDevice* defaultDevice = soundio_get_input_device(soundio, soundio_default_input_device_index(soundio)); 115 char* id = defaultDevice.id; 116 soundio_device_unref(defaultDevice); 117 118 return id == device.id && isInputDevice(); 119 } 120 121 @property const bool isDefaultOutputDevice() { 122 SoundIo* soundio = cast(SoundIo*)device.soundio; 123 SoundIoDevice* defaultDevice = soundio_get_output_device(soundio, soundio_default_output_device_index(soundio)); 124 char* id = defaultDevice.id; 125 soundio_device_unref(defaultDevice); 126 127 return id == device.id && isOutputDevice(); 128 } 129 130 package SoundIoDevice* device; 131 } 132 133 class AudioListener { 134 public: 135 static ref AudioListener getInstance() { 136 if(!_instance) 137 _instance = new AudioListener; 138 139 return _instance; 140 } 141 142 //import aurorafw.math.vector : Vector3d; 143 //Vector3d position = new Vector3d, direction = new Vector3d(0, 0, -1); 144 private: 145 __gshared AudioListener _instance; 146 this() {} 147 } 148 149 class AudioBackend { 150 public: 151 float globalVolume = 1f; 152 153 static ref AudioBackend getInstance() { 154 if(!_instance) 155 _instance = new AudioBackend(); 156 157 return _instance; 158 } 159 160 immutable (AudioDevice[]) getDevices() { 161 immutable (AudioDevice[]) devices = getOutputDevices ~ getInputDevices; 162 return devices; 163 } 164 165 immutable (AudioDevice[]) getOutputDevices() { 166 immutable int count = soundio_output_device_count(soundio); 167 AudioDevice[] devices; 168 169 for(int i = 0; i < count; i++) { 170 devices ~= new AudioDevice(soundio_get_output_device(soundio, i)); 171 } 172 173 return cast(immutable)devices; 174 } 175 176 immutable (AudioDevice[]) getInputDevices() { 177 immutable int count = soundio_input_device_count(soundio); 178 AudioDevice[] devices; 179 180 for(int i = 0; i < count; i++) { 181 devices ~= new AudioDevice(soundio_get_input_device(soundio, i)); 182 } 183 184 return cast(immutable)devices; 185 } 186 187 void setInputDevice(AudioDevice inputDevice) { 188 pragma(msg, debugMsgPrefix, "TODO: Implement setInputDevice()"); 189 } 190 191 void setOutputDevice(AudioDevice outputDevice) { 192 pragma(msg, debugMsgPrefix, "TODO: Implement setOutputDevice()"); 193 } 194 195 void flushEvents() { 196 soundio_flush_events(soundio); 197 } 198 private: 199 this() { 200 log("Initializing AudioBackend..."); 201 202 // Get versions from SoundIo and SNDFile 203 log("SoundIo version: ", soundio_version_string.fromStringz); 204 205 char[128] sndfileVersion; 206 sf_command(null, SFC_GET_LIB_VERSION, sndfileVersion.ptr, sndfileVersion.sizeof); 207 log("SNDFile version: ", sndfileVersion.ptr.fromStringz); 208 209 // Initializes SoundIo 210 soundio = soundio_create(); 211 if(!soundio) 212 throw new SNDFileException(SoundIoError.SoundIoErrorNoMem); 213 214 // Connects to a backend 215 catchSOUNDIOProblem(cast(SoundIoError)soundio_connect(soundio)); 216 217 log("Connection to backend successfull. Current backend: ", soundio_backend_name(soundio.current_backend).fromStringz); 218 219 // Gets the number of available devices 220 flushEvents(); 221 immutable int outputNum = soundio_output_device_count(soundio), inputNum = soundio_input_device_count(soundio); 222 log("AudioBackend initialized. Num. of ", 223 "available audio devices: ", text(outputNum + inputNum), " (", 224 outputNum, " output devices, ", 225 inputNum, " input devices.)"); 226 } 227 228 ~this() { 229 // Terminates SoundIo 230 soundio_destroy(soundio); 231 } 232 233 static AudioBackend _instance; 234 package SoundIo* soundio; 235 } 236 237 void catchSOUNDIOProblem(const SoundIoError error) { 238 if(error != SoundIoError.SoundIoErrorNone) 239 throw new SoundioException(error); 240 } 241 242 void catchSNDFILEProblem(const int error) { 243 if(error != SF_ERR_NO_ERROR) 244 throw new SNDFileException(error); 245 }