Adonthell  0.4
audio.cc
1 /*
2  Copyright (C) 2000 Andrew Henderson <hendersa@db.erau.edu>
3  Copyright (C) 2002 Kai Sterker <kai.sterker@gmail.com>
4  Part of the Adonthell Project <http://adonthell.nongnu.org>
5 
6  Adonthell is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  Adonthell is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with Adonthell. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <SDL.h>
24 #include "audio.h"
25 
26 // #include "audio_loop.h"
27 
28 bool audio::audio_initialized = false;
29 
30 int audio::background_volume;
31 int audio::effects_volume;
32 #ifdef OGG_MUSIC
33 // loop_info *audio::loop[NUM_MUSIC];
34 #endif
35 Mix_Music *audio::music[NUM_MUSIC];
36 string audio::music_file[NUM_MUSIC];
37 Mix_Chunk *audio::sounds[NUM_WAVES];
38 int audio::current_background;
39 int audio::last_background;
40 bool audio::background_paused;
41 int audio::audio_rate;
42 Uint16 audio::buffer_size;
43 Uint16 audio::audio_format;
44 int audio::audio_channels;
45 
46 // python schedule stuff
47 py_object audio::schedule;
48 bool audio::schedule_active = 0;
49 PyObject *audio::schedule_args = NULL;
50 
51 
52 void audio::init (config *myconfig) {
53  int i; // Generic counter variable
54 
55  // Sample rate: 11025, 22050 or 44100 Hz
56  switch( myconfig->audio_sample_rate ) {
57  case 0: {
58  audio_rate = 11025;
59  break; }
60  case 1: {
61  audio_rate = 22050;
62  break; }
63  default: {
64  audio_rate = 44100;
65  break; }
66  }
67 
68  // Output in signed 8/16-bit form
69  audio_format = myconfig->audio_resolution == 0 ? AUDIO_S8 : AUDIO_S16SYS;
70 
71  // 1 is mono, 2 is stereo
72  audio_channels = myconfig->audio_channels == 0 ? 1 : 2;
73 
74  // 100... scales to percentages ;>
75  background_volume = myconfig->audio_volume;
76 
77  buffer_size = 4096; // Audio buffer size
78  effects_volume = 128; // Still figuring this one out...
79  current_background = -1; // No song currently playing
80  last_background = -1; // No song played so far
81  background_paused = false; // Music isn't paused
82  audio_initialized = false; // No audio connection yet
83  schedule_active = false; // No schedule file yet
84 
85  // Mark all slots in sound and music arrays as empty
86  for (i = 0; i < NUM_WAVES; i++) sounds[i] = NULL;
87  for (i = 0; i < NUM_MUSIC; i++) {
88  music[i] = NULL;
89  music_file[i] = "";
90  }
91 
92  // Try opening the audio device at our defaults
93  i = Mix_OpenAudio(audio_rate, audio_format, audio_channels, buffer_size);
94 
95  // Now see what we got when opening the audio device
96  // If we COULDN'T open the audio...
97  if ( i < 0 ) {
98  fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
99  fprintf(stderr, "Audio will not be used.\n");
100 
101  // If we COULD open the audio...
102  } else {
103  audio_initialized = true;
104  Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
105  set_background_volume (background_volume);
106  }
107 }
108 
109 void audio::cleanup(void)
110 {
111  int i;
112  // No music is queued to play
113  current_background = -1;
114 
115  // Null out those tunes and sound effects
116  for (i = 0; i < NUM_WAVES; i++)
117  {
118  unload_wave(i);
119  sounds[i] = NULL;
120  }
121 
122  for (i = 0; i < NUM_MUSIC; i++)
123  {
124  unload_background(i);
125  music[i] = NULL;
126  music_file[i] = "";
127  }
128 
129  // Clean audio schedule
130  schedule.clear ();
131 
132  // Close out audio connection
133  if (audio_initialized == true)
134  {
135  Mix_CloseAudio();
136  audio_initialized = false;
137  }
138 }
139 
140 int audio::load_background(int slot, char *filename) {
141 
142  if (!audio_initialized) return (0);
143 
144  // Check for bad input
145  if ((slot >= NUM_MUSIC) || (slot < 0)) {
146  fprintf(stderr, "Error: Tried to put music in invalid slot.\n");
147  return(0);
148  }
149 
150  // Check if the file exists at all...
151  FILE *f = fopen (filename, "r");
152  if (!f) {
153  fprintf(stderr, "Error: No such file: %s.\n", filename);
154  return 0;
155  }
156  fclose (f);
157 
158  // Music already occupies that slot
159  if (music[slot] != NULL)
160  unload_background (slot);
161 
162  // No music in slot, load new tune in, ...
163  music[slot] = Mix_LoadMUS(filename);
164  music_file[slot] = filename;
165 
166 #ifdef OGG_MUSIC
167  // read loop points and ...
168  // loop[slot] = new loop_info (&music[slot]->ogg_data.ogg->vf);
169 
170  // ... enable looping
171  // music[slot]->ogg_data.ogg->vf.callbacks.read_func = &ogg_read_callback;
172 #endif
173  return(1);
174 }
175 
176 void audio::unload_background (int slot)
177 {
178  if (music[slot] == NULL) return;
179 
180  // If we are unloading background music from the slot
181  // the current background music is in...
182  if (current_background == slot)
183  {
184  last_background = current_background;
185  current_background = -1;
186 
187  // Just a precaution
188  Mix_HaltMusic();
189  Mix_ResumeMusic ();
190  }
191 
192  Mix_FreeMusic (music[slot]);
193  music[slot] = NULL;
194  music_file[slot] = "";
195 
196 #ifdef OGG_MUSIC
197  // delete loop[slot];
198 #endif
199 }
200 
201 void audio::pause_music(void) {
202  Mix_PauseMusic();
203 }
204 
205 void audio::unpause_music(void) {
206  Mix_ResumeMusic();
207 }
208 
209 // Accepts a percentage of the maximum volume level
210 // and clips out of bounds values to 0-100.
211 void audio::set_background_volume(int volume) {
212 
213  // Check for bad input
214  if (volume < 0) {
215  background_volume = 0;
216  } else if (volume > 100) {
217  background_volume = 100;
218  } else
219  background_volume = volume;
220 
221  // Scales 0-100% to 0-128
222  Mix_VolumeMusic(int(background_volume * 1.28));
223 }
224 
225 // This should be done better, but I'll wait until
226 // I have enough sound effects to play with ;>
227 int audio::load_wave(int slot, char *filename) {
228 
229  if (!audio_initialized) return(1);
230 
231  // Check for bad input
232  if ((slot >= NUM_WAVES) || (slot < 0)) {
233  fprintf(stderr, "Error: Tried to put wave in invalid slot.\n");
234  return(1);
235  } else {
236  // Check if the file exists at all...
237  FILE *f = fopen (filename, "r");
238  if (!f)
239  {
240  sounds[slot] = NULL;
241  return 1;
242  }
243 
244  fclose (f);
245 
246  sounds[slot] = Mix_LoadWAV(filename);
247  }
248  return(0);
249 }
250 
251 void audio::unload_wave(int wave) {
252  if (sounds[wave] != NULL) {
253  Mix_FreeChunk(sounds[wave]);
254  sounds[wave] = NULL;
255  }
256 }
257 
258 void audio::play_wave(int channel, int slot) {
259  if ((slot > -1) && (slot < NUM_CHANNELS))
260  if (sounds[slot] != NULL) Mix_PlayChannel(channel, sounds[slot], 0);
261 }
262 
263 void audio::play_background(int slot) {
264  if (music[slot] != NULL) {
265  current_background = slot;
266  Mix_PlayMusic(music[current_background], 0);
267  }
268 }
269 
270 void audio::fade_out_background(int time) {
271  if (Mix_PlayingMusic ())
272  {
273  Mix_FadeOutMusic(time);
274  last_background = current_background;
275  current_background = -1;
276  }
277 #ifdef OGG_MUSIC
278  // music[current_background]->ogg_data.ogg->vf.callbacks.read_func = &fread_wrap;
279 #endif
280 }
281 
282 void audio::fade_in_background(int slot, int time) {
283  if (music[slot] != NULL) {
284  current_background = slot;
285  Mix_FadeInMusic(music[slot], 0, time);
286  }
287 }
288 
289 // Temporary convience function for testing
290 void audio::change_background(int slot, int time) {
291  fade_out_background(time);
292  fade_in_background(slot, time);
293 }
294 
295 #ifdef OGG_MUSIC
296 // OggVorbis_File* audio::get_vorbisfile ()
297 // {
298 // return &music[current_background]->ogg_data.ogg->vf;
299 // }
300 #endif
301 
302 // set audio schedule
303 void audio::set_schedule (string file, PyObject * args)
304 {
305  // Clears the schedule
306  schedule.clear ();
307  Py_XDECREF (schedule_args);
308  schedule_args = NULL;
309 
310  // Set new schedule
311  if (file != "")
312  {
313  schedule_args = args;
314  Py_XINCREF (schedule_args);
315  schedule.create_instance ("schedules.audio." + file, file, args);
316  }
317 }
318 
319 // run the audio control schedule
320 void audio::run_schedule ()
321 {
322  PyObject *song = Py_BuildValue ("(i)", last_background);
323  if (schedule_active) schedule.call_method ("music_finished", song);
324  Py_DECREF (song);
325 }
326 
327 // save state
328 s_int8 audio::put_state (ogzstream& file)
329 {
330  // currently playing
331  current_background >> file;
332 
333  // music file
334  if (current_background != -1) music_file[current_background] >> file;
335 
336  // Save the schedule script state
337  schedule.class_name () >> file;
338  if (schedule_args)
339  {
340  true >> file;
341  python::put_tuple (schedule_args, file);
342  }
343  else false >> file;
344  is_schedule_activated () >> file;
345 
346  return 1;
347 }
348 
349 // get state
350 s_int8 audio::get_state (igzstream& file)
351 {
352  string song, script;
353  bool have_args;
354 
355  // current background
356  last_background << file;
357 
358  // if song was playing, see which it is
359  if (last_background != -1)
360  {
361  song << file;
362 
363  // ... and resume playing
364  if (load_background (last_background, (char *) song.c_str ()))
365  play_background (last_background);
366  }
367 
368  // Restore the schedule script state
369  PyObject * args = NULL;
370  script << file;
371 
372  have_args << file;
373  if (have_args) args = python::get_tuple (file);
374  set_schedule (script, args);
375  Py_XDECREF (args);
376 
377  schedule_active << file;
378 
379  return 1;
380 }
config::audio_channels
u_int8 audio_channels
The number of channels: mono (0) or stereo (1).
Definition: prefs.h:155
igzstream
Class to read data from a Gzip compressed file.
Definition: fileops.h:135
py_object::create_instance
bool create_instance(string file, string classname, PyObject *args=NULL)
Creates an instance of a Python class.
Definition: py_object.cc:57
py_object
Python object class.
Definition: py_object.h:45
ogzstream
Class to write data from a Gzip compressed file.
Definition: fileops.h:227
s_int8
#define s_int8
8 bits long signed integer
Definition: types.h:44
config::audio_volume
u_int8 audio_volume
The volume: a value betwen 0 and 100.
Definition: prefs.h:168
python::get_tuple
static PyObject * get_tuple(igzstream &file)
Loads a Python tuple previously saved with put_tuple ().
Definition: python_class.cc:134
python::put_tuple
static void put_tuple(PyObject *tuple, ogzstream &file)
Save a Python tuple into a file.
Definition: python_class.cc:167
config
This class contains the engine's configuration read either from the config file or from the command l...
Definition: prefs.h:74
py_object::call_method
void call_method(const string &name, PyObject *args=NULL) const
Call a method of this object.
Definition: py_object.h:113
py_object::class_name
std::string class_name() const
Returns the class name of this object.
Definition: py_object.h:223
config::audio_resolution
u_int8 audio_resolution
The resolution: 8 bit (0) or 16 bit (1)
Definition: prefs.h:159
config::audio_sample_rate
u_int8 audio_sample_rate
The sample rate: 11025 Hz (0), 22050 Hz (1) or 44100 Hz (2)
Definition: prefs.h:163
py_object::clear
void clear()
Resets the script to it's post-constructor state.
Definition: py_object.cc:46