source: abuse/trunk/src/sdlport/sound.cpp @ 536

Last change on this file since 536 was 536, checked in by Sam Hocevar, 11 years ago

sdlport: add some error reporting to the new SDL_Mixer features.

File size: 10.8 KB
Line 
1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 2001 Anthony Kruize <trandor@labyrinth.net.au>
4 *  Copyright (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5 *
6 *  This program 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 *  This program 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 this program; if not, write to the Free Software Foundation,
18 *  Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 */
20
21#include "config.h"
22
23#include <string.h>
24
25#include <SDL.h>
26#ifdef USE_SDL_MIXER
27#include <SDL/SDL_mixer.h>
28#include "hmi.h"
29#endif
30
31#include "sound.h"
32#include "readwav.h"
33#include "specs.h"
34#include "setup.h"
35
36class effect_handle
37{
38public:
39    effect_handle    *prev;        // Handle to the previous effect
40    effect_handle    *next;        // Handle to the next effect
41    Uint8            *data;        // Audio data
42    Uint8            *pos;        // current playing position in the data
43    Uint32            length;        // Length of the data
44    Uint32            volume;        // Volume for this effect.
45
46    effect_handle( effect_handle *Next )
47    {
48        next = Next;
49        if( next )
50        {
51            next->prev = this;
52        }
53        prev = NULL;
54        data = NULL;
55        pos = NULL;
56    }
57
58    ~effect_handle()
59    {
60        if( next )
61        {
62            next->prev = prev;
63        }
64        if( prev )
65        {
66            prev->next = next;
67        }
68    }
69};
70
71#ifndef USE_SDL_MIXER
72static effect_handle * fx_list = NULL;
73#endif
74extern flags_struct flags;
75static int sound_enabled = 0;
76static SDL_AudioSpec audioObtained;
77
78//
79// mix_audio
80//
81// Do the actual playing of the audio by looping through our effects list
82// and mixing each sample.
83//
84void mix_audio(void *udata, Uint8 *stream, int len)
85{
86#ifndef USE_SDL_MIXER
87    effect_handle *handle = fx_list;
88
89    while( handle )
90    {
91        if( handle->length > 0 && handle->pos )
92        {
93            len = ( len > (int)handle->length ? handle->length : len );
94            SDL_MixAudio( stream, handle->pos, len, handle->volume );
95            handle->pos += len;
96            handle->length -= len;
97            handle = handle->next;
98        }
99        else
100        {
101            // update the list pointer if we're deleting the first node
102            if( fx_list == handle )
103            {
104                fx_list = handle->next;
105            }
106            effect_handle *tmp = handle->next;
107            if( !flags.mono )
108            {
109                // delete the audio buffer
110                free( handle->data );
111            }
112            delete handle;
113            handle = tmp;
114        }
115    }
116#endif
117}
118
119//
120// sound_init()
121// Initialise audio
122//
123int sound_init( int argc, char **argv )
124{
125    char *sfxdir, *datadir;
126    FILE *fd = NULL;
127
128    // Disable sound if requested.
129    if( flags.nosound )
130    {
131        // User requested that sound be disabled
132        printf( "Sound: Disabled (-nosound)\n" );
133        return 0;
134    }
135
136    // Check for the sfx directory, disable sound if we can't find it.
137    datadir = get_filename_prefix();
138    sfxdir = (char *)malloc( strlen( datadir ) + 5 + 1 );
139    sprintf( sfxdir, "%s/sfx/", datadir );
140    if( (fd = fopen( sfxdir,"r" )) == NULL )
141    {
142        // Didn't find the directory, so disable sound.
143        printf( "Sound: Disabled (couldn't find the sfx directory)\n" );
144        return 0;
145    }
146    free( sfxdir );
147
148#ifdef USE_SDL_MIXER
149    if (Mix_OpenAudio(11025, AUDIO_U8, 2, 128) < 0)
150    {
151        printf( "Sound: Unable to open audio - %s\nSound: Disabled (error)\n", SDL_GetError() );
152        return 0;
153    }
154
155    Mix_AllocateChannels(50);
156
157    int tempChannels = 0;
158    Mix_QuerySpec(&audioObtained.freq, &audioObtained.format, &tempChannels);
159    audioObtained.channels = tempChannels & 0xFF;
160
161    sound_enabled = SFX_INITIALIZED | MUSIC_INITIALIZED;
162
163    printf( "Music: Enabled\n" );
164#else
165    SDL_AudioSpec audioWanted;
166    audioWanted.freq = 11025;
167    audioWanted.format = AUDIO_U8;
168    audioWanted.channels = 2 - flags.mono;
169    audioWanted.samples = 128;
170    audioWanted.callback = mix_audio;
171    audioWanted.userdata = NULL;
172
173    // Now open the audio device
174    if( SDL_OpenAudio( &audioWanted, &audioObtained ) < 0 )
175    {
176        printf( "Sound: Unable to open audio - %s\nSound: Disabled (error)\n", SDL_GetError() );
177        return 0;
178    }
179
180    SDL_PauseAudio( 0 );
181
182    sound_enabled = SFX_INITIALIZED;
183#endif
184
185    printf( "Sound: Enabled\n" );
186
187    // It's all good
188    return sound_enabled;
189}
190
191//
192// sound_uninit
193//
194// Shutdown audio and release any memory left over.
195//
196void sound_uninit()
197{
198    if( sound_enabled )
199    {
200#ifdef USE_SDL_MIXER
201        Mix_CloseAudio();
202#else
203        SDL_PauseAudio( 1 );
204        while( fx_list )
205        {
206            effect_handle *last = fx_list;
207            fx_list = fx_list->next;
208            free( last );
209        }
210        SDL_CloseAudio();
211#endif
212    }
213}
214
215//
216// sound_effect constructor
217//
218// Read in the requested .wav file.
219//
220sound_effect::sound_effect( char * filename )
221{
222    if( sound_enabled )
223    {
224        int sample_speed;
225
226#ifdef USE_SDL_MIXER
227        void* temp_data = (void *)read_wav( filename, sample_speed, size );
228
229        SDL_AudioCVT audiocvt;
230
231        SDL_BuildAudioCVT( &audiocvt, AUDIO_U8, 1, 11025, audioObtained.format, audioObtained.channels, audioObtained.freq );
232        data = malloc( size * audiocvt.len_mult );
233
234        memcpy( data, temp_data, size );
235        audiocvt.buf = (Uint8*)data;
236        audiocvt.len = size;
237        SDL_ConvertAudio( &audiocvt );
238        size = (Uint32)((double)size * audiocvt.len_ratio);
239
240        free(temp_data);
241
242        this->chunk = Mix_QuickLoad_RAW((Uint8*)data, size);
243#else
244        data = (void *)read_wav( filename, sample_speed, size );
245#endif
246    }
247}
248
249//
250// sound_effect destructor
251//
252// Release the audio data.
253//
254sound_effect::~sound_effect()
255{
256    if( sound_enabled )
257    {
258#ifdef USE_SDL_MIXER
259        // Sound effect deletion only happens on level load, so there
260        // is no problem in stopping everything. But the original playing
261        // code handles the sound effects and the "playlist" differently.
262        // Therefore with SDL_mixer, a sound that has not finished playing
263        // on a level load will cut off in the middle. This is most noticable
264        // for the button sound of the load savegame dialog.
265        Mix_FadeOutGroup(-1, 100);
266        while (Mix_Playing(-1))
267            SDL_Delay(10);
268        Mix_FreeChunk(this->chunk);
269#endif
270
271        if( data )
272        {
273            free( data );
274        }
275    }
276}
277
278//
279// sound_effect::play
280//
281// Insert a new effect_handle into the list and modify the audio
282// if we're doing stereo.
283// panpot defines the pan position for the sound effect.
284//   0   - Completely to the right.
285//   128 - Centered.
286//   255 - Completely to the left.
287//
288void sound_effect::play( int volume, int pitch, int panpot )
289{
290    if( sound_enabled )
291    {
292#ifdef USE_SDL_MIXER
293        int channel = Mix_PlayChannel(-1, this->chunk, 0);
294        if (channel > -1)
295        {
296            Mix_Volume(channel, volume);
297            Mix_SetPanning(channel, panpot, 255 - panpot);
298        }
299#else
300        SDL_LockAudio();
301
302        fx_list = new effect_handle( fx_list );
303        if( fx_list == NULL )
304        {
305            printf( "Sound: ERROR - Failed to create new effect.\n" );
306            SDL_UnlockAudio();
307            return;
308        }
309
310        if( !flags.mono )
311        {
312            unsigned int i;
313            Uint32 cvtBufferSize;
314            SDL_AudioCVT audiocvt;
315
316            // Do some audio conversion
317            SDL_BuildAudioCVT( &audiocvt, AUDIO_U8, 1, 11025, audioObtained.format, audioObtained.channels, audioObtained.freq );
318            audiocvt.buf = (Uint8 *)malloc( size * audiocvt.len_mult );
319            audiocvt.len = size;
320            memcpy( audiocvt.buf, data, size );
321            SDL_ConvertAudio( &audiocvt );
322            cvtBufferSize = (Uint32)((double)size * audiocvt.len_ratio);
323
324            // Adjust for requested pan position
325            if( panpot != 128 )
326            {
327                if( panpot > 128 )
328                {
329                    // Pan to the left
330                    panpot = (panpot - 255) * -1;
331                    for( i = 1 ; i <= cvtBufferSize; i += 2 )
332                    {
333                        audiocvt.buf[i] = (((audiocvt.buf[i] - 128) * panpot) / 128) + 128;
334                    }
335                }
336                else
337                {
338                    // Pan to the right
339                    for( i = 0 ; i < cvtBufferSize; i += 2 )
340                    {
341                        audiocvt.buf[i] = (((audiocvt.buf[i] - 128) * panpot) / 128) + 128;
342                    }
343                }
344            }
345
346            fx_list->data = audiocvt.buf;
347            fx_list->pos = audiocvt.buf;
348            fx_list->length = cvtBufferSize;
349            fx_list->volume = volume;
350        }
351        else
352        {
353            // Only doing mono so don't mess with the audio data.
354            fx_list->data = (Uint8 *)data;
355            fx_list->pos = (Uint8 *)data;
356            fx_list->length = size;
357            fx_list->volume = volume;
358        }
359        SDL_UnlockAudio();
360#endif
361    }
362}
363
364
365// Play music using SDL_Mixer
366
367song::song(char const * filename)
368{
369    data = NULL;
370    Name = strdup(filename);
371    song_id = 0;
372
373#ifdef USE_SDL_MIXER
374    rw = NULL;
375    music = NULL;
376
377    char realname[255];
378    strcpy(realname, get_filename_prefix());
379    strcat(realname, filename);
380
381    unsigned int data_size;
382    data = load_hmi(realname, data_size);
383
384    if (!data)
385    {
386        printf("Music: ERROR - could not load %s\n", realname);
387        return;
388    }
389
390    rw = SDL_RWFromMem(data, data_size);
391    music = Mix_LoadMUS_RW(rw);
392
393    if (!music)
394    {
395        printf("Music: ERROR - %s while loading %s\n",
396               Mix_GetError(), realname);
397        return;
398    }
399#endif
400}
401
402song::~song()
403{
404    if(playing())
405        stop();
406    free(data);
407    free(Name);
408
409#ifdef USE_SDL_MIXER
410    Mix_FreeMusic(music);
411    SDL_FreeRW(rw);
412#endif
413}
414
415void song::play( unsigned char volume )
416{
417    song_id = 1;
418
419#ifdef USE_SDL_MIXER
420    Mix_PlayMusic(this->music, 0);
421    Mix_VolumeMusic(volume);
422#endif
423}
424
425void song::stop( long fadeout_time )
426{
427    song_id = 0;
428
429#ifdef USE_SDL_MIXER
430    Mix_FadeOutMusic(100);
431#endif
432}
433
434int song::playing()
435{
436#ifdef USE_SDL_MIXER
437    return Mix_PlayingMusic();
438#else
439    return song_id;
440#endif
441}
442
443void song::set_volume( int volume )
444{
445#ifdef USE_SDL_MIXER
446    Mix_VolumeMusic(volume);
447#else
448    // do nothing...
449#endif
450}
Note: See TracBrowser for help on using the repository browser.