JOALSoundImpl.java :  » Game » Jake2-0.9.5 » Jake2 » sound » joal » Java Open Source

Java Open Source » Game » Jake2 0.9.5 
Jake2 0.9.5 » Jake2 » sound » joal » JOALSoundImpl.java
/*
 * JOALSoundImpl.java
 * Copyright (C) 2004
 *
 * $Id: JOALSoundImpl.java,v 1.15 2005/12/04 19:21:04 cawe Exp $
 */
package jake2.sound.joal;

import jake2.Defines;
import jake2.Globals;
import jake2.game.*;
import jake2.qcommon.*;
import jake2.sound.*;
import jake2.util.Lib;
import jake2.util.Vargs;

import java.io.*;
import java.nio.*;

import net.java.games.joal.*;
import net.java.games.joal.eax.EAX;
import net.java.games.joal.eax.EAXFactory;

/**
 * JOALSoundImpl
 */
public final class JOALSoundImpl implements Sound {
  
  static {
    S.register(new JOALSoundImpl());
  };

  static AL al;
  static ALC alc;
  static EAX eax;
  
  cvar_t s_volume;
  
  private int[] buffers = new int[MAX_SFX + STREAM_QUEUE];

  // singleton 
  private JOALSoundImpl() {
  }

  /**
   * unpack OpenAL shared library on Linux
   */
  private void unpack() {
    String path = System.getProperty("user.home") + "/.jake2/libopenal.so";
    File f = new File(path);
    if (!f.exists()) {
      try {
        f.createNewFile();
        InputStream in = getClass().getResourceAsStream("/libopenal.so");
        OutputStream out = new FileOutputStream(f);
        byte[] buf = new byte[8192];
        int len;
        while ((len = in.read(buf)) > 0) {
          out.write(buf, 0, len);
        }
      } catch (Exception e) {
        f.delete();
      }
    }
  }
  
  /* (non-Javadoc)
   * @see jake2.sound.SoundImpl#Init()
   */
  public boolean Init() {
    
    // preload OpenAL native library
    String os = System.getProperty("os.name");
    if (os.startsWith("Linux")) {
      unpack();  
    } else if (os.startsWith("Windows")) {
        try {
            System.loadLibrary("OpenAL32");
        } catch (Throwable e) {}
    }
    
    try {
      initOpenAL();
      al = ALFactory.getAL();
      checkError();
      initOpenALExtensions();    
    } catch (OpenALException e) {
      Com.Printf(e.getMessage() + '\n');
      return false;
    } catch (Throwable e) {
      Com.DPrintf(e.getMessage() + '\n');
      return false;
    }
    // set the master volume
    s_volume = Cvar.Get("s_volume", "0.7", Defines.CVAR_ARCHIVE);

    al.alGenBuffers(buffers.length, buffers);
    int count = Channel.init(al, buffers);
    Com.Printf("... using " + count + " channels\n");
    al.alDistanceModel(AL.AL_INVERSE_DISTANCE_CLAMPED);
    Cmd.AddCommand("play", new xcommand_t() {
      public void execute() {
        Play();
      }
    });
    Cmd.AddCommand("stopsound", new xcommand_t() {
      public void execute() {
        StopAllSounds();
      }
    });
    Cmd.AddCommand("soundlist", new xcommand_t() {
      public void execute() {
        SoundList();
      }
    });
    Cmd.AddCommand("soundinfo", new xcommand_t() {
      public void execute() {
        SoundInfo_f();
      }
    });

    num_sfx = 0;

    Com.Printf("sound sampling rate: 44100Hz\n");

    StopAllSounds();
    Com.Printf("------------------------------------\n");
    return true;
  }
  
  
  private void initOpenAL() throws OpenALException {
    ALFactory.initialize();
    alc = ALFactory.getALC();
    String deviceName = null;

    String os = System.getProperty("os.name");
    if (os.startsWith("Windows")) {
      deviceName = "DirectSound3D";
    }
    ALC.Device device = alc.alcOpenDevice(deviceName);
    String deviceSpecifier = alc.alcGetString(device, ALC.ALC_DEVICE_SPECIFIER);
    String defaultSpecifier = alc.alcGetString(device, ALC.ALC_DEFAULT_DEVICE_SPECIFIER);

    Com.Printf(os + " using " + ((deviceName == null) ? defaultSpecifier : deviceName) + '\n');

    ALC.Context context = alc.alcCreateContext(device, new int[] {0});
    alc.alcMakeContextCurrent(context);
    // Check for an error.
    if (alc.alcGetError(device) != ALC.ALC_NO_ERROR) {
      Com.DPrintf("Error with SoundDevice");
    }
  }
  
