DEADSOFTWARE

sound: fix voices in menu
[flatwaifu.git] / src / openal / sound.c
1 #include "sound.h"
2 #include "music.h"
4 #include "files.h" // F_findres F_getreslen
5 #include "memory.h" // M_lock M_unlock
6 #include "misc.h" // int2host
7 #include "error.h" // logo
9 #include <OpenAL/al.h>
10 #include <OpenAL/alc.h>
11 #include <SDL.h> // SDL_BuildAudioCVT SDL_ConvertAudio
12 #include <assert.h>
13 #include <stdlib.h> // malloc
14 #include <string.h> // memcpy
16 #define TAG_OAL1 0x4F414C31
17 #define MAX_CHANNELS 8
19 #pragma pack(1)
20 typedef struct dmi {
21 dword len;
22 dword rate;
23 dword lstart;
24 dword llen;
25 byte data[];
26 } dmi;
27 #pragma pack()
29 typedef struct openal_snd {
30 snd_t base;
31 ALuint buffer;
32 } openal_snd;
34 typedef struct openal_channel {
35 ALuint source;
36 } openal_channel;
38 short snd_vol;
39 short mus_vol;
40 char music_random;
41 int music_time;
42 int music_fade;
44 static ALCdevice *device;
45 static ALCcontext *context;
46 static ALuint sources[MAX_CHANNELS];
48 /* Music */
50 void S_initmusic (void) {
52 }
54 void S_donemusic (void) {
56 }
58 void S_startmusic (int time) {
60 }
62 void S_stopmusic (void) {
64 }
66 void S_volumemusic (int v) {
68 }
70 void F_loadmus (char n[8]) {
72 }
74 void F_freemus (void) {
76 }
78 void S_updatemusic (void) {
80 }
82 /* Sound */
84 static void convert_this_ext (Uint32 src_format, int src_chan, int src_rate, Uint32 dst_format, int dst_chan, int dst_rate, const void *buf, int len, void **maxbuf, int *maxlen) {
85 SDL_AudioCVT cvt;
86 *maxlen = 0;
87 *maxbuf = NULL;
88 if (SDL_BuildAudioCVT(&cvt, src_format, src_chan, src_rate, dst_format, dst_chan, dst_rate) != -1) {
89 *maxlen = len * cvt.len_mult;
90 *maxbuf = malloc(*maxlen);
91 if (*maxbuf != NULL) {
92 memcpy(*maxbuf, buf, len);
93 cvt.buf = *maxbuf;
94 cvt.len = len;
95 if (SDL_ConvertAudio(&cvt) == 0) {
96 *maxlen = len * cvt.len_ratio;
97 } else {
98 free(*maxbuf);
99 *maxbuf = NULL;
100 *maxlen = 0;
106 static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
107 assert(data);
108 ALuint buffer = 0;
109 openal_snd *snd = NULL;
110 void *newdata = NULL;
111 int newlen = 0;
112 // for some reason 8bit formats makes psshshshsh
113 // TODO do this without SDL
114 convert_this_ext(sign ? AUDIO_S8 : AUDIO_U8, 1, rate, AUDIO_S16SYS, 1, rate, data, len, &newdata, &newlen);
115 if (newdata != NULL) {
116 alGenBuffers(1, &buffer);
117 if (alGetError() == AL_NO_ERROR) {
118 alBufferData(buffer, AL_FORMAT_MONO16, newdata, newlen, rate);
119 if (alGetError() == AL_NO_ERROR) {
120 snd = malloc(sizeof(openal_snd));
121 if (snd != NULL) {
122 snd->base.tag = TAG_OAL1;
123 snd->buffer = buffer;
124 // TODO loops
125 } else {
126 alDeleteBuffers(1, &buffer);
128 } else {
129 alDeleteBuffers(1, &buffer);
132 free(newdata);
134 return snd;
137 snd_t *S_get (int id) {
138 void *handle;
139 openal_snd *snd = NULL;
140 if (context != NULL) {
141 handle = M_lock(id);
142 if (handle != NULL) {
143 void *data = handle;
144 dword len = F_getreslen(id);
145 dword rate = 11025;
146 dword lstart = 0;
147 dword llen = 0;
148 int sign = 0;
149 if (len > 16) {
150 dmi *hdr = handle;
151 dword hdr_len = int2host(hdr->len);
152 dword hdr_rate = int2host(hdr->rate);
153 dword hdr_lstart = int2host(hdr->lstart);
154 dword hdr_llen = int2host(hdr->llen);
155 if (hdr_len <= len - 8 && hdr_lstart + hdr_llen <= len - 16) {
156 data = hdr->data;
157 len = hdr_len;
158 rate = hdr_rate;
159 lstart = hdr_lstart;
160 llen = hdr_llen;
161 sign = 1;
164 snd = new_openal_snd(data, len, rate, lstart, llen, sign);
165 M_unlock(handle);
168 return (snd_t*)snd;
171 snd_t *S_load (const char name[8]) {
172 return S_get(F_findres(name));
175 void S_free (snd_t *s) {
176 int i;
177 ALint h;
178 openal_snd *snd = (openal_snd*)s;
179 if (snd != NULL) {
180 assert(snd->base.tag = TAG_OAL1);
181 if (context != NULL) {
182 for (i = 0; i < MAX_CHANNELS; i++) {
183 alGetSourcei(sources[i], AL_BUFFER, &h);
184 if (h == snd->buffer) {
185 alSourceStop(sources[i]);
186 alSourcei(sources[i], AL_BUFFER, 0);
189 alDeleteBuffers(1, &snd->buffer);
190 assert(alGetError() == AL_NO_ERROR);
192 snd->base.tag = 0;
193 free(s);
197 void S_init (void) {
198 assert(device == NULL && context == NULL);
199 const ALCint attrs[] = {ALC_MONO_SOURCES, MAX_CHANNELS, 0};
200 device = alcOpenDevice(NULL);
201 if (device != NULL) {
202 context = alcCreateContext(device, attrs);
203 if (context != NULL) {
204 if (alcMakeContextCurrent(context)) {
205 alGenSources(MAX_CHANNELS, sources);
206 if (alGetError() == AL_NO_ERROR) {
207 alListenerf(AL_GAIN, 1);
208 alListener3f(AL_POSITION, 0, 0, 0);
209 alListener3f(AL_VELOCITY, 0, 0, 0);
210 } else {
211 logo("S_init: unable to create OpenAL sources\n");
212 alcDestroyContext(context);
213 alcCloseDevice(device);
214 context = NULL;
215 device = NULL;
217 } else {
218 logo("S_init: unable to make OpenAL context current\n");
219 alcDestroyContext(context);
220 alcCloseDevice(device);
221 context = NULL;
222 device = NULL;
224 } else {
225 logo("S_init: unable to create OpenAL context\n");
226 alcCloseDevice(device);
227 device = NULL;
229 } else {
230 logo("S_init: unable to open OpenAL device\n");
234 void S_done (void) {
235 if (context != NULL) {
236 alcMakeContextCurrent(NULL);
237 alcDestroyContext(context);
238 alcCloseDevice(device);
240 context = NULL;
241 device = NULL;
244 short S_play (snd_t *s, short c, short v) {
245 assert(c >= 0 && c <= MAX_CHANNELS);
246 assert(v >= 0 && v < 256);
247 ALuint source;
248 ALint state;
249 int channel;
250 if (context != NULL && s != NULL) {
251 openal_snd *snd = (openal_snd*)s;
252 assert(snd->base.tag == TAG_OAL1);
253 if (c == 0) {
254 for (channel = 0; channel < MAX_CHANNELS; channel++) {
255 state = AL_PLAYING;
256 alGetSourcei(sources[channel], AL_SOURCE_STATE, &state);
257 if (state == AL_STOPPED || state == AL_INITIAL) {
258 break; // !!!
261 } else {
262 channel = c - 1;
264 if (channel < MAX_CHANNELS) {
265 source = sources[channel];
266 alSourceStop(source);
267 alSourcei(source, AL_BUFFER, snd->buffer);
268 alSourcef(source, AL_PITCH, 1);
269 alSourcef(source, AL_GAIN, v / 255.0);
270 alSource3f(source, AL_POSITION, 0, 0, 0);
271 alSource3f(source, AL_VELOCITY, 0, 0, 0);
272 alSourcei(source, AL_LOOPING, AL_FALSE);
273 alSourcePlay(source);
276 return 0;
279 void S_stop (short c) {
280 assert(c >= 0 && c <= MAX_CHANNELS);
281 if (context != NULL) {
282 if (c != 0) {
283 alSourceStop(sources[c - 1]);
288 void S_volume (int v) {
289 assert(v >= 0 && v <= 128);
290 snd_vol = v;
291 if (context != NULL) {
292 alListenerf(AL_GAIN, v / 128.0);
296 void S_wait (void) {
297 int i;
298 ALint state;
299 if (context != NULL) {
300 for (i = 0; i < MAX_CHANNELS; i++) {
301 do {
302 state = AL_STOPPED;
303 alGetSourcei(sources[i], AL_SOURCE_STATE, &state);
304 } while (state == AL_PLAYING);