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