/* sound.m, 
 *
 * This file handles sound stuff.  Will be more useful when Allegro
 * dependence is removed.
 */

#include <assert.h>
#include "common.h"
#include "seborrhea/seborrhea-allegro.h" /* XXX */
#include "seborrhea/seborrhea.h"
#include "sound.h"


BOOL sound_initialized;
int sound_vol = 255;


static struct {
    int voice_n, priority;
} reserved_channels[TOTAL_RESERVED_CHANNELS];

/*------------------------------------------------------------*/
/* Sound.                                                     */
/*------------------------------------------------------------*/

static int calculate_pan(int x)
{
    x = PANRANGE * (x - screen_w/2)/screen_w + (PANMAX/2);
    return MID(0, x, PANMAX);
}

void play_panned_sample(Sebum<SebSample> *spl, int x)
{
    if (spl)
	[spl playWithVolume:sound_vol/SOUND_VOLMAX Pan:calculate_pan(x)];
}

BOOL play_voice(Sebum<SebSample> *spl, int x, int priority, enum VOICE_CHANNEL ch)
{
    SAMPLE *s;
    int voice_n;
    assert(ch < TOTAL_RESERVED_CHANNELS);

    /* No sound or mute. */
    if (not spl || sound_vol == 0)
	return YES;

    /* This channel is occupied by something of higher priority. */
    if (reserved_channels[ch].voice_n >= 0 &&
	reserved_channels[ch].priority >= priority &&
	voice_get_position(reserved_channels[ch].voice_n) > -1)
	return NO;

    s = [(SebSampleAllegro *)spl sample];

    if (voice_check(reserved_channels[ch].voice_n) != s) {
	if (reserved_channels[ch].voice_n >= 0)
	    reallocate_voice(reserved_channels[ch].voice_n, s);
	else
	    reserved_channels[ch].voice_n = allocate_voice(s);

	voice_n = reserved_channels[ch].voice_n;
	reserved_channels[ch].priority = priority;
    }
    else
	voice_n = reserved_channels[ch].voice_n;

    voice_set_priority(voice_n, priority);
    voice_set_volume(voice_n, [spl defaultVolume] * sound_vol/SOUND_VOLMAX);
    voice_set_pan(voice_n, calculate_pan(x));
    voice_start(voice_n);

    return YES;
}

void play_voice_on_channels(Sebum<SebSample> *spl, int x, int p, enum VOICE_CHANNEL c)
{
    if (sound_vol == 0)
	return;

    if (c == PRIMARY_WEAPON_CHANNELS) {
	if (play_voice(spl, x, p, PRIMARY_WEAPON_1)) return;
	if (play_voice(spl, x, p, PRIMARY_WEAPON_2)) return;
	if (play_voice(spl, x, p, PRIMARY_WEAPON_3)) return;
	if (play_voice(spl, x, p, PRIMARY_WEAPON_4)) return;

	/* Last resort: try to play on any other channel. */
	play_panned_sample(spl, x);
    }
}

void free_reserved_sounds(void)
{
    int i;

    for (i = 0; i < TOTAL_RESERVED_CHANNELS; i++) {
	if (reserved_channels[i].voice_n >= 0) {
	    deallocate_voice(reserved_channels[i].voice_n);
	    reserved_channels[i].voice_n = -1;
	}
    }
}

/*------------------------------------------------------------*/

int sound_init(void)
{
    int i;

    reserve_voices(10, 0);

    set_volume_per_voice(1);
    if (install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL) < 0) {
        fprintf(stderr, "Error initializing sound: %s.\n", allegro_error);
	sound_vol = 0;
	return -1;
    }

    for (i = 0; i < TOTAL_RESERVED_CHANNELS; i++)
	reserved_channels[i].voice_n = -1;

    sound_initialized = YES;
    return 0;
}

void sound_shutdown(void)
{
    free_reserved_sounds();
    sound_initialized = NO;
    remove_sound();
}
