com.tnf.ptm.sound.OggSoundManager.java Source code

Java tutorial

Introduction

Here is the source code for com.tnf.ptm.sound.OggSoundManager.java

Source

/*
 * Copyright 2017 TheNightForum
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.tnf.ptm.sound;

import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.math.Vector2;
import com.tnf.ptm.common.Const;
import com.tnf.ptm.assets.audio.OggSound;
import com.tnf.ptm.assets.audio.PlayableSound;
import com.tnf.ptm.common.Nullable;
import com.tnf.ptm.common.PtmMath;
import com.tnf.ptm.common.GameDrawer;
import com.tnf.ptm.common.PtmObject;
import com.tnf.ptm.common.PtmGame;
import com.tnf.ptm.entities.planet.Planet;
import com.tnf.ptm.entities.ship.PtmShip;
import com.tnf.ptm.assets.Assets;
import com.tnf.ptm.common.DebugOptions;
import org.terasology.assets.ResourceUrn;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class OggSoundManager {
    // private static Logger logger = LoggerFactory.getLogger(OggSoundManager.class);
    private final Map<String, OggSound> soundMap;
    private final Map<PtmObject, Map<OggSound, Float>> loopedSoundMap;
    private final DebugHintDrawer debugHintDrawer;

    private float myLoopAwait;

    public OggSoundManager() {
        this.soundMap = new HashMap<>();
        this.loopedSoundMap = new HashMap<>();
        this.debugHintDrawer = new DebugHintDrawer();
    }

    public OggSound getSound(String urn) {
        return getSound(urn, 1.0f);
    }

    public OggSound getSound(String urn, float basePitch) {
        if (soundMap.containsKey(urn)) {
            return soundMap.get(urn);
        }

        OggSound sound = Assets.getSound(new ResourceUrn(urn));
        sound.setBasePitch(basePitch);
        soundMap.put(urn, sound);
        return sound;
    }

    /**
     * Plays a sound. Source must not be null.
     *
     * @param position         position of a sound. If null, source.getPosition() will be used
     * @param source           bearer of a sound. Must not be null for looped sounds
     * @param volumeMultiplier multiplier for sound volume
     */
    public void play(PtmGame game, PlayableSound playableSound, @Nullable Vector2 position,
            @Nullable PtmObject source, float volumeMultiplier) {
        if (playableSound == null) {
            return;
        }

        OggSound sound = playableSound.getOggSound();
        // logger.debug("Playing sound: {}", sound.getUrn().toString());

        // Perform some initial argument validation
        if (source == null && position == null) {
            throw new AssertionError("Either position or source must be non-null");
        }
        if (source == null && sound.getLoopTime() > 0) {
            throw new AssertionError("Attempted to loop a sound without a parent object: " + sound.getUrn());
        }
        if (position == null) {
            position = source.getPosition();
        }

        // Calculate the volume multiplier for the sound
        float globalVolumeMultiplier = game.getCmp().getOptions().sfxVolumeMultiplier;
        if (globalVolumeMultiplier == 0) {
            return;
        }

        Vector2 cameraPosition = game.getCam().getPos();
        Planet nearestPlanet = game.getPlanetMan().getNearestPlanet();

        float airPerc = 0;
        if (nearestPlanet.getConfig().skyConfig != null) {
            float distanceToAtmosphere = cameraPosition.dst(nearestPlanet.getPos())
                    - nearestPlanet.getGroundHeight() - Const.ATM_HEIGHT / 2;
            airPerc = PtmMath.clamp(1 - distanceToAtmosphere / (Const.ATM_HEIGHT / 2));
        }
        if (DebugOptions.SOUND_IN_SPACE) {
            airPerc = 1;
        }

        float maxSoundDist = 1 + 1.5f * Const.CAM_VIEW_DIST_GROUND * airPerc;

        PtmShip hero = game.getHero();
        float soundRadius = hero == null ? 0 : hero.getHull().config.getApproxRadius();
        float distance = position.dst(cameraPosition) - soundRadius;
        float distanceMultiplier = PtmMath.clamp(1 - distance / maxSoundDist);

        float volume = sound.getBaseVolume() * volumeMultiplier * distanceMultiplier * globalVolumeMultiplier;

        if (volume <= 0) {
            return;
        }

        // Calculate the pitch for the sound
        float pitch = PtmMath.rnd(.97f, 1.03f) * game.getTimeFactor() * playableSound.getBasePitch();

        if (skipLooped(source, sound, game.getTime())) {
            return;
        }

        if (DebugOptions.SOUND_INFO) {
            debugHintDrawer.add(source, position, sound.toString());
        }

        Sound gdxSound = sound.getSound();
        gdxSound.play(volume, pitch, 0);
    }

    /**
     * Plays a sound. Source must not be null.
     *
     * @param position position of a sound. If null, source.getPosition() will be used
     * @param source   bearer of a sound. Must not be null for looped sounds
     */
    public void play(PtmGame game, PlayableSound sound, @Nullable Vector2 position, @Nullable PtmObject source) {
        this.play(game, sound, position, source, 1f);
    }

    private boolean skipLooped(PtmObject source, OggSound sound, float time) {
        if (sound.getLoopTime() == 0) {
            return false;
        }

        boolean playing;
        Map<OggSound, Float> looped = loopedSoundMap.get(source);
        if (looped == null) {
            looped = new HashMap<>();
            loopedSoundMap.put(source, looped);
            playing = false;
        } else {
            Float endTime = looped.get(sound);
            if (endTime == null || endTime <= time) {
                looped.put(sound, time + sound.getLoopTime()); // argh, performance loss
                playing = false;
            } else {
                playing = time < endTime;
            }
        }
        return playing;
    }

    public void drawDebug(GameDrawer drawer, PtmGame game) {
        if (DebugOptions.SOUND_INFO) {
            debugHintDrawer.draw(drawer, game);
        }
    }

    public void update(PtmGame game) {
        if (DebugOptions.SOUND_INFO) {
            debugHintDrawer.update(game);
        }

        myLoopAwait -= game.getTimeStep();
        if (myLoopAwait <= 0) {
            myLoopAwait = 30;
            cleanLooped(game);
        }
    }

    private void cleanLooped(PtmGame game) {
        Iterator<PtmObject> it = loopedSoundMap.keySet().iterator();
        while (it.hasNext()) {
            PtmObject o = it.next();
            if (o.shouldBeRemoved(game)) {
                it.remove();
            }
        }
    }

    public void dispose() {
        for (OggSound sound : soundMap.values()) {
            sound.doDispose();
        }
    }
}