package com.brackeen.javagamebook.sound;

import com.brackeen.javagamebook.graphics.Sprite;

/**
    Klasa Filter3d dziedziczy po SoundFilter i realizuje efekt
    dźwięku 3D. Dźwięk jest filtrowany w taki sposób, że
    cichnie wraz ze wzrostem odległości od słuchacza.
    <p>Możliwe rozszerzenia tej klasy:
    <ul><li>przesuwanie dźwięku między lewym i prawym głośnikiem.
    </ul>
    @see FilteredSoundStream
*/
public class Filter3d extends SoundFilter {

    // Liczba próbek do przesunięcia podczas zmiany głośności.
    private static final int NUM_SHIFTING_SAMPLES = 500;

    private Sprite source;
    private Sprite listener;
    private int maxDistance;
    private float lastVolume;

    /**
        Tworzy nowy obiekt Filter3d z podanym duszkiem źródła i słuchacza.
        Zmiana położenia duszka może być dokonywana w czasie działania
        filtra.
        <p> Parametr maxDistance jest maksymalną odległością, z której
        jest słyszalny dźwięk.
    */
    public Filter3d(Sprite source, Sprite listener,
        int maxDistance)
    {
        this.source = source;
        this.listener = listener;
        this.maxDistance = maxDistance;
        this.lastVolume = 0.0f;
    }

    /**
        Filtruje dźwięk w taki sposób, że przy większej odległości jest cichszy.
    */
    public void filter(byte[] samples, int offset, int length) {

        if (source == null || listener == null) {
            // Brak danych do filtrowania – koniec.
            return;
        }

        // Obliczenie odległości słuchacza od źródła dźwięku.
        float dx = (source.getX() - listener.getX());
        float dy = (source.getY() - listener.getY());
        float distance = (float)Math.sqrt(dx * dx + dy * dy);

        // Ustawienie głośności od 0 (cisza) do 1.
        float newVolume = (maxDistance - distance) / maxDistance;
        if (newVolume <= 0) {
            newVolume = 0;
        }

        // Ustawienie głośności próbek.
        int shift = 0;
        for (int i=offset; i<offset+length; i+=2) {

            float volume = newVolume;

            // Przesunięcie od ostatniej głośności do nowej.
            if (shift < NUM_SHIFTING_SAMPLES) {
                volume = lastVolume + (newVolume - lastVolume) *
                    shift / NUM_SHIFTING_SAMPLES;
                shift++;
            }

            // Zmiana głośności próbki.
            short oldSample = getSample(samples, i);
            short newSample = (short)(oldSample * volume);
            setSample(samples, i, newSample);
        }

        lastVolume = newVolume;
    }
}
