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

Last change on this file since 494 was 494, checked in by Sam Hocevar, 12 years ago

style: remove trailing spaces, fix copyright statements.

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