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

Last change on this file since 479 was 479, checked in by Sam Hocevar, 10 years ago

sdlport: add music playback support through SDL_Mixer, courtesy of
Jochen Schleu.

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