This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_nasaudio.c
292 lines (251 loc) · 8.58 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2006 Sam Lantinga
4
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Lesser General Public
7
License as published by the Free Software Foundation; either
8
version 2.1 of the License, or (at your option) any later version.
9
10
11
12
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Lesser General Public License for more details.
14
15
16
17
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19
Sam Lantinga
20
slouken@libsdl.org
21
22
23
24
25
This driver was written by:
Erik Inge Bolsø
knan@mo.himolde.no
*/
26
#include "SDL_config.h"
27
28
29
30
31
32
/* Allow access to a raw mixing buffer */
#include <signal.h>
#include <unistd.h>
33
#include "SDL_timer.h"
34
#include "SDL_audio.h"
35
36
#include "../SDL_audiomem.h"
#include "../SDL_audio_c.h"
37
38
#include "SDL_nasaudio.h"
39
/* The tag name used by nas audio */
40
41
42
43
#define NAS_DRIVER_NAME "nas"
static struct SDL_PrivateAudioData *this2 = NULL;
44
/* !!! FIXME: dynamic loading? */
45
46
static int
47
NAS_Available(void)
48
{
49
50
51
AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
if (!aud)
return 0;
52
53
54
AuCloseServer(aud);
return 1;
55
56
57
}
/* This function waits until it is possible to write a full sound buffer */
58
static void
59
NAS_WaitDevice(_THIS)
60
{
61
62
63
64
65
while (this->hidden->buf_free < this->hidden->mixlen) {
AuEvent ev;
AuNextEvent(this->hidden->aud, AuTrue, &ev);
AuDispatchEvent(this->hidden->aud, &ev);
}
66
67
}
68
static void
69
NAS_PlayDevice(_THIS)
70
{
71
72
73
74
75
76
while (this->hidden->mixlen > this->hidden->buf_free) {
/*
* We think the buffer is full? Yikes! Ask the server for events,
* in the hope that some of them is LowWater events telling us more
* of the buffer is free now than what we think.
*/
77
78
79
80
81
82
83
84
85
86
87
88
AuEvent ev;
AuNextEvent(this->hidden->aud, AuTrue, &ev);
AuDispatchEvent(this->hidden->aud, &ev);
}
this->hidden->buf_free -= this->hidden->mixlen;
/* Write the audio data */
AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
this->hidden->written += this->hidden->mixlen;
89
#ifdef DEBUG_AUDIO
90
fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
91
92
93
#endif
}
94
static Uint8 *
95
NAS_GetDeviceBuf(_THIS)
96
{
97
return (this->hidden->mixbuf);
98
99
}
100
static void
101
NAS_CloseDevice(_THIS)
102
{
103
104
105
106
107
108
109
110
111
112
113
if (this->hidden != NULL) {
if (this->hidden->mixbuf != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
}
if (this->hidden->aud) {
AuCloseServer(this->hidden->aud);
this->hidden->aud = 0;
}
SDL_free(this->hidden);
this->hidden = NULL;
114
}
115
116
}
117
118
static unsigned char
sdlformat_to_auformat(unsigned int fmt)
119
{
120
switch (fmt) {
121
case AUDIO_U8:
122
return AuFormatLinearUnsigned8;
123
case AUDIO_S8:
124
return AuFormatLinearSigned8;
125
case AUDIO_U16LSB:
126
return AuFormatLinearUnsigned16LSB;
127
case AUDIO_U16MSB:
128
return AuFormatLinearUnsigned16MSB;
129
case AUDIO_S16LSB:
130
return AuFormatLinearSigned16LSB;
131
case AUDIO_S16MSB:
132
return AuFormatLinearSigned16MSB;
133
}
134
return AuNone;
135
136
137
}
static AuBool
138
event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
139
{
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
switch (ev->type) {
case AuEventTypeElementNotify:
{
AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
switch (event->kind) {
case AuElementNotifyKindLowWater:
if (this2->buf_free >= 0) {
this2->really += event->num_bytes;
gettimeofday(&this2->last_tv, 0);
this2->buf_free += event->num_bytes;
} else {
this2->buf_free = event->num_bytes;
}
break;
case AuElementNotifyKindState:
switch (event->cur_state) {
case AuStatePause:
if (event->reason != AuReasonUser) {
if (this2->buf_free >= 0) {
this2->really += event->num_bytes;
gettimeofday(&this2->last_tv, 0);
this2->buf_free += event->num_bytes;
} else {
this2->buf_free = event->num_bytes;
}
}
break;
}
}
}
}
return AuTrue;
173
174
175
176
177
}
static AuDeviceID
find_device(_THIS, int nch)
{
178
179
180
181
182
183
184
185
186
int i;
for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
AuComponentKindPhysicalOutput) &&
AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
}
}
return AuNone;
187
188
}
189
static int
190
NAS_OpenDevice(_THIS, const char *devname, int iscapture)
191
{
192
193
AuElement elms[3];
int buffer_size;
194
SDL_AudioFormat test_format, format;
195
196
197
198
199
200
201
202
203
/* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc((sizeof *this->hidden));
if (this->hidden == NULL) {
SDL_OutOfMemory();
return 0;
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
204
205
206
/* Try for a closest match on audio format */
format = 0;
207
for (test_format = SDL_FirstAudioFormat(this->spec.format);
208
209
210
211
212
213
214
!format && test_format;) {
format = sdlformat_to_auformat(test_format);
if (format == AuNone) {
test_format = SDL_NextAudioFormat();
}
}
if (format == 0) {
215
216
217
NAS_CloseDevice(this);
SDL_SetError("NAS: Couldn't find any hardware audio formats");
return 0;
218
}
219
this->spec.format = test_format;
220
221
222
this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
if (this->hidden->aud == 0) {
223
224
225
NAS_CloseDevice(this);
SDL_SetError("NAS: Couldn't open connection to NAS server");
return 0;
226
227
}
228
this->hidden->dev = find_device(this, this->spec.channels);
229
230
if ((this->hidden->dev == AuNone)
|| (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
231
232
233
NAS_CloseDevice(this);
SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
return 0;
234
235
}
236
buffer_size = this->spec.freq;
237
238
239
240
241
242
243
if (buffer_size < 4096)
buffer_size = 4096;
if (buffer_size > 32768)
buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
/* Calculate the final parameters for this audio specification */
244
SDL_CalculateAudioSpec(&this->spec);
245
246
247
this2 = this->hidden;
248
AuMakeElementImportClient(elms,this->spec.freq,format,this->spec.channels,
249
AuTrue, buffer_size, buffer_size / 4, 0, NULL);
250
AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
251
AuUnlimitedSamples, 0, NULL);
252
AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
253
254
255
256
257
258
259
AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
this->hidden->flow, event_handler,
(AuPointer) NULL);
AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
/* Allocate mixing buffer */
260
this->hidden->mixlen = this->spec.size;
261
262
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
263
264
NAS_CloseDevice(this);
SDL_OutOfMemory();
265
266
return (-1);
}
267
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
268
269
/* We're ready to rock and roll. :-) */
270
return 1;
271
}
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
static int
NAS_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->OpenDevice = NAS_OpenDevice;
impl->PlayDevice = NAS_PlayDevice;
impl->WaitDevice = NAS_WaitDevice;
impl->GetDeviceBuf = NAS_GetDeviceBuf;
impl->CloseDevice = NAS_CloseDevice;
impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: is this true? */
return 1;
}
AudioBootStrap NAS_bootstrap = {
NAS_DRIVER_NAME, "Network Audio System",
NAS_Available, NAS_Init, 0
};
292
/* vi: set ts=4 sw=4 expandtab: */