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 riverd.sndfile; 39 import riverd.soundio; 40 import aurorafw.core.logger; 41 import aurorafw.stdx.exception; 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 //TODO: Need implementation 189 throw new NotImplementedException("Not yet implemented!"); 190 } 191 192 void setOutputDevice(AudioDevice outputDevice) { 193 //TODO: Need implementation 194 throw new NotImplementedException("Not yet implemented!"); 195 } 196 197 void flushEvents() { 198 soundio_flush_events(soundio); 199 } 200 private: 201 this() { 202 log("Initializing AudioBackend..."); 203 204 // Get versions from SoundIo and SNDFile 205 log("SoundIo version: ", soundio_version_string.fromStringz); 206 207 char[128] sndfileVersion; 208 sf_command(null, SFC_GET_LIB_VERSION, sndfileVersion.ptr, sndfileVersion.sizeof); 209 log("SNDFile version: ", sndfileVersion.ptr.fromStringz); 210 211 // Initializes SoundIo 212 soundio = soundio_create(); 213 if(!soundio) 214 throw new SNDFileException(SoundIoError.SoundIoErrorNoMem); 215 216 // Connects to a backend 217 catchSOUNDIOProblem(cast(SoundIoError)soundio_connect(soundio)); 218 219 log("Connection to backend successfull. Current backend: ", soundio_backend_name(soundio.current_backend).fromStringz); 220 221 // Gets the number of available devices 222 flushEvents(); 223 immutable int outputNum = soundio_output_device_count(soundio), inputNum = soundio_input_device_count(soundio); 224 log("AudioBackend initialized. Num. of ", 225 "available audio devices: ", text(outputNum + inputNum), " (", 226 outputNum, " output devices, ", 227 inputNum, " input devices.)"); 228 } 229 230 ~this() { 231 // Terminates SoundIo 232 soundio_destroy(soundio); 233 } 234 235 static AudioBackend _instance; 236 package SoundIo* soundio; 237 } 238 239 void catchSOUNDIOProblem(const SoundIoError error) { 240 if(error != SoundIoError.SoundIoErrorNone) 241 throw new SoundioException(error); 242 } 243 244 void catchSNDFILEProblem(const int error) { 245 if(error != SF_ERR_NO_ERROR) 246 throw new SNDFileException(error); 247 }