|
| 1 | +// |
| 2 | +// Audio.swift |
| 3 | +// SwiftSDL2 |
| 4 | +// |
| 5 | +// Created by sunlubo on 2018/8/12. |
| 6 | +// |
| 7 | + |
| 8 | +import CSDL2 |
| 9 | + |
| 10 | +// MARK: - AudioFormat |
| 11 | + |
| 12 | +/// Audio format flags. |
| 13 | +/// |
| 14 | +/// These are what the 16 bits in SDL_AudioFormat currently mean... |
| 15 | +/// (Unspecified bits are always zero). |
| 16 | +/// |
| 17 | +/// ``` |
| 18 | +/// ++-----------------------sample is signed if set |
| 19 | +/// || |
| 20 | +/// || ++-----------sample is bigendian if set |
| 21 | +/// || || |
| 22 | +/// || || ++---sample is float if set |
| 23 | +/// || || || |
| 24 | +/// || || || +---sample bit size---+ |
| 25 | +/// || || || | | |
| 26 | +/// 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 |
| 27 | +/// ``` |
| 28 | +public typealias AudioFormat = SDL_AudioFormat |
| 29 | + |
| 30 | +extension AudioFormat { |
| 31 | + // MARK: - 8-bit support |
| 32 | + |
| 33 | + // signed 8-bit samples |
| 34 | + public static let s8 = UInt16(AUDIO_S8) |
| 35 | + // unsigned 8-bit samples |
| 36 | + public static let u8 = UInt16(AUDIO_U8) |
| 37 | + |
| 38 | + // MARK: - 16-bit support |
| 39 | + |
| 40 | + // signed 16-bit samples in little-endian byte order |
| 41 | + public static let s16lsb = UInt16(AUDIO_S16LSB) |
| 42 | + // signed 16-bit samples in big-endian byte order |
| 43 | + public static let s16msb = UInt16(AUDIO_S16MSB) |
| 44 | + // signed 16-bit samples in native byte order |
| 45 | + public static let s16sys = UInt16(AUDIO_S16SYS) |
| 46 | + // AUDIO_S16LSB |
| 47 | + public static let s16 = UInt16(AUDIO_S16) |
| 48 | + // unsigned 16-bit samples in little-endian byte order |
| 49 | + public static let u16lsb = UInt16(AUDIO_U16LSB) |
| 50 | + // unsigned 16-bit samples in big-endian byte order |
| 51 | + public static let u16msb = UInt16(AUDIO_U16MSB) |
| 52 | + // unsigned 16-bit samples in native byte order |
| 53 | + public static let u16sys = UInt16(AUDIO_U16SYS) |
| 54 | + // AUDIO_U16LSB |
| 55 | + public static let u16 = UInt16(AUDIO_U16) |
| 56 | + |
| 57 | + // MARK: - 32-bit support |
| 58 | + |
| 59 | + // 32-bit integer samples in little-endian byte order |
| 60 | + public static let s32lsb = UInt16(AUDIO_S32LSB) |
| 61 | + // 32-bit integer samples in big-endian byte order |
| 62 | + public static let s32msb = UInt16(AUDIO_S32MSB) |
| 63 | + // 32-bit integer samples in native byte order |
| 64 | + public static let s32sys = UInt16(AUDIO_F32SYS) |
| 65 | + // AUDIO_S32LSB |
| 66 | + public static let s32 = UInt16(AUDIO_S32) |
| 67 | + |
| 68 | + // MARK: - float support |
| 69 | + |
| 70 | + // 32-bit floating point samples in little-endian byte order |
| 71 | + public static let f32lsb = UInt16(AUDIO_F32LSB) |
| 72 | + // 32-bit floating point samples in big-endian byte order |
| 73 | + public static let f32msb = UInt16(AUDIO_F32MSB) |
| 74 | + // 32-bit floating point samples in native byte order |
| 75 | + public static let f32sys = UInt16(AUDIO_F32SYS) |
| 76 | + // AUDIO_F32LSB |
| 77 | + public static let f32 = UInt16(AUDIO_F32) |
| 78 | +} |
| 79 | + |
| 80 | +// MARK: - AudioStatus |
| 81 | + |
| 82 | +/// Audio state |
| 83 | +public typealias AudioStatus = SDL_AudioStatus |
| 84 | + |
| 85 | +extension AudioStatus { |
| 86 | + // audio device is stopped |
| 87 | + public static let stopped = SDL_AUDIO_STOPPED |
| 88 | + // audio device is playing |
| 89 | + public static let playing = SDL_AUDIO_PLAYING |
| 90 | + // audio device is paused |
| 91 | + public static let paused = SDL_AUDIO_PAUSED |
| 92 | +} |
| 93 | + |
| 94 | +// MARK: - AudioSpec |
| 95 | + |
| 96 | +/// The calculated values in this structure are calculated by SDL_OpenAudio(). |
| 97 | +/// |
| 98 | +/// For multi-channel audio, the default SDL channel mapping is: |
| 99 | +/// - 2: FL FR (stereo) |
| 100 | +/// - 3: FL FR LFE (2.1 surround) |
| 101 | +/// - 4: FL FR BL BR (quad) |
| 102 | +/// - 5: FL FR FC BL BR (quad + center) |
| 103 | +/// - 6: FL FR FC LFE SL SR (5.1 surround - last two can also be BL BR) |
| 104 | +/// - 7: FL FR FC LFE BC SL SR (6.1 surround) |
| 105 | +/// - 8: FL FR FC LFE BL BR SL SR (7.1 surround) |
| 106 | +public typealias AudioSpec = SDL_AudioSpec |
| 107 | + |
| 108 | +public typealias AudioCallback = SDL_AudioCallback |
| 109 | + |
| 110 | +/// Allow change flags |
| 111 | +/// |
| 112 | +/// Which audio format changes are allowed when opening a device. |
| 113 | +public struct AudioAllowedChangeFlags: OptionSet { |
| 114 | + public let rawValue: Int32 |
| 115 | + |
| 116 | + public init(rawValue: Int32) { |
| 117 | + self.rawValue = rawValue |
| 118 | + } |
| 119 | + |
| 120 | + public static let frequency = AudioAllowedChangeFlags(rawValue: SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) |
| 121 | + public static let format = AudioAllowedChangeFlags(rawValue: SDL_AUDIO_ALLOW_FORMAT_CHANGE) |
| 122 | + public static let channels = AudioAllowedChangeFlags(rawValue: SDL_AUDIO_ALLOW_CHANNELS_CHANGE) |
| 123 | + public static let any = [.frequency, .format, .channels] as AudioAllowedChangeFlags |
| 124 | +} |
| 125 | + |
| 126 | +public final class AudioDevice { |
| 127 | + let deviceId: SDL_AudioDeviceID |
| 128 | + |
| 129 | + /// the desired output format |
| 130 | + let desiredSpec: AudioSpec |
| 131 | + /// the obtained output format |
| 132 | + let obtainedSpec: AudioSpec |
| 133 | + |
| 134 | + /// Create and open a specific audio device. Passing in a device name of nil requests |
| 135 | + /// the most reasonable default (and is equivalent to calling SDL_OpenAudio()). |
| 136 | + /// |
| 137 | + /// - Parameters: |
| 138 | + /// - device: device name |
| 139 | + //// - isCapture: non-zero to specify a device should be opened for recording, not playback |
| 140 | + /// - spec: the desired output format |
| 141 | + /// - flags: 0, or one or more flags OR'd together |
| 142 | + /// - Throws: SDLError |
| 143 | + public init( |
| 144 | + device: String?, |
| 145 | + isCapture: Bool, |
| 146 | + spec: AudioSpec, |
| 147 | + flags: AudioAllowedChangeFlags |
| 148 | + ) throws { |
| 149 | + var spec = spec |
| 150 | + var specPtr = UnsafeMutablePointer<AudioSpec>.allocate(capacity: 1) |
| 151 | + defer { specPtr.deallocate() } |
| 152 | + let ret = SDL_OpenAudioDevice(device, isCapture ? 1 : 0, &spec, specPtr, flags.rawValue) |
| 153 | + if ret == 0 { throw SDLError(code: Int32(ret)) } |
| 154 | + self.deviceId = ret |
| 155 | + self.desiredSpec = spec |
| 156 | + self.obtainedSpec = specPtr.pointee |
| 157 | + } |
| 158 | + |
| 159 | + /// The current audio state of an audio device. |
| 160 | + public var status: AudioStatus { |
| 161 | + return SDL_GetAudioDeviceStatus(deviceId) |
| 162 | + } |
| 163 | + |
| 164 | + /// Pause audio playback on a specified device. |
| 165 | + func pause() { |
| 166 | + SDL_PauseAudioDevice(deviceId, 1) |
| 167 | + } |
| 168 | + |
| 169 | + /// Resume audio playback on a specified device. |
| 170 | + func resume() { |
| 171 | + SDL_PauseAudioDevice(deviceId, 0) |
| 172 | + } |
| 173 | + |
| 174 | + /// Lock out the audio callback function for a specified device. |
| 175 | + public func lock() { |
| 176 | + SDL_LockAudioDevice(deviceId) |
| 177 | + } |
| 178 | + |
| 179 | + /// Unlock the audio callback function for a specified device. |
| 180 | + public func unlock() { |
| 181 | + SDL_UnlockAudioDevice(deviceId) |
| 182 | + } |
| 183 | + |
| 184 | + /// Get the number of available devices exposed by the current driver. |
| 185 | + /// Only valid after a successfully initializing the audio subsystem. |
| 186 | + /// |
| 187 | + /// In many common cases, when this function returns a value <= 0, it can still |
| 188 | + /// successfully open the default device (NULL for first argument of |
| 189 | + /// SDL_OpenAudioDevice()). |
| 190 | + /// |
| 191 | + /// - Parameter isCapture: zero to request playback devices, non-zero to request recording devices |
| 192 | + /// - Returns: Returns -1 if an explicit list of devices can't be determined; this is |
| 193 | + /// not an error. For example, if SDL is set up to talk to a remote audio |
| 194 | + /// server, it can't list every one available on the Internet, but it will |
| 195 | + /// still allow a specific host to be specified to SDL_OpenAudioDevice(). |
| 196 | + public static func deviceCount(isCapture: Bool) -> Int { |
| 197 | + return Int(SDL_GetNumAudioDevices(isCapture ? 1 : 0)) |
| 198 | + } |
| 199 | + |
| 200 | + /// Get the human-readable name of a specific audio device. |
| 201 | + /// |
| 202 | + /// - Parameters: |
| 203 | + /// - index: the index of the audio device; the value ranges from 0 to deviceCount - 1 |
| 204 | + /// - isCapture: non-zero to specify a device that has recording capability |
| 205 | + /// - Returns: Returns the name of the audio device at the requested index, or nil on error. |
| 206 | + public static func deviceName(index: Int, isCapture: Bool) -> String? { |
| 207 | + assert(index < deviceCount(isCapture: isCapture), "Must be a value between 0 and (number of audio devices-1).") |
| 208 | + if let strBytes = SDL_GetAudioDeviceName(Int32(index), isCapture ? 1 : 0) { |
| 209 | + return String(cString: strBytes) |
| 210 | + } |
| 211 | + return nil |
| 212 | + } |
| 213 | + |
| 214 | + /// Gget the number of built-in audio drivers. |
| 215 | + /// |
| 216 | + /// - Returns: Returns the number of built-in audio drivers. |
| 217 | + public static func driverCount(isCapture: Bool) -> Int { |
| 218 | + return Int(SDL_GetNumAudioDrivers()) |
| 219 | + } |
| 220 | + |
| 221 | + /// Get the human-readable name of a specific audio driver. |
| 222 | + /// |
| 223 | + /// - Parameters: |
| 224 | + /// - index: the index of the audio driver; the value ranges from 0 to driverCount - 1 |
| 225 | + /// - Returns: Returns the name of the audio driver at the requested index, or nil if an invalid index was specified. |
| 226 | + public static func driverName(index: Int) -> String? { |
| 227 | + if let strBytes = SDL_GetAudioDriver(Int32(index)) { |
| 228 | + return String(cString: strBytes) |
| 229 | + } |
| 230 | + return nil |
| 231 | + } |
| 232 | + |
| 233 | + deinit { |
| 234 | + SDL_CloseAudioDevice(deviceId) |
| 235 | + } |
| 236 | +} |
| 237 | + |
| 238 | +/// Mix audio data in a specified format. |
| 239 | +/// |
| 240 | +/// - Parameters: |
| 241 | +/// - src: the source audio buffer to be mixed |
| 242 | +/// - dst: the destination for the mixed audio |
| 243 | +/// - format: the desired audio format |
| 244 | +/// - len: the length of the audio buffer in bytes |
| 245 | +/// - volume: ranges from 0 - 128, and should be set to SDL_MIX_MAXVOLUME for full audio volume |
| 246 | +public func mixAudioFormat( |
| 247 | + src: UnsafePointer<UInt8>, |
| 248 | + dst: UnsafeMutablePointer<UInt8>, |
| 249 | + format: AudioFormat, |
| 250 | + len: Int, |
| 251 | + volume: Int |
| 252 | +) { |
| 253 | + SDL_MixAudioFormat(dst, src, format, UInt32(len), Int32(volume)) |
| 254 | +} |
0 commit comments