  private void initOpenALExtensions() {
    if (al.alIsExtensionPresent("EAX2.0")) {
      Com.Printf("... using EAX2.0\n");
      eax = EAXFactory.getEAX();
    } else {
      Com.Printf("... EAX2.0 not found\n");
      eax = null;
    }
  }
  
  void exitOpenAL() {
    // Get the current context.
    ALC.Context curContext = alc.alcGetCurrentContext();
    // Get the device used by that context.
    ALC.Device curDevice = alc.alcGetContextsDevice(curContext);
    // Reset the current context to NULL.
    alc.alcMakeContextCurrent(null);
    // Release the context and the device.
    alc.alcDestroyContext(curContext);
    alc.alcCloseDevice(curDevice);
  }
  
    // TODO check the sfx direct buffer size
    // 2MB sfx buffer
    private ByteBuffer sfxDataBuffer = Lib.newByteBuffer(2 * 1024 * 1024);
    
    /* (non-Javadoc)
     * @see jake2.sound.SoundImpl#RegisterSound(jake2.sound.sfx_t)
     */
    private void initBuffer(byte[] samples, int bufferId, int freq) {
        ByteBuffer data = sfxDataBuffer.slice();
        data.put(samples).flip();
        al.alBufferData(buffers[bufferId], AL.AL_FORMAT_MONO16,
                data, data.limit(), freq);
    }

  private void checkError() {
    Com.DPrintf("AL Error: " + alErrorString() +'\n');
  }
  
  private String alErrorString(){
    int error;
    String message = "";
    if ((error = al.alGetError()) != AL.AL_NO_ERROR) {
      switch(error) {
        case AL.AL_INVALID_OPERATION: message = "invalid operation"; break;
        case AL.AL_INVALID_VALUE: message = "invalid value"; break;
        case AL.AL_INVALID_ENUM: message = "invalid enum"; break;
        case AL.AL_INVALID_NAME: message = "invalid name"; break;
        default: message = "" + error;
      }
    }
    return message; 
  }

  /* (non-Javadoc)
   * @see jake2.sound.SoundImpl#Shutdown()
   */
  public void Shutdown() {
    StopAllSounds();
    Channel.shutdown();
    al.alDeleteBuffers(buffers.length, buffers);
    exitOpenAL();

    Cmd.RemoveCommand("play");
    Cmd.RemoveCommand("stopsound");
    Cmd.RemoveCommand("soundlist");
    Cmd.RemoveCommand("soundinfo");

    // free all sounds
    for (int i = 0; i < num_sfx; i++) {
      if (known_sfx[i].name == null)
        continue;
      known_sfx[i].clear();
    }
    num_sfx = 0;
  }
  
  /* (non-Javadoc)
   * @see jake2.sound.SoundImpl#StartSound(float[], int, int, jake2.sound.sfx_t, float, float, float)
   */
  public void StartSound(float[] origin, int entnum, int entchannel, sfx_t sfx, float fvol, float attenuation, float timeofs) {

    if (sfx == null)
      return;
      
    if (sfx.name.charAt(0) == '*')
      sfx = RegisterSexedSound(Globals.cl_entities[entnum].current, sfx.name);
    
    if (LoadSound(sfx) == null)
      return; // can't load sound

    if (attenuation != Defines.ATTN_STATIC)
      attenuation *= 0.5f;

    PlaySound.allocate(origin, entnum, entchannel, buffers[sfx.bufferId], fvol, attenuation, timeofs);
  }

