1 /*
2  * OpenTyrian: A modern cross-platform port of Tyrian
3  * Copyright (C) 2007-2009  The OpenTyrian Development Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 #include "file.h"
20 #include "lds_play.h"
21 #include "loudness.h"
22 #include "nortsong.h"
23 #include "opentyr.h"
24 #include "params.h"
25 
26 float music_volume = 0, sample_volume = 0;
27 
28 bool music_stopped = true;
29 unsigned int song_playing = 0;
30 
31 bool audio_disabled = false, music_disabled = false, samples_disabled = false;
32 
33 /* SYN: These shouldn't be used outside this file. Hands off! */
34 FILE *music_file = NULL;
35 Uint32 *song_offset;
36 Uint16 song_count = 0;
37 
38 
39 SAMPLE_TYPE *channel_buffer[SFX_CHANNELS] = { NULL };
40 SAMPLE_TYPE *channel_pos[SFX_CHANNELS] = { NULL };
41 Uint32 channel_len[SFX_CHANNELS] = { 0 };
42 Uint8 channel_vol[SFX_CHANNELS];
43 
44 int sound_init_state = false;
45 int freq = 11025 * OUTPUT_QUALITY;
46 
47 static SDL_AudioCVT audio_cvt; // used for format conversion
48 
49 void audio_cb( void *userdata, unsigned char *feedme, int howmuch );
50 
51 void load_song( unsigned int song_num );
52 
init_audio(void)53 bool init_audio( void )
54 {
55 	if (audio_disabled)
56 		return false;
57 
58 	SDL_AudioSpec ask, got;
59 
60 	ask.freq = freq;
61 	ask.format = (BYTES_PER_SAMPLE == 2) ? AUDIO_S16SYS : AUDIO_S8;
62 	ask.channels = 1;
63 	ask.samples = 2048;
64 	ask.callback = audio_cb;
65 
66 	printf("\trequested %d Hz, %d channels, %d samples\n", ask.freq, ask.channels, ask.samples);
67 
68 	if (SDL_OpenAudio(&ask, &got) == -1)
69 	{
70 		fprintf(stderr, "error: failed to initialize SDL audio: %s\n", SDL_GetError());
71 		audio_disabled = true;
72 		return false;
73 	}
74 
75 	printf("\tobtained  %d Hz, %d channels, %d samples\n", got.freq, got.channels, got.samples);
76 
77 	SDL_BuildAudioCVT(&audio_cvt, ask.format, ask.channels, ask.freq, got.format, got.channels, got.freq);
78 
79 	opl_init();
80 
81 	SDL_PauseAudio(0); // unpause
82 
83 	return true;
84 }
85 
audio_cb(void * user_data,unsigned char * sdl_buffer,int howmuch)86 void audio_cb( void *user_data, unsigned char *sdl_buffer, int howmuch )
87 {
88 	(void)user_data;
89 
90 	// prepare for conversion
91 	howmuch /= audio_cvt.len_mult;
92 	audio_cvt.buf = sdl_buffer;
93 	audio_cvt.len = howmuch;
94 
95 	static long ct = 0;
96 
97 	SAMPLE_TYPE *feedme = (SAMPLE_TYPE *)sdl_buffer;
98 
99 	if (!music_disabled && !music_stopped)
100 	{
101 		/* SYN: Simulate the fm synth chip */
102 		SAMPLE_TYPE *music_pos = feedme;
103 		long remaining = howmuch / BYTES_PER_SAMPLE;
104 		while (remaining > 0)
105 		{
106 			while (ct < 0)
107 			{
108 				ct += freq;
109 				lds_update(); /* SYN: Do I need to use the return value for anything here? */
110 			}
111 			/* SYN: Okay, about the calculations below. I still don't 100% get what's going on, but...
112 			- freq is samples/time as output by SDL.
113 			- REFRESH is how often the play proc would have been called in Tyrian. Standard speed is
114 			70Hz, which is the default value of 70.0f
115 			- ct represents the margin between play time (representing # of samples) and tick speed of
116 			the songs (70Hz by default). It keeps track of which one is ahead, because they don't
117 			synch perfectly. */
118 
119 			/* set i to smaller of data requested by SDL and a value calculated from the refresh rate */
120 			long i = (long)((ct / REFRESH) + 4) & ~3;
121 			i = (i > remaining) ? remaining : i; /* i should now equal the number of samples we get */
122 			opl_update((SAMPLE_TYPE *)music_pos, i);
123 			music_pos += i;
124 			remaining -= i;
125 			ct -= (long)(REFRESH * i);
126 		}
127 
128 		/* Reduce the music volume. */
129 		int qu = howmuch / BYTES_PER_SAMPLE;
130 		for (int smp = 0; smp < qu; smp++)
131 		{
132 			feedme[smp] *= music_volume;
133 		}
134 	}
135 
136 	if (!samples_disabled)
137 	{
138 		/* SYN: Mix sound channels and shove into audio buffer */
139 		for (int ch = 0; ch < SFX_CHANNELS; ch++)
140 		{
141 			float volume = sample_volume * (channel_vol[ch] / (float)SFX_CHANNELS);
142 
143 			/* SYN: Don't copy more data than is in the channel! */
144 			unsigned int qu = ((unsigned)howmuch > channel_len[ch] ? channel_len[ch] : (unsigned)howmuch) / BYTES_PER_SAMPLE;
145 			for (unsigned int smp = 0; smp < qu; smp++)
146 			{
147 #if (BYTES_PER_SAMPLE == 2)
148 				Sint32 clip = (Sint32)feedme[smp] + (Sint32)(channel_pos[ch][smp] * volume);
149 				feedme[smp] = (clip > 0x7fff) ? 0x7fff : (clip <= -0x8000) ? -0x8000 : (Sint16)clip;
150 #else  /* BYTES_PER_SAMPLE */
151 				Sint16 clip = (Sint16)feedme[smp] + (Sint16)(channel_pos[ch][smp] * volume);
152 				feedme[smp] = (clip > 0x7f) ? 0x7f : (clip <= -0x80) ? -0x80 : (Sint8)clip;
153 #endif  /* BYTES_PER_SAMPLE */
154 			}
155 
156 			channel_pos[ch] += qu;
157 			channel_len[ch] -= qu * BYTES_PER_SAMPLE;
158 
159 			/* SYN: If we've emptied a channel buffer, let's free the memory and clear the channel. */
160 			if (channel_len[ch] == 0)
161 			{
162 				free(channel_buffer[ch]);
163 				channel_buffer[ch] = channel_pos[ch] = NULL;
164 			}
165 		}
166 	}
167 
168 	// do conversion
169 	SDL_ConvertAudio(&audio_cvt);
170 }
171 
deinit_audio(void)172 void deinit_audio( void )
173 {
174 	if (audio_disabled)
175 		return;
176 
177 	SDL_PauseAudio(1); // pause
178 
179 	SDL_CloseAudio();
180 
181 	for (unsigned int i = 0; i < SFX_CHANNELS; i++)
182 	{
183 		free(channel_buffer[i]);
184 		channel_buffer[i] = channel_pos[i] = NULL;
185 		channel_len[i] = 0;
186 	}
187 
188 	lds_free();
189 }
190 
191 
load_music(void)192 void load_music( void )
193 {
194 	if (music_file == NULL)
195 	{
196 		music_file = dir_fopen_die(data_dir(), "music.mus", "rb");
197 
198 		efread(&song_count, sizeof(song_count), 1, music_file);
199 
200 		song_offset = malloc((song_count + 1) * sizeof(*song_offset));
201 
202 		efread(song_offset, 4, song_count, music_file);
203 		song_offset[song_count] = ftell_eof(music_file);
204 	}
205 }
206 
load_song(unsigned int song_num)207 void load_song( unsigned int song_num )
208 {
209 	if (audio_disabled)
210 		return;
211 
212 	SDL_LockAudio();
213 
214 	if (song_num < song_count)
215 	{
216 		unsigned int song_size = song_offset[song_num + 1] - song_offset[song_num];
217 		lds_load(music_file, song_offset[song_num], song_size);
218 	}
219 	else
220 	{
221 		fprintf(stderr, "warning: failed to load song %d\n", song_num + 1);
222 	}
223 
224 	SDL_UnlockAudio();
225 }
226 
play_song(unsigned int song_num)227 void play_song( unsigned int song_num )
228 {
229 	if (song_num != song_playing)
230 	{
231 		load_song(song_num);
232 		song_playing = song_num;
233 	}
234 
235 	music_stopped = false;
236 }
237 
restart_song(void)238 void restart_song( void )
239 {
240 	unsigned int temp = song_playing;
241 	song_playing = -1;
242 	play_song(temp);
243 }
244 
stop_song(void)245 void stop_song( void )
246 {
247 	music_stopped = true;
248 }
249 
fade_song(void)250 void fade_song( void )
251 {
252 	/* STUB: we have no implementation of this to port */
253 }
254 
set_volume(unsigned int music,unsigned int sample)255 void set_volume( unsigned int music, unsigned int sample )
256 {
257 	music_volume = music * (1.5f / 255.0f);
258 	sample_volume = sample * (1.0f / 255.0f);
259 }
260 
JE_multiSamplePlay(JE_byte * buffer,JE_word size,JE_byte chan,JE_byte vol)261 void JE_multiSamplePlay(JE_byte *buffer, JE_word size, JE_byte chan, JE_byte vol)
262 {
263 	if (audio_disabled || samples_disabled)
264 		return;
265 
266 	SDL_LockAudio();
267 
268 	free(channel_buffer[chan]);
269 
270 	channel_len[chan] = size * BYTES_PER_SAMPLE * SAMPLE_SCALING;
271 	channel_buffer[chan] = malloc(channel_len[chan]);
272 	channel_pos[chan] = channel_buffer[chan];
273 	channel_vol[chan] = vol + 1;
274 
275 	for (int i = 0; i < size; i++)
276 	{
277 		for (int ex = 0; ex < SAMPLE_SCALING; ex++)
278 		{
279 #if (BYTES_PER_SAMPLE == 2)
280 			channel_buffer[chan][(i * SAMPLE_SCALING) + ex] = (Sint8)buffer[i] << 8;
281 #else  /* BYTES_PER_SAMPLE */
282 			channel_buffer[chan][(i * SAMPLE_SCALING) + ex] = (Sint8)buffer[i];
283 #endif  /* BYTES_PER_SAMPLE */
284 		}
285 	}
286 
287 	SDL_UnlockAudio();
288 }
289 
290