1 /* File: sdl-sound.c */
2 /* Purpose: SDL_Audio routines for loading and playing WAVs */
3 #if defined(USE_SDL) || defined(USE_SDL2)
4 
5 #include "c-angband.h"
6 #include <SDL.h>
7 
8 #ifdef USE_SOUND
9 
10 typedef struct sound_wave
11 {
12 	Uint8* buffer;
13 	Uint32 length;
14 	Uint32 pos;
15 	bool playing;
16 } sound_wave;
17 sound_wave sound_data[MSG_MAX][SAMPLE_MAX];
18 sound_wave *now_playing = NULL;
19 
20 SDL_AudioSpec wav_obtained;
21 
wav_play(void * userdata,Uint8 * stream,int len)22 static void wav_play(void *userdata, Uint8 *stream, int len)
23 {
24 	/* Grab */
25 	sound_wave * wav = now_playing;
26 
27 	/* Paranoia */
28 	Uint32 tocopy = ((wav->length - wav->pos > len) ? len: wav->length - wav->pos);
29 
30 	/* Samples to copy for silence */
31 	Uint32 topad = len - tocopy;
32 
33 	/* Copy data to audio buffer */
34 	memcpy(stream, wav->buffer + wav->pos, tocopy);
35 
36 	/* Copy silence to audio buffer */
37 	/* TODO: for >=16bit unsigned formats, "0" is wrong, but I don't want
38 	 * to do interleaved memset here :( Ideally, we should have a static
39 	 * buffer pre-filled with silence and copy from there... */
40 	memset(stream + tocopy, 0, topad);
41 
42 	/* Advance */
43 	wav->pos += tocopy;
44 }
45 
sdl_play_sound_end(bool wait)46 void sdl_play_sound_end(bool wait)
47 {
48 	bool end_sound;
49 	sound_wave * wav = now_playing;
50 
51 	if (wav == NULL) return;
52 
53 	end_sound = (wait? (wav->pos == wav->length): TRUE);
54 
55 	/* Wait for end of audio thread */
56 	if ((wav->length != 0) && end_sound && wav->playing)
57 	{
58 		/* Stop playing */
59 		SDL_PauseAudio(1);
60 		wav->playing = FALSE;
61 	}
62 }
63 
64 /*
65  * Init sound
66  */
sdl_init_sound()67 void sdl_init_sound()
68 {
69 	SDL_AudioSpec wav_desired;
70 	int i, j;
71 
72 	/* Desired audio parameters */
73 	wav_desired.freq = 44100;
74 	wav_desired.format = 0; /* let OS pick //AUDIO_U16SYS;*/
75 	wav_desired.channels = 2;
76 	wav_desired.samples = 512;
77 	wav_desired.callback = &wav_play;
78 	wav_desired.userdata = NULL;
79 
80 	/* Open the audio device */
81 	if (SDL_OpenAudio(&wav_desired, &wav_obtained) != 0)
82 	{
83 		plog_fmt("Could not open audio: %s", SDL_GetError());
84 		use_sound = FALSE;
85 		return;
86 	}
87 
88 	/* Clear sound_data */
89 	for (j = 0; j < MSG_MAX; j++)	for (i = 0; i < SAMPLE_MAX; i++)
90 	{
91 		(void)WIPE(&sound_data[j][i], sound_wave);
92 	}
93 }
94 
95 /*
96  * Close sound
97  */
sdl_cleanup_sound()98 void sdl_cleanup_sound()
99 {
100 	int j, i;
101 
102 	/* Close the audio device */
103 	SDL_CloseAudio();
104 
105 	/* Free loaded wavs */
106 	for (j = 0; j < MSG_MAX; j++)	for (i = 0; i < SAMPLE_MAX; i++)
107 	{
108 		if (sound_data[j][i].buffer != NULL)
109 		{
110 			free(sound_data[j][i].buffer);
111 		}
112 	}
113 }
114 /*
115  * Load a sound
116  */
sdl_load_sound(int v,int s)117 void sdl_load_sound(int v, int s)
118 {
119 	SDL_AudioSpec wav_spec;
120 	SDL_AudioCVT wav_cvt;
121 	char buf[MSG_LEN];
122 
123 	sound_wave * wav = &sound_data[v][s];
124 
125 	/* Build the path */
126 	path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_SOUND, sound_file[v][s]);
127 
128 	/* Load the WAV */
129 	if (SDL_LoadWAV(buf, &wav_spec, &wav->buffer, &wav->length) == NULL)
130 	{
131 		plog_fmt("Could not open %s: %s", buf, SDL_GetError());
132 		return;
133 	}
134 
135 	/* Build the audio converter */
136 	if (SDL_BuildAudioCVT(&wav_cvt, wav_spec.format, wav_spec.channels, wav_spec.freq,
137 		wav_obtained.format, wav_obtained.channels, wav_obtained.freq) < 0)
138 	{
139 		plog_fmt("Could not build audio converter: %s", SDL_GetError());
140 		return;
141 	}
142 
143 	/* Allocate a buffer for the audio converter */
144 	wav_cvt.buf = malloc(wav->length * wav_cvt.len_mult);
145 	wav_cvt.len = wav->length;
146 	memcpy(wav_cvt.buf, wav->buffer, wav->length);
147 
148 	/* Convert audio data to correct format */
149 	if (SDL_ConvertAudio(&wav_cvt) != 0)
150 	{
151 		plog_fmt("Could not convert audio file: %s", SDL_GetError());
152 		return;
153 	}
154 
155 	/* Free the WAV */
156 	SDL_FreeWAV(wav->buffer);
157 
158 	/* Allocate a buffer for the audio data */
159 	wav->buffer = malloc(wav_cvt.len_cvt);
160 	memcpy(wav->buffer, wav_cvt.buf, wav_cvt.len_cvt);
161 	free(wav_cvt.buf);
162 	wav->length = wav_cvt.len_cvt;
163 }
164 /*
165  * Make a sound
166  */
sdl_play_sound(int v,int s)167 void sdl_play_sound(int v, int s)
168 {
169 	sound_wave * wav = &sound_data[v][s];
170 
171 	/* If another sound is currently playing, stop it */
172 	sdl_play_sound_end(FALSE);
173 
174 	/* If sound isn't loaded, load it */
175 	if (wav->buffer == NULL) sdl_load_sound(v,s);
176 
177 	/* Start playing */
178 	wav->pos = 0;
179 	wav->playing = TRUE;
180 	SDL_PauseAudio(0);
181 
182 	now_playing = wav;
183 }
184 
185 /*
186  * BONUS API - Play a system beep sound
187  */
188 #ifdef ON_OSX
189 #include "CoreFoundation/CoreFoundation.h"
190 #endif
191 #ifdef WINDOWS
192 /* <windows.h> should already be included by "c-angband.h" */
193 #endif
sdl_bell(void)194 void sdl_bell(void)
195 {
196 #ifdef ON_OSX
197 	NSBeep();
198 #elif defined(WINDOWS)
199 	MessageBeep(MB_ICONASTERISK);
200 #elif defined(SET_UID) && !defined(MOBILE_UI)
201 	/* Hacky fallback for "UNIX" */
202 	fprintf(stdout, "\a");
203 #endif
204 }
205 
206 #endif
207 #endif
208