  private float[] listenerOrigin = {0, 0, 0};
  private float[] listenerOrientation = {0, 0, 0, 0, 0, 0};
  private IntBuffer eaxEnv = Lib.newIntBuffer(1);
  private int currentEnv = -1;
  private boolean changeEnv = true;
  
  // TODO workaround for JOAL-bug
  // should be EAX.LISTENER
  private final static int EAX_LISTENER = 0;
  // should be EAX.SOURCE
  private final static int EAX_SOURCE = 1;

  /* (non-Javadoc)
   * @see jake2.sound.SoundImpl#Update(float[], float[], float[], float[])
   */
  public void Update(float[] origin, float[] forward, float[] right, float[] up) {
    Channel.convertVector(origin, listenerOrigin);    
    al.alListenerfv(AL.AL_POSITION, listenerOrigin);

    Channel.convertOrientation(forward, up, listenerOrientation);    
    al.alListenerfv(AL.AL_ORIENTATION, listenerOrientation);
    
    // set the listener (master) volume
    al.alListenerf(AL.AL_GAIN, s_volume.value);
    
    if (eax != null) {
      // workaround for environment initialisation
      if (currentEnv == -1) {
        eaxEnv.put(0, EAX.EAX_ENVIRONMENT_UNDERWATER);
        eax.EAXSet(EAX_LISTENER, EAX.DSPROPERTY_EAXLISTENER_ENVIRONMENT | EAX.DSPROPERTY_EAXLISTENER_DEFERRED, 0, eaxEnv, 4);
        changeEnv = true;
      }

      if ((GameBase.gi.pointcontents.pointcontents(origin)& Defines.MASK_WATER)!= 0) {
        changeEnv = currentEnv != EAX.EAX_ENVIRONMENT_UNDERWATER;
        currentEnv = EAX.EAX_ENVIRONMENT_UNDERWATER;
      } else {
        changeEnv = currentEnv != EAX.EAX_ENVIRONMENT_GENERIC;
        currentEnv = EAX.EAX_ENVIRONMENT_GENERIC;
      }
      if (changeEnv) {
        eaxEnv.put(0, currentEnv);
        eax.EAXSet(EAX_LISTENER, EAX.DSPROPERTY_EAXLISTENER_ENVIRONMENT | EAX.DSPROPERTY_EAXLISTENER_DEFERRED, 0, eaxEnv, 4);
      }
    }

      Channel.addLoopSounds();
      Channel.addPlaySounds();
    Channel.playAllSounds(listenerOrigin);
  }

  /* (non-Javadoc)
   * @see jake2.sound.SoundImpl#StopAllSounds()
   */
  public void StopAllSounds() {
    // mute the listener (master)
    al.alListenerf(AL.AL_GAIN, 0);
      PlaySound.reset();
      Channel.reset();
  }
  
  /* (non-Javadoc)
   * @see jake2.sound.Sound#getName()
   */
  public String getName() {
    return "joal";
  }

  int s_registration_sequence;
  boolean s_registering;

  /* (non-Javadoc)
   * @see jake2.sound.Sound#BeginRegistration()
   */
  public void BeginRegistration() {
    s_registration_sequence++;
    s_registering = true;
  }

  /* (non-Javadoc)
   * @see jake2.sound.Sound#RegisterSound(java.lang.String)
   */
  public sfx_t RegisterSound(String name) {
    sfx_t sfx = FindName(name, true);
    sfx.registration_sequence = s_registration_sequence;

    if (!s_registering)
      LoadSound(sfx);
      
    return sfx;
  }

  /* (non-Javadoc)
   * @see jake2.sound.Sound#EndRegistration()
   */
  public void EndRegistration() {
    int i;
    sfx_t sfx;

    // free any sounds not from this registration sequence
    for (i = 0; i < num_sfx; i++) {
      sfx = known_sfx[i];
      if (sfx.name == null)
        continue;
      if (sfx.registration_sequence != s_registration_sequence) {
        // don't need this sound
        sfx.clear();
      }
    }

    // load everything in
    for (i = 0; i < num_sfx; i++) {
      sfx = known_sfx[i];
      if (sfx.name == null)
        continue;
      LoadSound(sfx);
    }

    s_registering = false;
  }
  
