1 /*
2  * sdl-sound without SDL_mixer.dll
3  *
4  * This file is part of the Simutrans project under the artistic license.
5  */
6 
7 
8 #include <SDL.h>
9 #include <string.h>
10 #include "sound.h"
11 #include "../simmem.h"
12 #include "../simdebug.h"
13 #include <stdio.h>
14 
15 /*
16  * Hajo: flag if sound module should be used
17  */
18 static int use_sound = 0;
19 
20 /*
21  * defines the number of channels available
22  */
23 #define CHANNELS 4
24 
25 
26 /*
27  * this structure contains the data for one sample
28  */
29 struct sample {
30 	/* the buffer containing the data for the sample, the format
31 	 * must always be identical to the one of the system output
32 	 * format */
33 	Uint8 *audio_data;
34 
35 	Uint32 audio_len;	/* number of samples in the audio data */
36 };
37 
38 
39 /* this list contains all the samples
40  */
41 static sample samples[64];
42 
43 /* all samples are stored chronologically there
44  */
45 static int samplenumber = 0;
46 
47 
48 /* this structure contains the information about one channel
49  */
50 struct channel {
51 	Uint32 sample_pos;	/* the current position inside this sample */
52 	Uint8 sample;		/* which sample is played, 255 for no sample */
53 	Uint8 volume;		/* the volume this channel should be played */
54 };
55 
56 
57 /* this array contains all the information of the currently played samples */
58 static channel channels[CHANNELS];
59 
60 
61 /* the format of the output audio channel in use
62  * all loaded waved need to be converted to this format
63  */
64 static SDL_AudioSpec output_audio_format;
65 
66 
sdl_sound_callback(void *,Uint8 * stream,int len)67 void sdl_sound_callback(void *, Uint8 * stream, int len)
68 {
69 	memset(stream, 0, len); // SDL2 requires the output stream to be fully written on every callback.
70 
71 	/*
72 	* add all the sample that need to be played
73 	*/
74 	for(  int c = 0;  c < CHANNELS;  c++  ) {
75 		/*
76 		* only do something, if the channel is used
77 		*/
78 		if (channels[c].sample != 255) {
79 			sample * smp = &samples[channels[c].sample];
80 
81 			/*
82 			* add sample
83 			*/
84 			if (len + channels[c].sample_pos >= smp->audio_len ) {
85 				// SDL_MixAudio(stream, smp->audio_data + channels[c].sample_pos, smp->audio_len - channels[c].sample_pos, channels[c].volume);
86 				channels[c].sample = 255;
87 			}
88 			else {
89 				SDL_MixAudio(stream, smp->audio_data + channels[c].sample_pos, len, channels[c].volume);
90 				channels[c].sample_pos += len;
91 			}
92 		}
93 	}
94 }
95 
96 
97 /**
98  * Sound initialisation routine
99  */
dr_init_sound()100 bool dr_init_sound()
101 {
102 	int sound_ok = 0;
103 	if(use_sound!=0) {
104 		return use_sound;	// avoid init twice
105 	}
106 	use_sound = 1;
107 
108 	// initialize SDL sound subsystem
109 	if (SDL_InitSubSystem(SDL_INIT_AUDIO) != -1) {
110 
111 		// open an audio channel
112 		SDL_AudioSpec desired;
113 
114 		desired.freq = 22050;
115 		desired.channels = 1;
116 		desired.format = AUDIO_S16SYS;
117 		desired.samples = 1024;
118 		desired.userdata = NULL;
119 
120 		desired.callback = sdl_sound_callback;
121 
122 		if (SDL_OpenAudio(&desired, &output_audio_format) != -1) {
123 
124 			// check if we got the right audio format
125 			if (output_audio_format.format == AUDIO_S16SYS) {
126 
127 				int i;
128 
129 				// finished initializing
130 				sound_ok = 1;
131 
132 				for (i = 0; i < CHANNELS; i++)
133 				channels[i].sample = 255;
134 
135 				// start playing sounds
136 				SDL_PauseAudio(0);
137 
138 			}
139 			else {
140 				dbg->error("dr_init_sound()", "Open audio channel doesn't meet requirements. Muting");
141 				SDL_CloseAudio();
142 				SDL_QuitSubSystem(SDL_INIT_AUDIO);
143 			}
144 
145 
146 		}
147 		else {
148 			dbg->error("dr_init_sound()", "Could not open required audio channel. Muting");
149 			SDL_QuitSubSystem(SDL_INIT_AUDIO);
150 		}
151 	}
152 	else {
153 		dbg->error("dr_init_sound()", "Could not initialize sound system. Muting");
154 	}
155 
156 	use_sound = sound_ok ? 1: -1;
157 	return sound_ok;
158 }
159 
160 
161 /**
162  * loads a sample
163  * @return a handle for that sample or -1 on failure
164  * @author Hj. Malthaner
165  */
dr_load_sample(const char * filename)166 int dr_load_sample(const char *filename)
167 {
168 	if(use_sound>0  &&  samplenumber<64) {
169 
170 		SDL_AudioSpec wav_spec;
171 		SDL_AudioCVT  wav_cvt;
172 		Uint8 *wav_data;
173 		Uint32 wav_length;
174 		sample smp;
175 
176 		/* load the sample */
177 		if (SDL_LoadWAV(filename, &wav_spec, &wav_data, &wav_length) == NULL) {
178 			dbg->warning("dr_load_sample", "could not load wav (%s)", SDL_GetError());
179 			return -1;
180 		}
181 
182 		/* convert the loaded wav to the internally used sound format */
183 		if (SDL_BuildAudioCVT(&wav_cvt,
184 			    wav_spec.format, wav_spec.channels, wav_spec.freq,
185 			    output_audio_format.format,
186 			    output_audio_format.channels,
187 			    output_audio_format.freq) < 0) {
188 			dbg->warning("dr_load_sample", "could not create conversion structure");
189 			SDL_FreeWAV(wav_data);
190 			return -1;
191 		}
192 
193 		wav_cvt.buf = MALLOCN(Uint8, wav_length * wav_cvt.len_mult);
194 		wav_cvt.len = wav_length;
195 		memcpy(wav_cvt.buf, wav_data, wav_length);
196 
197 		SDL_FreeWAV(wav_data);
198 
199 		if (SDL_ConvertAudio(&wav_cvt) < 0) {
200 			dbg->warning("dr_load_sample", "could not convert wav to output format");
201 			return -1;
202 		}
203 
204 		/* save the data */
205 		smp.audio_data = wav_cvt.buf;
206 		smp.audio_len = wav_cvt.len_cvt;
207 		samples[samplenumber] = smp;
208 		dbg->message("dr_load_sample", "Loaded %s to sample %i.",filename,samplenumber);
209 
210 		return samplenumber++;
211 	}
212 	return -1;
213 }
214 
215 
216 /**
217  * plays a sample
218  * @param key the key for the sample to be played
219  * @author Hj. Malthaner
220  */
dr_play_sample(int sample_number,int volume)221 void dr_play_sample(int sample_number, int volume)
222 {
223 	if(use_sound>0) {
224 
225 		int c;
226 
227 		if (sample_number == -1) {
228 			return;
229 		}
230 
231 		/* find an empty channel, and play */
232 		for (c = 0; c < CHANNELS; c++) {
233 			if (channels[c].sample == 255) {
234 				channels[c].sample = sample_number;
235 				channels[c].sample_pos = 0;
236 				channels[c].volume = volume * SDL_MIX_MAXVOLUME >> 8;
237 				break;
238 			}
239 		}
240 	}
241 }
242