2 * OpenAL cross platform audio library
3 * Copyright (C) 2018 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
31 #include "backends/base.h"
34 typedef struct ALCsdl2Backend
{
35 DERIVE_FROM_TYPE(ALCbackend
);
37 SDL_AudioDeviceID deviceID
;
41 enum DevFmtChannels FmtChans
;
42 enum DevFmtType FmtType
;
46 static void ALCsdl2Backend_Construct(ALCsdl2Backend
*self
, ALCdevice
*device
);
47 static void ALCsdl2Backend_Destruct(ALCsdl2Backend
*self
);
48 static ALCenum
ALCsdl2Backend_open(ALCsdl2Backend
*self
, const ALCchar
*name
);
49 static ALCboolean
ALCsdl2Backend_reset(ALCsdl2Backend
*self
);
50 static ALCboolean
ALCsdl2Backend_start(ALCsdl2Backend
*self
);
51 static void ALCsdl2Backend_stop(ALCsdl2Backend
*self
);
52 static DECLARE_FORWARD2(ALCsdl2Backend
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
53 static DECLARE_FORWARD(ALCsdl2Backend
, ALCbackend
, ALCuint
, availableSamples
)
54 static DECLARE_FORWARD(ALCsdl2Backend
, ALCbackend
, ClockLatency
, getClockLatency
)
55 static void ALCsdl2Backend_lock(ALCsdl2Backend
*self
);
56 static void ALCsdl2Backend_unlock(ALCsdl2Backend
*self
);
57 DECLARE_DEFAULT_ALLOCATORS(ALCsdl2Backend
)
59 DEFINE_ALCBACKEND_VTABLE(ALCsdl2Backend
);
61 static const ALCchar defaultDeviceName
[] = "Default device";
63 static void ALCsdl2Backend_Construct(ALCsdl2Backend
*self
, ALCdevice
*device
)
65 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
66 SET_VTABLE2(ALCsdl2Backend
, ALCbackend
, self
);
69 self
->frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
70 self
->Frequency
= device
->Frequency
;
71 self
->FmtChans
= device
->FmtChans
;
72 self
->FmtType
= device
->FmtType
;
73 self
->UpdateSize
= device
->UpdateSize
;
76 static void ALCsdl2Backend_Destruct(ALCsdl2Backend
*self
)
79 SDL_CloseAudioDevice(self
->deviceID
);
82 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
86 static void ALCsdl2Backend_audioCallback(void *ptr
, Uint8
*stream
, int len
)
88 ALCsdl2Backend
*self
= (ALCsdl2Backend
*)ptr
;
89 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
91 assert((len
% self
->frameSize
) == 0);
92 aluMixData(device
, stream
, len
/ self
->frameSize
);
95 static ALCenum
ALCsdl2Backend_open(ALCsdl2Backend
*self
, const ALCchar
*name
)
97 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
98 SDL_AudioSpec want
, have
;
100 want
.freq
= device
->Frequency
;
101 want
.format
= AUDIO_F32
;
102 want
.channels
= (device
->FmtChans
== DevFmtMono
) ? 1 : 2;
103 want
.samples
= device
->UpdateSize
;
104 want
.callback
= ALCsdl2Backend_audioCallback
;
105 want
.userdata
= self
;
107 if (name
&& strcmp(name
, defaultDeviceName
) == 0)
108 name
= NULL
; // Passing NULL to SDL_OpenAudioDevice is special and will NOT select the first
109 // device in the list.
110 self
->deviceID
= SDL_OpenAudioDevice(name
, 0, &want
, &have
, SDL_AUDIO_ALLOW_ANY_CHANGE
);
111 if(self
->deviceID
== 0)
112 return ALC_INVALID_VALUE
;
114 device
->Frequency
= have
.freq
;
115 if(have
.channels
== 1)
116 device
->FmtChans
= DevFmtMono
;
117 else if(have
.channels
== 2)
118 device
->FmtChans
= DevFmtStereo
;
121 ERR("Got unhandled SDL channel count: %d\n", (int)have
.channels
);
122 return ALC_INVALID_VALUE
;
126 case AUDIO_U8
: device
->FmtType
= DevFmtUByte
; break;
127 case AUDIO_S8
: device
->FmtType
= DevFmtByte
; break;
128 case AUDIO_U16SYS
: device
->FmtType
= DevFmtUShort
; break;
129 case AUDIO_S16SYS
: device
->FmtType
= DevFmtShort
; break;
130 case AUDIO_S32SYS
: device
->FmtType
= DevFmtInt
; break;
131 case AUDIO_F32SYS
: device
->FmtType
= DevFmtFloat
; break;
133 ERR("Got unsupported SDL format: 0x%04x\n", have
.format
);
134 return ALC_INVALID_VALUE
;
136 device
->UpdateSize
= have
.samples
;
137 device
->NumUpdates
= 2; /* SDL always (tries to) use two periods. */
139 self
->frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
140 self
->Frequency
= device
->Frequency
;
141 self
->FmtChans
= device
->FmtChans
;
142 self
->FmtType
= device
->FmtType
;
143 self
->UpdateSize
= device
->UpdateSize
;
145 alstr_copy_cstr(&device
->DeviceName
, name
? name
: defaultDeviceName
);
150 static ALCboolean
ALCsdl2Backend_reset(ALCsdl2Backend
*self
)
152 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
153 device
->Frequency
= self
->Frequency
;
154 device
->FmtChans
= self
->FmtChans
;
155 device
->FmtType
= self
->FmtType
;
156 device
->UpdateSize
= self
->UpdateSize
;
157 device
->NumUpdates
= 2;
158 SetDefaultWFXChannelOrder(device
);
162 static ALCboolean
ALCsdl2Backend_start(ALCsdl2Backend
*self
)
164 SDL_PauseAudioDevice(self
->deviceID
, 0);
168 static void ALCsdl2Backend_stop(ALCsdl2Backend
*self
)
170 SDL_PauseAudioDevice(self
->deviceID
, 1);
173 static void ALCsdl2Backend_lock(ALCsdl2Backend
*self
)
175 SDL_LockAudioDevice(self
->deviceID
);
178 static void ALCsdl2Backend_unlock(ALCsdl2Backend
*self
)
180 SDL_UnlockAudioDevice(self
->deviceID
);
184 typedef struct ALCsdl2BackendFactory
{
185 DERIVE_FROM_TYPE(ALCbackendFactory
);
186 } ALCsdl2BackendFactory
;
187 #define ALCsdl2BACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsdl2BackendFactory, ALCbackendFactory) } }
189 ALCbackendFactory
*ALCsdl2BackendFactory_getFactory(void);
191 static ALCboolean
ALCsdl2BackendFactory_init(ALCsdl2BackendFactory
*self
);
192 static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory
*self
);
193 static ALCboolean
ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory
*self
, ALCbackend_Type type
);
194 static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory
*self
, enum DevProbe type
);
195 static ALCbackend
* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
196 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsdl2BackendFactory
);
199 ALCbackendFactory
*ALCsdl2BackendFactory_getFactory(void)
201 static ALCsdl2BackendFactory factory
= ALCsdl2BACKENDFACTORY_INITIALIZER
;
202 return STATIC_CAST(ALCbackendFactory
, &factory
);
206 static ALCboolean
ALCsdl2BackendFactory_init(ALCsdl2BackendFactory
* UNUSED(self
))
208 if(SDL_InitSubSystem(SDL_INIT_AUDIO
) == 0)
213 static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory
* UNUSED(self
))
215 SDL_QuitSubSystem(SDL_INIT_AUDIO
);
218 static ALCboolean
ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory
* UNUSED(self
), ALCbackend_Type type
)
220 if(type
== ALCbackend_Playback
)
225 static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory
* UNUSED(self
), enum DevProbe type
)
229 if(type
!= ALL_DEVICE_PROBE
)
232 num_devices
= SDL_GetNumAudioDevices(SDL_FALSE
);
234 AppendAllDevicesList(defaultDeviceName
);
235 for(i
= 0;i
< num_devices
;++i
)
236 AppendAllDevicesList(SDL_GetAudioDeviceName(i
, SDL_FALSE
));
239 static ALCbackend
* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
241 if(type
== ALCbackend_Playback
)
243 ALCsdl2Backend
*backend
;
244 NEW_OBJ(backend
, ALCsdl2Backend
)(device
);
245 if(!backend
) return NULL
;
246 return STATIC_CAST(ALCbackend
, backend
);