  sfx_t RegisterSexedSound(entity_state_t ent, String base) {

    sfx_t sfx = null;

    // determine what model the client is using
    String model = null;
    int n = Globals.CS_PLAYERSKINS + ent.number - 1;
    if (Globals.cl.configstrings[n] != null) {
      int p = Globals.cl.configstrings[n].indexOf('\\');
      if (p >= 0) {
        p++;
        model = Globals.cl.configstrings[n].substring(p);
        //strcpy(model, p);
        p = model.indexOf('/');
        if (p > 0)
          model = model.substring(0, p);
      }
    }
    // if we can't figure it out, they're male
    if (model == null || model.length() == 0)
      model = "male";

    // see if we already know of the model specific sound
    String sexedFilename = "#players/" + model + "/" + base.substring(1);
    //Com_sprintf (sexedFilename, sizeof(sexedFilename), "#players/%s/%s", model, base+1);
    sfx = FindName(sexedFilename, false);

    if (sfx != null) return sfx;
    
    //
    // fall back strategies
    //
    // not found , so see if it exists
    if (FS.FileLength(sexedFilename.substring(1)) > 0) {
      // yes, register it
      return RegisterSound(sexedFilename);
    }
      // try it with the female sound in the pak0.pak
    if (model.equalsIgnoreCase("female")) {
      String femaleFilename = "player/female/" + base.substring(1);
      if (FS.FileLength("sound/" + femaleFilename) > 0)
          return AliasName(sexedFilename, femaleFilename);
    }
    // no chance, revert to the male sound in the pak0.pak
    String maleFilename = "player/male/" + base.substring(1);
    return AliasName(sexedFilename, maleFilename);
  }

  static sfx_t[] known_sfx = new sfx_t[MAX_SFX];
  static {
    for (int i = 0; i< known_sfx.length; i++)
      known_sfx[i] = new sfx_t();
  }
  static int num_sfx;

  sfx_t FindName(String name, boolean create) {
    int i;
    sfx_t sfx = null;

    if (name == null)
      Com.Error(Defines.ERR_FATAL, "S_FindName: NULL\n");
    if (name.length() == 0)
      Com.Error(Defines.ERR_FATAL, "S_FindName: empty name\n");

    if (name.length() >= Defines.MAX_QPATH)
      Com.Error(Defines.ERR_FATAL, "Sound name too long: " + name);

    // see if already loaded
    for (i = 0; i < num_sfx; i++)
      if (name.equals(known_sfx[i].name)) {
        return known_sfx[i];
      }

    if (!create)
      return null;

    // find a free sfx
    for (i = 0; i < num_sfx; i++)
      if (known_sfx[i].name == null)
        // registration_sequence < s_registration_sequence)
        break;

    if (i == num_sfx) {
      if (num_sfx == MAX_SFX)
        Com.Error(Defines.ERR_FATAL, "S_FindName: out of sfx_t");
      num_sfx++;
    }

    sfx = known_sfx[i];
    sfx.clear();
    sfx.name = name;
    sfx.registration_sequence = s_registration_sequence;
    sfx.bufferId = i;

    return sfx;
  }

  /*
  ==================
  S_AliasName

  ==================
  */
  sfx_t AliasName(String aliasname, String truename)
  {
    sfx_t sfx = null;
    String s;
    int i;

    s = new String(truename);

    // find a free sfx
    for (i=0 ; i < num_sfx ; i++)
      if (known_sfx[i].name == null)
        break;

    if (i == num_sfx)
    {
      if (num_sfx == MAX_SFX)
        Com.Error(Defines.ERR_FATAL, "S_FindName: out of sfx_t");
      num_sfx++;
    }
  
    sfx = known_sfx[i];
    sfx.clear();
    sfx.name = new String(aliasname);
    sfx.registration_sequence = s_registration_sequence;
    sfx.truename = s;
    // set the AL bufferId
    sfx.bufferId = i;

    return sfx;
  }

