1 /*
2  * Copyright (c) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "SDL_config.h"
18 
19 /* Allow access to a raw mixing buffer */
20 
21 #ifdef HAVE_SIGNAL_H
22 #include <signal.h>
23 #endif
24 #include <unistd.h>
25 
26 #include "SDL_timer.h"
27 #include "SDL_audio.h"
28 #include "../SDL_audiomem.h"
29 #include "../SDL_audio_c.h"
30 #include "../SDL_audiodev_c.h"
31 #include "SDL_sndioaudio.h"
32 
33 /* The tag name used by sndio audio */
34 #define SNDIO_DRIVER_NAME         "sndio"
35 
36 /* Audio driver functions */
37 static int SNDIO_OpenAudio(_THIS, SDL_AudioSpec *spec);
38 static void SNDIO_WaitAudio(_THIS);
39 static void SNDIO_PlayAudio(_THIS);
40 static Uint8 *SNDIO_GetAudioBuf(_THIS);
41 static void SNDIO_CloseAudio(_THIS);
42 
43 /* Audio driver bootstrap functions */
44 
45 static int Audio_Available(void)
46 {
47 	struct sio_hdl *this_hdl;
48 	int available = 0;
49 
50 	if ( (this_hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0)) != NULL ) {
51 		sio_close(this_hdl);
52 		available = 1;
53 	}
54 
55 	return available;
56 }
57 
58 static void Audio_DeleteDevice(SDL_AudioDevice *device)
59 {
60 	SDL_free(device->hidden);
61 	SDL_free(device);
62 }
63 
64 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
65 {
66 	SDL_AudioDevice *this;
67 
68 	/* Initialize all variables that we clean on shutdown */
69 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
70 	if ( this ) {
71 		SDL_memset(this, 0, (sizeof *this));
72 		this->hidden = (struct SDL_PrivateAudioData *)
73 				SDL_malloc((sizeof *this->hidden));
74 	}
75 	if ( (this == NULL) || (this->hidden == NULL) ) {
76 		SDL_OutOfMemory();
77 		if ( this ) {
78 			SDL_free(this);
79 		}
80 		return(0);
81 	}
82 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
83 
84 	/* Set the function pointers */
85 	this->OpenAudio = SNDIO_OpenAudio;
86 	this->WaitAudio = SNDIO_WaitAudio;
87 	this->PlayAudio = SNDIO_PlayAudio;
88 	this->GetAudioBuf = SNDIO_GetAudioBuf;
89 	this->CloseAudio = SNDIO_CloseAudio;
90 
91 	this->free = Audio_DeleteDevice;
92 
93 	hdl = NULL;
94 
95 	return this;
96 }
97 
98 AudioBootStrap SNDIO_bootstrap = {
99 	SNDIO_DRIVER_NAME, "sndio",
100 	Audio_Available, Audio_CreateDevice
101 };
102 
103 
104 
105 /* This function waits until it is possible to write a full sound buffer */
106 static void SNDIO_WaitAudio(_THIS)
107 {
108 	/* nothing, we're using the blocking api */
109 }
110 
111 static void SNDIO_PlayAudio(_THIS)
112 {
113 	int written;
114 
115 	/* Write the audio data */
116 	written = sio_write(hdl, mixbuf, mixlen);
117 
118 	/* If we couldn't write, assume fatal error for now */
119 	if ( written == 0 ) {
120 		this->enabled = 0;
121 	}
122 #ifdef DEBUG_AUDIO
123 	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
124 #endif
125 }
126 
127 static Uint8 *SNDIO_GetAudioBuf(_THIS)
128 {
129 	return(mixbuf);
130 }
131 
132 static void SNDIO_CloseAudio(_THIS)
133 {
134 	if ( mixbuf != NULL ) {
135 		SDL_FreeAudioMem(mixbuf);
136 		mixbuf = NULL;
137 	}
138 	if ( hdl != NULL ) {
139 		sio_close(hdl);
140 		hdl = NULL;
141 	}
142 }
143 
144 static int SNDIO_OpenAudio(_THIS, SDL_AudioSpec *spec)
145 {
146 	struct sio_par par;
147 
148 	mixbuf = NULL;
149 
150 	if ((hdl = sio_open(NULL, SIO_PLAY, 0)) == NULL) {
151 		SDL_SetError("sio_open() failed");
152 		return(-1);
153 	}
154 
155 	sio_initpar(&par);
156 
157 	switch (spec->format) {
158 	case AUDIO_S16LSB:
159 		par.bits = 16;
160 		par.sig = 1;
161 		par.le = 1;
162 		break;
163 	case AUDIO_S16MSB:
164 		par.bits = 16;
165 		par.sig = 1;
166 		par.le = 0;
167 		break;
168 	case AUDIO_S8:
169 		par.bits = 8;
170 		par.sig = 1;
171 		break;
172 	case AUDIO_U16LSB:
173 		par.bits = 16;
174 		par.sig = 0;
175 		par.le = 1;
176 		break;
177 	case AUDIO_U16MSB:
178 		par.bits = 16;
179 		par.sig = 0;
180 		par.le = 0;
181 		break;
182 	case AUDIO_U8:
183 		par.bits = 8;
184 		par.sig = 0;
185 		break;
186 	default:
187 		SDL_SetError("SNDIO unknown format");
188 		return(-1);
189 	}
190 
191 	par.rate = spec->freq;
192 	par.pchan = spec->channels;
193 	par.round = spec->samples;
194 	par.appbufsz = par.round * 2;
195 
196 	if (sio_setpar(hdl, &par) == 0) {
197 		SDL_SetError("sio_setpar() failed");
198 		return(-1);
199 	}
200 
201 	if (sio_getpar(hdl, &par) == 0) {
202 		SDL_SetError("sio_getpar() failed");
203 		return(-1);
204 	}
205 
206 	if (par.bits == 16) {
207 		if (par.sig && par.le) {
208 			spec->format = AUDIO_S16LSB;
209 		} else if (par.sig && !par.le) {
210 			spec->format = AUDIO_S16MSB;
211 		} else if (!par.sig && par.le) {
212 			spec->format = AUDIO_U16LSB;
213 		} else
214 			spec->format = AUDIO_U16MSB;
215 	} else if (par.bits == 8) {
216 		spec->format = par.sig ? AUDIO_S8 : AUDIO_U8;
217 	} else {
218 		SDL_SetError("SNDIO couldn't configure a suitable format");
219 		return(-1);
220 	}
221 
222 	spec->freq = par.rate;
223 	spec->channels = par.pchan;
224 	spec->samples = par.round;
225 
226 	SDL_CalculateAudioSpec(spec);
227 
228 	/* Allocate mixing buffer */
229 	mixlen = spec->size;
230 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
231 	if ( mixbuf == NULL ) {
232 		return(-1);
233 	}
234 	SDL_memset(mixbuf, spec->silence, spec->size);
235 
236 	if ( sio_start(hdl) == 0 ) {
237 		SDL_SetError("sio_start() failed");
238 		return(-1);
239 	}
240 
241 	/* We're ready to rock and roll. :-) */
242 	return(0);
243 }
244