  /*
  ==============
  S_LoadSound
  ==============
  */
  public sfxcache_t LoadSound(sfx_t s) {
      if (s.isCached) return s.cache;
    sfxcache_t sc = WaveLoader.LoadSound(s);
    if (sc != null) {
      initBuffer(sc.data, s.bufferId, sc.speed);
        s.isCached = true;
        // free samples for GC
        s.cache.data = null;
    }
    return sc;
  }

  /* (non-Javadoc)
   * @see jake2.sound.Sound#StartLocalSound(java.lang.String)
   */
  public void StartLocalSound(String sound) {
    sfx_t sfx = RegisterSound(sound);
    if (sfx == null) {
      Com.Printf("S_StartLocalSound: can't cache " + sound + "\n");
      return;
    }
    StartSound(null, Globals.cl.playernum + 1, 0, sfx, 1, 1, 0.0f);    
  }

    private ShortBuffer streamBuffer = sfxDataBuffer.slice().order(ByteOrder.BIG_ENDIAN).asShortBuffer();

    /* (non-Javadoc)
     * @see jake2.sound.Sound#RawSamples(int, int, int, int, byte[])
     */
    public void RawSamples(int samples, int rate, int width, int channels, ByteBuffer data) {
        int format;
        if (channels == 2) {
            format = (width == 2) ? AL.AL_FORMAT_STEREO16
                    : AL.AL_FORMAT_STEREO8;
        } else {
            format = (width == 2) ? AL.AL_FORMAT_MONO16
                    : AL.AL_FORMAT_MONO8;
        }
        
        // convert to signed 16 bit samples
        if (format == AL.AL_FORMAT_MONO8) {
            ShortBuffer sampleData = streamBuffer;
            int value;
            for (int i = 0; i < samples; i++) {
                value = (data.get(i) & 0xFF) - 128;
                sampleData.put(i, (short) value);
            }
            format = AL.AL_FORMAT_MONO16;
            width = 2;
            data = sfxDataBuffer.slice();
        }

        Channel.updateStream(data, samples * channels * width, format, rate);
    }
    
    public void disableStreaming() {
        Channel.disableStreaming();
    }
  
  /*
  ===============================================================================

  console functions

  ===============================================================================
  */

  void Play() {
        int i = 1;
        String name;
    while (i < Cmd.Argc()) {
      name = new String(Cmd.Argv(i));
      if (name.indexOf('.') == -1)
        name += ".wav";

      RegisterSound(name);
      StartLocalSound(name);
      i++;
    }
  }

  void SoundList() {
    int i;
    sfx_t sfx;
    sfxcache_t sc;
    int size, total;

    total = 0;
    for (i = 0; i < num_sfx; i++) {
      sfx = known_sfx[i];
      if (sfx.registration_sequence == 0)
        continue;
      sc = sfx.cache;
      if (sc != null) {
        size = sc.length * sc.width * (sc.stereo + 1);
        total += size;
        if (sc.loopstart >= 0)
          Com.Printf("L");
        else
          Com.Printf(" ");
        Com.Printf("(%2db) %6i : %s\n", new Vargs(3).add(sc.width * 8).add(size).add(sfx.name));
      } else {
        if (sfx.name.charAt(0) == '*')
          Com.Printf("  placeholder : " + sfx.name + "\n");
        else
          Com.Printf("  not loaded  : " + sfx.name + "\n");
      }
    }
    Com.Printf("Total resident: " + total + "\n");
  }
  
  void SoundInfo_f() {
    Com.Printf("%5d stereo\n", new Vargs(1).add(1));
    Com.Printf("%5d samples\n", new Vargs(1).add(22050));
    Com.Printf("%5d samplebits\n", new Vargs(1).add(16));
    Com.Printf("%5d speed\n", new Vargs(1).add(44100));
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.