Mostly implemented the SoundEffect classes
This commit is contained in:
parent
b9ce68510d
commit
66c6978d43
@ -0,0 +1,170 @@
|
||||
package Microsoft.Xna.Framework.Audio;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.lwjgl.openal.*;
|
||||
|
||||
import System.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author The MonoGame Team
|
||||
*/
|
||||
class AudioLoader
|
||||
{
|
||||
private AudioLoader()
|
||||
{
|
||||
}
|
||||
|
||||
private static int GetSoundFormat(int channels, int bits)
|
||||
{
|
||||
switch (channels)
|
||||
{
|
||||
case 1:
|
||||
return bits == 8 ? AL10.AL_FORMAT_MONO8 : AL10.AL_FORMAT_MONO16;
|
||||
case 2:
|
||||
return bits == 8 ? AL10.AL_FORMAT_STEREO8 : AL10.AL_FORMAT_STEREO16;
|
||||
default: throw new NotSupportedException("The specified sound format is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Load(InputStream data, Integer format, Integer size, Integer frequency)
|
||||
{
|
||||
byte[] audioData = null;
|
||||
format = AL10.AL_FORMAT_MONO8;
|
||||
size = 0;
|
||||
frequency = 0;
|
||||
|
||||
DataInputStream reader = null;
|
||||
|
||||
try
|
||||
{
|
||||
reader = new DataInputStream(data);
|
||||
|
||||
// decide which data type is this
|
||||
|
||||
// for now we'll only support wave files
|
||||
audioData = LoadWave(reader, format, size, frequency);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (reader != null)
|
||||
{
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return audioData;
|
||||
}
|
||||
|
||||
private static byte[] LoadWave(DataInputStream reader, Integer format, Integer size, Integer frequency)
|
||||
{
|
||||
// code based on opentk exemple
|
||||
|
||||
byte[] audioData = null;
|
||||
|
||||
try {
|
||||
//header
|
||||
String signature = new String(ReadChars(reader, 4));
|
||||
|
||||
if (signature != "RIFF")
|
||||
{
|
||||
throw new NotSupportedException("Specified stream is not a wave file.");
|
||||
}
|
||||
|
||||
reader.readInt(); // riff_chunck_size
|
||||
|
||||
String wformat = new String(ReadChars(reader,4));
|
||||
|
||||
if (wformat != "WAVE")
|
||||
{
|
||||
throw new NotSupportedException("Specified stream is not a wave file.");
|
||||
}
|
||||
|
||||
// WAVE header
|
||||
String format_signature = new String(ReadChars(reader, 4));
|
||||
|
||||
while (format_signature != "fmt ")
|
||||
{
|
||||
reader.skipBytes(reader.readInt());
|
||||
format_signature = new String(ReadChars(reader, 4));
|
||||
}
|
||||
|
||||
int format_chunk_size = reader.readInt();
|
||||
|
||||
// total bytes read: tbp
|
||||
int audio_format = reader.readShort(); // 2
|
||||
int num_channels = reader.readShort(); // 4
|
||||
int sample_rate = reader.readInt(); // 8
|
||||
reader.readInt(); // 12, byte_rate
|
||||
reader.readShort(); // 14, block_align
|
||||
int bits_per_sample = reader.readShort(); // 16
|
||||
|
||||
if (audio_format != 1)
|
||||
{
|
||||
throw new NotSupportedException("Wave compression is not supported.");
|
||||
}
|
||||
|
||||
// reads residual bytes
|
||||
if (format_chunk_size > 16)
|
||||
{
|
||||
reader.skipBytes(format_chunk_size - 16);
|
||||
}
|
||||
|
||||
String data_signature = new String(ReadChars(reader, 4));
|
||||
|
||||
while (data_signature.toLowerCase(Locale.ENGLISH) != "data")
|
||||
{
|
||||
reader.skipBytes(reader.readInt());
|
||||
data_signature = new String(ReadChars(reader, 4));
|
||||
}
|
||||
|
||||
if (data_signature != "data")
|
||||
{
|
||||
throw new NotSupportedException("Specified wave file is not supported.");
|
||||
}
|
||||
|
||||
int data_chunk_size = reader.readInt();
|
||||
|
||||
frequency = sample_rate;
|
||||
format = GetSoundFormat(num_channels, bits_per_sample);
|
||||
// TODO: allocate audioData
|
||||
//audioData = reader.readBytes((int)reader.BaseStream.Length);
|
||||
reader.readFully(audioData);
|
||||
size = data_chunk_size;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return audioData;
|
||||
}
|
||||
|
||||
private static char[] ReadChars(DataInputStream stream, int count)
|
||||
{
|
||||
char[] chars = new char[count];
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
chars[i] = (char)stream.readByte();
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return chars;
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
package Microsoft.Xna.Framework.Audio;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
import resources.Resources;
|
||||
|
||||
import System.*;
|
||||
|
||||
/**
|
||||
@ -9,7 +13,9 @@ import System.*;
|
||||
*/
|
||||
public final class DynamicSoundEffectInstance extends SoundEffectInstance
|
||||
{
|
||||
private AudioFormat format;
|
||||
private AudioChannels channels;
|
||||
private int format;
|
||||
private int sampleRate;
|
||||
|
||||
/**
|
||||
* Event that occurs when the number of audio capture buffers awaiting playback is less than or equal to two.
|
||||
@ -34,28 +40,43 @@ public final class DynamicSoundEffectInstance extends SoundEffectInstance
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("sampleRate");
|
||||
}
|
||||
if ((channels.getValue() < AudioChannels.Mono.getValue()) || (channels.getValue() > AudioChannels.Stereo.getValue()))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("channels");
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
this.sampleRate = sampleRate;
|
||||
this.channels = channels;
|
||||
|
||||
switch (channels)
|
||||
{
|
||||
case Mono:
|
||||
this.format = AL10.AL_FORMAT_MONO16;
|
||||
break;
|
||||
case Stereo:
|
||||
this.format = AL10.AL_FORMAT_STEREO16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnBufferNeeded()
|
||||
{
|
||||
if (BufferNeeded.hasHandlers())
|
||||
{
|
||||
BufferNeeded.raise(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected void Dispose(boolean disposing)
|
||||
protected synchronized void Dispose(boolean disposing)
|
||||
{
|
||||
synchronized (super.VoiceHandleLock)
|
||||
if (!super.IsDisposed())
|
||||
{
|
||||
if (!super.IsDisposed())
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
super.Dispose(disposing);
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
super.Dispose(disposing);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,28 +90,24 @@ public final class DynamicSoundEffectInstance extends SoundEffectInstance
|
||||
* @throws System.ArgumentException
|
||||
*
|
||||
*/
|
||||
public TimeSpan GetSampleDuration(int sizeInBytes)
|
||||
public synchronized TimeSpan GetSampleDuration(int sizeInBytes)
|
||||
{
|
||||
synchronized (super.VoiceHandleLock)
|
||||
if (super.IsDisposed())
|
||||
{
|
||||
if (super.IsDisposed())
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().getName(), "This object has already been disposed.");
|
||||
}
|
||||
|
||||
if (sizeInBytes < 0)
|
||||
{
|
||||
throw new ArgumentException("Buffer size cannot be negative.");
|
||||
}
|
||||
|
||||
if (sizeInBytes == 0)
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
// TODO: get this from AudioFormat
|
||||
throw new NotImplementedException();
|
||||
throw new ObjectDisposedException(super.getClass().getName(), Resources.GetString("Resources.ObjectDisposedException"));
|
||||
}
|
||||
|
||||
if (sizeInBytes < 0)
|
||||
{
|
||||
throw new ArgumentException("Buffer size cannot be negative.");
|
||||
}
|
||||
|
||||
if (sizeInBytes == 0)
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(sizeInBytes / (sampleRate * channels.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,31 +121,32 @@ public final class DynamicSoundEffectInstance extends SoundEffectInstance
|
||||
* @throws System.ArgumentOutOfException
|
||||
*
|
||||
*/
|
||||
public int GetSampleSizeInBytes(TimeSpan duration)
|
||||
public synchronized int GetSampleSizeInBytes(TimeSpan duration)
|
||||
{
|
||||
int num = 0;
|
||||
synchronized(super.VoiceHandleLock)
|
||||
|
||||
if (super.IsDisposed())
|
||||
{
|
||||
if (super.IsDisposed())
|
||||
throw new ObjectDisposedException(super.getClass().getName(), "This object has already been disposed.");
|
||||
throw new ObjectDisposedException(super.getClass().getName(), Resources.GetString("Resources.ObjectDisposedException"));
|
||||
}
|
||||
|
||||
if ((duration.getTotalMilliseconds() < 0.0) || (duration.getTotalMilliseconds() > 2147483647.0))
|
||||
{
|
||||
if ((duration.getTotalMilliseconds() < 0.0) || (duration.getTotalMilliseconds() > 2147483647.0))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("duration");
|
||||
}
|
||||
if (duration == TimeSpan.Zero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (duration == TimeSpan.Zero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
catch(OverflowException ex)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("duration", ex);
|
||||
}
|
||||
try
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
catch(OverflowException ex)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("duration", ex);
|
||||
}
|
||||
|
||||
return num;
|
||||
@ -140,18 +158,15 @@ public final class DynamicSoundEffectInstance extends SoundEffectInstance
|
||||
* @throws System.ObjectDisposedException
|
||||
*/
|
||||
@Override
|
||||
public void Play()
|
||||
public synchronized void Play()
|
||||
{
|
||||
synchronized(super.VoiceHandleLock)
|
||||
if (super.IsDisposed())
|
||||
{
|
||||
if (super.IsDisposed())
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().getName(), "This object has already been disposed.");
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
throw new ObjectDisposedException(super.getClass().getName(), Resources.GetString("Resources.ObjectDisposedException"));
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,40 +197,8 @@ public final class DynamicSoundEffectInstance extends SoundEffectInstance
|
||||
* @throws System.ArgumentException
|
||||
*
|
||||
*/
|
||||
public void SubmitBuffer(byte[] buffer, int offset, int count)
|
||||
public synchronized void SubmitBuffer(byte[] buffer, int offset, int count)
|
||||
{
|
||||
synchronized(super.VoiceHandleLock)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
if (super.IsDisposed())
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().getName(), "This object has already been disposed.");
|
||||
}
|
||||
|
||||
if (((buffer == null) || (buffer.length == 0)) || !this.format.IsAligned(buffer.length))
|
||||
{
|
||||
throw new ArgumentException("Ensure that the buffer length is non-zero and meets the block alignment requirements for the audio format.");
|
||||
}
|
||||
if (((offset < 0) || ( offset >= buffer.length)) || !this.format.IsAligned(offset))
|
||||
{
|
||||
throw new ArgumentException("Offset must be within the buffer boundaries and meet the block alignment requirements for the audio format.");
|
||||
}
|
||||
try
|
||||
{
|
||||
num = offset + count;
|
||||
}
|
||||
catch(OverflowException ex)
|
||||
{
|
||||
throw new ArgumentException("Ensure that count is valid and meets the block alignment requirements for the audio format. Offset and count must define a valid region within the buffer boundaries.", ex);
|
||||
}
|
||||
if (((count <= 0) || (num > buffer.length)) |!this.format.IsAligned(count))
|
||||
{
|
||||
throw new ArgumentException("Ensure that count is valid and meets the block alignment requirements for the audio format. Offset and count must define a valid region within the buffer boundaries.");
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
BindDataBuffer(buffer, format, count, sampleRate);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
package Microsoft.Xna.Framework.Audio;
|
||||
|
||||
import System.Runtime.InteropServices.ExternalException;
|
||||
|
||||
/**
|
||||
* The exception that is thrown when there is an attempt to play more than the platform specific maximum SoundEffectInstance sounds concurrently.
|
||||
*
|
||||
* @author Halofreak1990
|
||||
*/
|
||||
public class InstancePlayLimitException extends ExternalException
|
||||
{
|
||||
private static final long serialVersionUID = 5795485118619112644L;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the InstancePlayLimitException class.
|
||||
*/
|
||||
public InstancePlayLimitException()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the InstancePlayLimitException class with a specified error message.
|
||||
*
|
||||
* @param message
|
||||
* A String that describes the error. The content of message is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture.
|
||||
*/
|
||||
public InstancePlayLimitException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the InstancePlayLimitException class with a specified error message and a reference to the inner exception that is the cause of this exception.
|
||||
*
|
||||
* @param message
|
||||
* Error message that explains the reason for the exception.
|
||||
*
|
||||
* @param inner
|
||||
* Exception that is the cause of the current exception. If innerException is not null, the current exception is raised in a catch block that handles the inner exception.
|
||||
*/
|
||||
public InstancePlayLimitException(String message, Throwable inner)
|
||||
{
|
||||
super(message, inner);
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ public class NoAudioHardwareException extends ExternalException
|
||||
* @param innerException
|
||||
* The exception that is the cause of the current exception. If the innerException parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception.
|
||||
*/
|
||||
public NoAudioHardwareException(String message, RuntimeException innerException)
|
||||
public NoAudioHardwareException(String message, Throwable innerException)
|
||||
{
|
||||
super(message, innerException);
|
||||
}
|
||||
|
@ -0,0 +1,158 @@
|
||||
package Microsoft.Xna.Framework.Audio;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import org.lwjgl.openal.*;
|
||||
|
||||
import System.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author The MonoGame Team
|
||||
* @author Halofreak1990
|
||||
*/
|
||||
class OALSoundBuffer implements IDisposable
|
||||
{
|
||||
private IntBuffer openALDataBuffer;
|
||||
private int openALFormat;
|
||||
private int dataSize;
|
||||
private int sampleRate;
|
||||
private int _sourceId;
|
||||
private boolean _isDisposed;
|
||||
int _pauseCount;
|
||||
|
||||
public double Duration;
|
||||
|
||||
public int getOpenALDataBuffer()
|
||||
{
|
||||
return openALDataBuffer.get(0);
|
||||
}
|
||||
|
||||
public int getSourceId()
|
||||
{
|
||||
return _sourceId;
|
||||
}
|
||||
|
||||
public void setSourceId(int value)
|
||||
{
|
||||
_sourceId = value;
|
||||
|
||||
if (Reserved.hasHandlers())
|
||||
{
|
||||
Reserved.raise(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public final Event<EventArgs> Reserved = new Event<EventArgs>();
|
||||
public final Event<EventArgs> Recycled = new Event<EventArgs>();
|
||||
|
||||
public OALSoundBuffer()
|
||||
{
|
||||
//try
|
||||
//{
|
||||
openALDataBuffer = IntBuffer.allocate(1);
|
||||
|
||||
int alError = AL10.alGetError();
|
||||
AL10.alGenBuffers(openALDataBuffer);
|
||||
alError = AL10.alGetError();
|
||||
|
||||
if (alError != AL10.AL_NO_ERROR)
|
||||
{
|
||||
System.out.println("Failed to generate OpenAL data buffer: " + AL10.alGetString(alError));
|
||||
}
|
||||
/*}
|
||||
catch (DllNotFoundException e)
|
||||
{
|
||||
throw new NoAudioHardwareException("OpenAL drivers could not be found.", e);
|
||||
}*/
|
||||
}
|
||||
|
||||
public void BindDataBuffer(byte[] dataBuffer, int format, int size, int sampleRate)
|
||||
{
|
||||
openALFormat = format;
|
||||
dataSize = size;
|
||||
this.sampleRate = sampleRate;
|
||||
ByteBuffer buf = ByteBuffer.wrap(dataBuffer);
|
||||
AL10.alBufferData(openALDataBuffer.get(0), openALFormat, buf, this.sampleRate);
|
||||
|
||||
int bits, channels;
|
||||
|
||||
bits = AL10.alGetBufferi(openALDataBuffer.get(0), AL10.AL_BITS);
|
||||
int alError = AL10.alGetError();
|
||||
|
||||
if (alError != AL10.AL_NO_ERROR)
|
||||
{
|
||||
System.out.printf("Failed to get buffer bits: %s, format=%d, size=%d, sampleRate=%d", AL10.alGetString(alError), format, size, sampleRate);
|
||||
System.out.println();
|
||||
Duration = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
channels = AL10.alGetBufferi(openALDataBuffer.get(0), AL10.AL_CHANNELS);
|
||||
|
||||
alError = AL10.alGetError();
|
||||
if (alError != AL10.AL_NO_ERROR)
|
||||
{
|
||||
System.out.printf("Failed to get buffer bits: %s, format=%d, size=%d, sampleRate=%d", AL10.alGetString(alError), format, size, sampleRate);
|
||||
System.out.println();
|
||||
Duration = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Duration = (float)(size / ((bits / 8) * channels)) / (float)sampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
//Console.WriteLine("Duration: " + Duration + " / size: " + size + " bits: " + bits + " channels: " + channels + " rate: " + sampleRate);
|
||||
}
|
||||
|
||||
protected void finalize()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
protected void Dispose(boolean disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
AL10.alDeleteBuffers(openALDataBuffer);
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
if (_pauseCount == 0)
|
||||
{
|
||||
AL10.alSourcePause(_sourceId);
|
||||
}
|
||||
|
||||
++_pauseCount;
|
||||
}
|
||||
|
||||
public void RecycleSoundBuffer()
|
||||
{
|
||||
if (Recycled.hasHandlers())
|
||||
{
|
||||
Recycled.raise(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void Resume()
|
||||
{
|
||||
--_pauseCount;
|
||||
|
||||
if (_pauseCount == 0)
|
||||
{
|
||||
AL10.alSourcePlay(_sourceId);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,368 @@
|
||||
package Microsoft.Xna.Framework.Audio;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.*;
|
||||
|
||||
import org.lwjgl.openal.*;
|
||||
|
||||
import System.*;
|
||||
|
||||
final class OpenALSoundController implements IDisposable
|
||||
{
|
||||
private ALCcontext _context;
|
||||
private ALCdevice _device;
|
||||
private static OpenALSoundController _instance = null;
|
||||
private boolean _isDisposed = false;
|
||||
private int _lastOpenALError;
|
||||
private boolean _soundAvailable = false;
|
||||
private Throwable _soundInitException;
|
||||
private List<Integer> availableSourcesCollection;
|
||||
private List<OALSoundBuffer> inUseSourcesCollection;
|
||||
private List<OALSoundBuffer> playingSourcesCollection;
|
||||
private List<OALSoundBuffer> purgeMe;
|
||||
|
||||
private IntBuffer allSourcesArray;
|
||||
private final int MAX_NUMBER_OF_SOURCES = 32;
|
||||
|
||||
private OpenALSoundController()
|
||||
{
|
||||
if (!OpenSoundController())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We have hardware here and it is ready
|
||||
_soundAvailable = true;
|
||||
|
||||
allSourcesArray = IntBuffer.allocate(MAX_NUMBER_OF_SOURCES);
|
||||
AL10.alGenSources(allSourcesArray);
|
||||
|
||||
availableSourcesCollection = new ArrayList<Integer>();
|
||||
|
||||
for (int i = 0; i < MAX_NUMBER_OF_SOURCES; i++)
|
||||
{
|
||||
availableSourcesCollection.add(allSourcesArray.get(i));
|
||||
}
|
||||
|
||||
inUseSourcesCollection = new ArrayList<OALSoundBuffer>();
|
||||
playingSourcesCollection = new ArrayList<OALSoundBuffer>();
|
||||
purgeMe = new ArrayList<OALSoundBuffer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the AL controller was initialized properly. If there was an
|
||||
* exception thrown during the OpenAL init, then that exception is thrown
|
||||
* inside of NoAudioHardwareException.
|
||||
*
|
||||
* @return
|
||||
* True if the controller was initialized, false if not.
|
||||
*/
|
||||
private boolean CheckInitState()
|
||||
{
|
||||
if (!_soundAvailable)
|
||||
{
|
||||
if (_soundInitException != null)
|
||||
{
|
||||
Throwable e = _soundInitException;
|
||||
_soundInitException = null;
|
||||
|
||||
throw new NoAudioHardwareException("No audio hardware available.", e);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the AL context and closes the device, when they exist.
|
||||
*/
|
||||
private void CleanUpOpenAL()
|
||||
{
|
||||
ALC10.alcMakeContextCurrent(null);
|
||||
|
||||
if (_context != null)
|
||||
{
|
||||
ALC10.alcDestroyContext(_context);
|
||||
_context = null;
|
||||
}
|
||||
|
||||
if (_device != null)
|
||||
{
|
||||
ALC10.alcCloseDevice(_device);
|
||||
_device = null;
|
||||
}
|
||||
|
||||
_soundAvailable = false;
|
||||
}
|
||||
|
||||
private boolean OpenSoundController()
|
||||
{
|
||||
_device = ALC10.alcOpenDevice("");
|
||||
|
||||
if (CheckALError("Could not open AL device"))
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (_device != null)
|
||||
{
|
||||
IntBuffer attribute = IntBuffer.allocate(0);
|
||||
|
||||
_context = ALC10.alcCreateContext(_device, attribute);
|
||||
|
||||
if (CheckALError("Could not create AL context"))
|
||||
{
|
||||
CleanUpOpenAL();
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (_context != null)
|
||||
{
|
||||
ALC10.alcMakeContextCurrent(_context);
|
||||
|
||||
if (CheckALError("Could not make AL context current"))
|
||||
{
|
||||
CleanUpOpenAL();
|
||||
return(false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the error state of the OpenAL driver. If a value that is not {@link ALC10.ALC_NO_ERROR}
|
||||
* is returned, then the operation message and the error code is output to the console.
|
||||
*
|
||||
* @param operation
|
||||
* the operation message
|
||||
*
|
||||
* @return
|
||||
* true if an error occurs, and false if not.
|
||||
*/
|
||||
public boolean CheckALError(String operation)
|
||||
{
|
||||
_lastOpenALError = ALC10.alcGetError(_device);
|
||||
|
||||
if (_lastOpenALError == ALC10.ALC_NO_ERROR)
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
String errorFmt = "OpenAL Error: %s";
|
||||
System.out.println(String.format("%s - %s",
|
||||
operation,
|
||||
//String.format(errorFmt, ALC10.alGetString(_device, _lastOpenALError))));
|
||||
String.format(errorFmt, _lastOpenALError)));
|
||||
return (true);
|
||||
}
|
||||
|
||||
public static void DestroyInstance()
|
||||
{
|
||||
if (_instance != null)
|
||||
{
|
||||
_instance.Dispose();
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose of the OpenALSoundCOntroller.
|
||||
*/
|
||||
@Override
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (_soundAvailable)
|
||||
{
|
||||
CleanUpOpenAL();
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static OpenALSoundController GetInstance()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new OpenALSoundController();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
public boolean IsState(OALSoundBuffer soundBuffer, int state)
|
||||
{
|
||||
if (!CheckInitState())
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
int sourceState = AL10.alGetSourcei(soundBuffer.getSourceId(), AL10.AL_SOURCE_STATE);
|
||||
|
||||
if (state == sourceState)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void PauseSound(OALSoundBuffer soundBuffer)
|
||||
{
|
||||
if (!CheckInitState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
soundBuffer.Pause();
|
||||
}
|
||||
|
||||
public void PlaySound(OALSoundBuffer soundBuffer)
|
||||
{
|
||||
if (!CheckInitState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (playingSourcesCollection)
|
||||
{
|
||||
playingSourcesCollection.add(soundBuffer);
|
||||
}
|
||||
|
||||
AL10.alSourcePlay(soundBuffer.getSourceId());
|
||||
}
|
||||
|
||||
public void RecycleSource(OALSoundBuffer soundBuffer)
|
||||
{
|
||||
if (!CheckInitState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
inUseSourcesCollection.remove(soundBuffer);
|
||||
availableSourcesCollection.add(soundBuffer.getSourceId());
|
||||
soundBuffer.RecycleSoundBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserves the given sound buffer. If there are no available sources then false is
|
||||
* returned, otherwise true will be returned and the sound buffer can be played. If
|
||||
* the controller was not able to setup the hardware, then false will be returned.
|
||||
*
|
||||
* @param soundBuffer
|
||||
* The sound buffer you want to play
|
||||
*
|
||||
* @return
|
||||
* True if the buffer can be played, and false if not.
|
||||
*/
|
||||
public boolean ReserveSource(OALSoundBuffer soundBuffer)
|
||||
{
|
||||
if (!CheckInitState())
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
int sourceNumber;
|
||||
|
||||
if (availableSourcesCollection.size() == 0)
|
||||
{
|
||||
soundBuffer.setSourceId(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
sourceNumber = availableSourcesCollection.get(0);
|
||||
soundBuffer.setSourceId(sourceNumber);
|
||||
inUseSourcesCollection.add(soundBuffer);
|
||||
|
||||
availableSourcesCollection.remove(sourceNumber);
|
||||
|
||||
//sourceId = sourceNumber;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResumeSound(OALSoundBuffer soundBuffer)
|
||||
{
|
||||
if (!CheckInitState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
soundBuffer.Resume();
|
||||
}
|
||||
|
||||
public double SourceCurrentPosition(int sourceId)
|
||||
{
|
||||
if (!CheckInitState())
|
||||
{
|
||||
return(0.0);
|
||||
}
|
||||
|
||||
return AL10.alGetSourcei(sourceId, AL11.AL_SAMPLE_OFFSET);
|
||||
}
|
||||
|
||||
public void StopSound(OALSoundBuffer soundBuffer)
|
||||
{
|
||||
if (!CheckInitState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AL10.alSourceStop(soundBuffer.getSourceId());
|
||||
|
||||
AL10.alSourcei(soundBuffer.getSourceId(), AL10.AL_BUFFER, 0);
|
||||
|
||||
synchronized (playingSourcesCollection)
|
||||
{
|
||||
playingSourcesCollection.remove(soundBuffer);
|
||||
}
|
||||
|
||||
RecycleSource(soundBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called repeatedly, this method cleans up the state of the management lists. This method
|
||||
* will also lock on the playingSourcesCollection. Sources that are stopped will be recycled
|
||||
* using the RecycleSource method.
|
||||
*/
|
||||
public void Update()
|
||||
{
|
||||
if (!_soundAvailable)
|
||||
{
|
||||
//OK to ignore this here because the game can run without sound.
|
||||
return;
|
||||
}
|
||||
|
||||
int state;
|
||||
synchronized (playingSourcesCollection)
|
||||
{
|
||||
for (int i = playingSourcesCollection.size() - 1; i >= 0; --i)
|
||||
{
|
||||
OALSoundBuffer soundBuffer = playingSourcesCollection.get(i);
|
||||
|
||||
state = AL10.alGetSourcei(AL10.AL_SOURCE_STATE, soundBuffer.getSourceId());
|
||||
|
||||
if (state == AL10.AL_STOPPED)
|
||||
{
|
||||
purgeMe.add(soundBuffer);
|
||||
playingSourcesCollection.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (OALSoundBuffer soundBuffer : purgeMe)
|
||||
{
|
||||
AL10.alSourcei(soundBuffer.getSourceId(), AL10.AL_BUFFER, 0);
|
||||
RecycleSource(soundBuffer);
|
||||
}
|
||||
|
||||
purgeMe.clear();
|
||||
}
|
||||
}
|
@ -6,7 +6,6 @@ import java.util.*;
|
||||
import org.lwjgl.openal.*;
|
||||
|
||||
import System.*;
|
||||
import System.IO.*;
|
||||
|
||||
/**
|
||||
* Reference page contains links to related code samples.
|
||||
@ -15,6 +14,10 @@ import System.IO.*;
|
||||
*/
|
||||
public final class SoundEffect implements IDisposable
|
||||
{
|
||||
byte[] _data;
|
||||
float Rate;
|
||||
int Size;
|
||||
int Format;
|
||||
private static float currentVolume;
|
||||
private static float distanceScale;
|
||||
private static float dopplerScale;
|
||||
@ -28,7 +31,9 @@ public final class SoundEffect implements IDisposable
|
||||
void ChildDestroyed(SoundEffectInstance instance)
|
||||
{
|
||||
if (children.contains(instance))
|
||||
{
|
||||
children.remove(instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,7 +50,9 @@ public final class SoundEffect implements IDisposable
|
||||
public static void setDistanceScale(float value)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
value = (value <= Float.MIN_NORMAL) ? Float.MIN_NORMAL : value;
|
||||
distanceScale = value;
|
||||
@ -63,10 +70,12 @@ public final class SoundEffect implements IDisposable
|
||||
/**
|
||||
* Sets a value that adjusts the effect of doppler calculations on the sound (emitter).
|
||||
*/
|
||||
public static void setDopplerScale(float value)
|
||||
public synchronized static void setDopplerScale(float value)
|
||||
{
|
||||
if (value < 0f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
dopplerScale = value;
|
||||
AL10.alDopplerFactor(dopplerScale);
|
||||
@ -102,9 +111,13 @@ public final class SoundEffect implements IDisposable
|
||||
public static void setMasterVolume(float value)
|
||||
{
|
||||
if (value < 0f || value > 1f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
currentVolume = value;
|
||||
|
||||
SoundEffectInstancePool.UpdateMasterVolume();
|
||||
}
|
||||
|
||||
static float getMaxVelocityComponent()
|
||||
@ -126,10 +139,14 @@ public final class SoundEffect implements IDisposable
|
||||
public void setName(String value)
|
||||
{
|
||||
if (isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "");
|
||||
}
|
||||
|
||||
if (value == null || value == "")
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
this.effectName = value;
|
||||
}
|
||||
@ -145,19 +162,29 @@ public final class SoundEffect implements IDisposable
|
||||
/**
|
||||
* Sets the speed of sound.
|
||||
*/
|
||||
public static void setSpeedOfSound(float value)
|
||||
public synchronized static void setSpeedOfSound(float value)
|
||||
{
|
||||
if (value <= 0f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
speedOfSound = value;
|
||||
maxVelocityComponent = speedOfSound - (speedOfSound / 1000f);
|
||||
AL11.alSpeedOfSound(speedOfSound);
|
||||
}
|
||||
|
||||
private SoundEffect(Stream stream)
|
||||
private SoundEffect(InputStream stream)
|
||||
{
|
||||
// TODO: implement
|
||||
Integer format = null;
|
||||
Integer size = null;
|
||||
Integer freq = null;
|
||||
|
||||
_data = AudioLoader.Load(stream, format, size, freq);
|
||||
|
||||
Format = format;
|
||||
Size = size;
|
||||
Rate = freq;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,13 +202,12 @@ public final class SoundEffect implements IDisposable
|
||||
public SoundEffect(byte[] buffer, int sampleRate, AudioChannels channels)
|
||||
{
|
||||
if (buffer == null || buffer.length == 0)
|
||||
{
|
||||
throw new ArgumentException("Buffer is invalid. Ensure that the buffer length is non-zero and meets the block alignment requirements for the audio format.");
|
||||
}
|
||||
|
||||
duration = GetSampleDuration(buffer.length, sampleRate, channels);
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
SoundEffect(byte[] format, byte[] data, int loopStart, int loopLength, TimeSpan duration)
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
@ -211,16 +237,41 @@ public final class SoundEffect implements IDisposable
|
||||
*/
|
||||
public SoundEffect(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels, int loopStart, int loopLength)
|
||||
{
|
||||
duration = GetSampleDuration(count, sampleRate, channels);
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
SoundEffectInstance GetPooledInstance()
|
||||
{
|
||||
if (!SoundEffectInstancePool.SoundsAvailable())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
SoundEffectInstance inst = SoundEffectInstancePool.GetInstance();
|
||||
inst._effect = this;
|
||||
|
||||
inst.InitializeSound();
|
||||
inst.BindDataBuffer(_data, Format, Size, (int)Rate);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SoundEffectInstance for this SoundEffect. Reference page contains links to related code samples.
|
||||
*/
|
||||
public synchronized SoundEffectInstance CreateInstance()
|
||||
{
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
SoundEffectInstance inst = new SoundEffectInstance();
|
||||
|
||||
inst.InitializeSound();
|
||||
inst.BindDataBuffer(_data, Format, Size, (int)Rate);
|
||||
|
||||
inst._isPooled = false;
|
||||
inst._effect = this;
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,21 +287,18 @@ public final class SoundEffect implements IDisposable
|
||||
*
|
||||
* @param disposing
|
||||
*/
|
||||
protected void Dispose(boolean disposing)
|
||||
protected synchronized void Dispose(boolean disposing)
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
if (!this.isDisposed)
|
||||
{
|
||||
for (SoundEffectInstance sei : children)
|
||||
{
|
||||
sei.Dispose();
|
||||
}
|
||||
|
||||
// TODO: implement cleanup
|
||||
|
||||
isDisposed = true;
|
||||
if (!this.isDisposed)
|
||||
{
|
||||
for (SoundEffectInstance sei : children)
|
||||
{
|
||||
sei.Dispose();
|
||||
}
|
||||
|
||||
// TODO: implement cleanup
|
||||
|
||||
isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,24 +317,15 @@ public final class SoundEffect implements IDisposable
|
||||
* @throws System.ArgumentNullException
|
||||
* stream is null.
|
||||
*/
|
||||
public static SoundEffect FromStream(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException("stream");
|
||||
|
||||
return new SoundEffect(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SoundEffect object based on the specified data stream.
|
||||
*
|
||||
* @param stream
|
||||
* InputStream object that contains the data for this SoundEffect object.
|
||||
*/
|
||||
public static SoundEffect FromStream(InputStream stream)
|
||||
{
|
||||
// TODO: implement (probably wrap the InputStream, or FileInputStream into a MemoryStream)
|
||||
throw new NotImplementedException();
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException("stream");
|
||||
}
|
||||
|
||||
// TODO: maybe wrap the InputStream, or FileInputStream into a MemoryStream
|
||||
return new SoundEffect(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -304,13 +343,20 @@ public final class SoundEffect implements IDisposable
|
||||
public static TimeSpan GetSampleDuration(int sizeInBytes, int sampleRate, AudioChannels channels)
|
||||
{
|
||||
if (sizeInBytes < 0)
|
||||
{
|
||||
// TODO: error message
|
||||
throw new ArgumentException("", "sizeInBytes");
|
||||
}
|
||||
|
||||
if ((sampleRate < 0x1f40) || (sampleRate > 0xbb80))
|
||||
if ((sampleRate < 8000) || (sampleRate > 48000))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("sampleRate");
|
||||
}
|
||||
|
||||
if (sizeInBytes == 0)
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
return TimeSpan.FromSeconds(sizeInBytes / (sampleRate * channels.getValue()));
|
||||
}
|
||||
@ -330,14 +376,21 @@ public final class SoundEffect implements IDisposable
|
||||
public static int GetSampleSizeInBytes(TimeSpan duration, int sampleRate, AudioChannels channels)
|
||||
{
|
||||
int num = 0;
|
||||
if ((duration.getTotalMilliseconds() < 0.0) || (duration.getTotalMilliseconds() > 2147483647.0))
|
||||
throw new ArgumentOutOfRangeException("duration");
|
||||
|
||||
if ((sampleRate < 0x1f40) || (sampleRate > 0xbb80))
|
||||
if ((duration.getTotalMilliseconds() < 0.0) || (duration.getTotalMilliseconds() > 2147483647.0))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("duration");
|
||||
}
|
||||
|
||||
if ((sampleRate < 8000) || (sampleRate > 48000))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("sampleRate");
|
||||
}
|
||||
|
||||
if (duration == TimeSpan.Zero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
num = duration.getSeconds() * sampleRate * channels.getValue();
|
||||
|
||||
@ -352,7 +405,16 @@ public final class SoundEffect implements IDisposable
|
||||
*/
|
||||
public boolean Play()
|
||||
{
|
||||
return this.Play(1f, 0f, 0f);
|
||||
SoundEffectInstance inst = GetPooledInstance();
|
||||
|
||||
if (inst == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inst.Play();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -372,18 +434,19 @@ public final class SoundEffect implements IDisposable
|
||||
*/
|
||||
public boolean Play(float volume, float pitch, float pan)
|
||||
{
|
||||
try
|
||||
{
|
||||
SoundEffectInstance sei = new SoundEffectInstance(this, true);
|
||||
sei.setVolume(volume);
|
||||
sei.setPitch(pitch);
|
||||
sei.setPan(pan);
|
||||
sei.Play();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
SoundEffectInstance inst = GetPooledInstance();
|
||||
|
||||
if (inst == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inst.setVolume(volume);
|
||||
inst.setPitch(pitch);
|
||||
inst.setPan(pan);
|
||||
|
||||
inst.Play();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,13 @@
|
||||
package Microsoft.Xna.Framework.Audio;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import org.lwjgl.openal.*;
|
||||
|
||||
import resources.Resources;
|
||||
|
||||
import Microsoft.Xna.Framework.*;
|
||||
|
||||
import System.*;
|
||||
|
||||
/**
|
||||
@ -9,30 +17,50 @@ import System.*;
|
||||
*/
|
||||
public class SoundEffectInstance implements IDisposable
|
||||
{
|
||||
private float currentPan;
|
||||
private float currentPitch;
|
||||
private float currentVolume;
|
||||
private boolean isDisposed;
|
||||
private boolean isFireAndForget;
|
||||
private SoundEffect parent;
|
||||
Object VoiceHandleLock;
|
||||
private boolean _isDisposed = false;
|
||||
boolean _isPooled = true;
|
||||
SoundEffect _effect;
|
||||
private float _pan;
|
||||
private float _volume;
|
||||
private float _pitch;
|
||||
private SoundState soundState = SoundState.Stopped;
|
||||
private boolean _looped = false;
|
||||
private float _alVolume = 1;
|
||||
|
||||
int sourceId;
|
||||
|
||||
private OALSoundBuffer soundBuffer;
|
||||
private OpenALSoundController controller;
|
||||
|
||||
boolean hasSourceId = false;
|
||||
|
||||
private final SoundBufferRecycled HandleSoundBufferRecycled = new SoundBufferRecycled();
|
||||
private final SoundBufferReserved HandleSoundBufferReserved = new SoundBufferReserved();
|
||||
|
||||
/**
|
||||
* Gets a value that indicates whether looping is enabled for the SoundEffectInstance. Reference page contains links to related code samples.
|
||||
* Gets a value that indicates whether looping is enabled for the SoundEffectInstance.
|
||||
*/
|
||||
public boolean IsLooped;
|
||||
public boolean IsLooped()
|
||||
{
|
||||
return _looped;
|
||||
}
|
||||
|
||||
public synchronized void IsLooped(boolean value)
|
||||
{
|
||||
if (hasSourceId)
|
||||
{
|
||||
AL10.alSourcei(sourceId, AL10.AL_LOOPING, value ? 1 : 0);
|
||||
}
|
||||
|
||||
_looped = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value that indicates whether the object is disposed.
|
||||
*/
|
||||
public boolean IsDisposed()
|
||||
{
|
||||
return isDisposed;
|
||||
}
|
||||
|
||||
boolean IsFireAndForget()
|
||||
{
|
||||
return this.isFireAndForget;
|
||||
return _isDisposed;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,29 +68,30 @@ public class SoundEffectInstance implements IDisposable
|
||||
*/
|
||||
public float getPan()
|
||||
{
|
||||
return this.currentPan;
|
||||
return this._pan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the panning for the SoundEffectInstance.
|
||||
*/
|
||||
public void setPan(float value)
|
||||
public synchronized void setPan(float value)
|
||||
{
|
||||
synchronized(this)
|
||||
if (this._isDisposed)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (value < -1f || value > 1f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
//Helpers.ThrowExceptionFromErrorCode(SoundEffectUnsafeNativeMethods.SetPan(this.voiceHandle, value));
|
||||
this.currentPan = value;
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (value < -1f || value > 1f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
if (hasSourceId)
|
||||
{
|
||||
AL10.alSource3f(sourceId, AL10.AL_POSITION, value, 0.0f, 0.1f);
|
||||
}
|
||||
|
||||
this._pan = value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,51 +99,71 @@ public class SoundEffectInstance implements IDisposable
|
||||
*/
|
||||
public float getPitch()
|
||||
{
|
||||
return this.currentPitch;
|
||||
return this._pitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pitch adjustment for the SoundEffectInstance. Reference page contains links to related code samples.
|
||||
*/
|
||||
public void setPitch(float value)
|
||||
public synchronized void setPitch(float value)
|
||||
{
|
||||
synchronized(this)
|
||||
if (this._isDisposed)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (value < -1f || value > 1f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
//Helpers.ThrowExceptionFromErrorCode(SoundEffectUnsafeNativeMethods.SetPitch(this.voiceHandle, value));
|
||||
this.currentPitch = value;
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (value < -1f || value > 1f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
if (hasSourceId)
|
||||
{
|
||||
AL10.alSourcef(sourceId, AL10.AL_PITCH, XnaPitchToAlPitch(value));
|
||||
}
|
||||
|
||||
this._pitch = value;
|
||||
}
|
||||
|
||||
SoundEffect getSoundEffect()
|
||||
{
|
||||
return this.parent;
|
||||
return this._effect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current state (playing, paused, or stopped) of the SoundEffectInstance.
|
||||
*/
|
||||
public SoundState getState()
|
||||
public synchronized SoundState getState()
|
||||
{
|
||||
synchronized(this)
|
||||
if (this._isDisposed)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (!hasSourceId)
|
||||
{
|
||||
return SoundState.Stopped;
|
||||
}
|
||||
|
||||
int alState = AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE);
|
||||
|
||||
switch (alState)
|
||||
{
|
||||
case AL10.AL_INITIAL:
|
||||
case AL10.AL_STOPPED:
|
||||
soundState = SoundState.Stopped;
|
||||
break;
|
||||
|
||||
case AL10.AL_PAUSED:
|
||||
soundState = SoundState.Paused;
|
||||
break;
|
||||
|
||||
case AL10.AL_PLAYING:
|
||||
soundState = SoundState.Playing;
|
||||
break;
|
||||
}
|
||||
|
||||
return soundState;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,58 +171,108 @@ public class SoundEffectInstance implements IDisposable
|
||||
*/
|
||||
public float getVolume()
|
||||
{
|
||||
return this.currentVolume;
|
||||
return this._volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the volume of the SoundEffectInstance. Reference page contains links to related code samples.
|
||||
*/
|
||||
public void setVolume(float value)
|
||||
public synchronized void setVolume(float value)
|
||||
{
|
||||
synchronized(this)
|
||||
if (this._isDisposed)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (value < -1f || value > 1f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
//Helpers.ThrowExceptionFromErrorCode(SoundEffectUnsafeNativeMethods.SetVolume(this.voiceHandle, value));
|
||||
this.currentVolume = value;
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (value < -1f || value > 1f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
if (hasSourceId)
|
||||
{
|
||||
AL10.alSourcef(sourceId, AL10.AL_GAIN, value * SoundEffect.getMasterVolume());
|
||||
}
|
||||
|
||||
this._volume = value;
|
||||
this._alVolume = value * SoundEffect.getMasterVolume();
|
||||
}
|
||||
|
||||
SoundEffectInstance()
|
||||
{
|
||||
this.currentVolume = 1f;
|
||||
// TODO: implement
|
||||
this.VoiceHandleLock = new Object();
|
||||
_pan = 0.0f;
|
||||
_volume = 1.0f;
|
||||
_pitch = 0.0f;
|
||||
}
|
||||
|
||||
SoundEffectInstance(SoundEffect parentEffect, boolean fireAndForget)
|
||||
SoundEffectInstance(byte[] buffer, int sampleRate, int channels)
|
||||
{
|
||||
this.currentVolume = 1f;
|
||||
this.VoiceHandleLock = new Object();
|
||||
|
||||
if (parentEffect.IsDisposed())
|
||||
{
|
||||
throw new ObjectDisposedException(SoundEffect.class.getName(), "This object has already been disposed.");
|
||||
}
|
||||
|
||||
this.parent = parentEffect;
|
||||
this.setVolume(1f);
|
||||
this.setPitch(0f);
|
||||
this.setPan(0f);
|
||||
this.IsLooped = false;
|
||||
this.isFireAndForget = fireAndForget;
|
||||
this();
|
||||
|
||||
InitializeSound();
|
||||
BindDataBuffer(
|
||||
buffer,
|
||||
(channels == 2) ? AL10.AL_FORMAT_STEREO16 : AL10.AL_FORMAT_MONO16,
|
||||
buffer.length,
|
||||
sampleRate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preserves the given data buffer by reference and binds its contents to the OALSoundBuffer
|
||||
* that is created in the InitializeSound method.
|
||||
*
|
||||
* @param data
|
||||
* The sound data buffer
|
||||
*
|
||||
* @param format
|
||||
* The sound buffer data format, e.g. Mono, Mono16 bit, Stereo, etc.
|
||||
*
|
||||
* @param size
|
||||
* The size of the data buffer.
|
||||
*
|
||||
* @param rate
|
||||
* The sampling rate of the sound effect, e.g. 44 khz, 22 khz.
|
||||
*/
|
||||
void BindDataBuffer(byte[] data, int format, int size, int rate)
|
||||
{
|
||||
soundBuffer.BindDataBuffer(data, format, size, rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the OpenAL sound controller, constructs the sound buffer, and sets up the event delegates for
|
||||
* the reserved and recycled events.
|
||||
*/
|
||||
void InitializeSound()
|
||||
{
|
||||
controller = OpenALSoundController.GetInstance();
|
||||
soundBuffer = new OALSoundBuffer();
|
||||
soundBuffer.Reserved.addHandler(HandleSoundBufferReserved);
|
||||
soundBuffer.Recycled.addHandler(HandleSoundBufferRecycled);
|
||||
}
|
||||
|
||||
private static float XnaPitchToAlPitch(float xnaPitch)
|
||||
{
|
||||
/*XNA sets pitch bounds to [-1.0f, 1.0f], each end being one octave.
|
||||
•OpenAL's AL_PITCH boundaries are (0.0f, INF). *
|
||||
•Consider the function f(x) = 2 ^ x
|
||||
•The domain is (-INF, INF) and the range is (0, INF). *
|
||||
•0.0f is the original pitch for XNA, 1.0f is the original pitch for OpenAL.
|
||||
•Note that f(0) = 1, f(1) = 2, f(-1) = 0.5, and so on.
|
||||
•XNA's pitch values are on the domain, OpenAL's are on the range.
|
||||
•Remember: the XNA limit is arbitrarily between two octaves on the domain. *
|
||||
•To convert, we just plug XNA pitch into f(x).*/
|
||||
|
||||
if (xnaPitch < -1.0f || xnaPitch > 1.0f)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("XNA PITCH MUST BE WITHIN [-1.0f, 1.0f]!");
|
||||
}
|
||||
|
||||
return (float)Math.pow(2, xnaPitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies 3D positioning to the sound using a single listener. Reference page contains links to related code samples.
|
||||
* Applies 3D positioning to the sound using a single listener.
|
||||
*
|
||||
* @param listener
|
||||
* Position of the listener.
|
||||
@ -181,9 +280,32 @@ public class SoundEffectInstance implements IDisposable
|
||||
* @param emitter
|
||||
* Position of the emitter.
|
||||
*/
|
||||
public void Apply3D(AudioListener listener, AudioEmitter emitter)
|
||||
public synchronized void Apply3D(AudioListener listener, AudioEmitter emitter)
|
||||
{
|
||||
this.Apply3D(new AudioListener[] { listener }, emitter);
|
||||
if (this._isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), Resources.GetString("Resources.ObjectDisposedException"));
|
||||
}
|
||||
|
||||
// get AL's listener position
|
||||
FloatBuffer fb = FloatBuffer.allocate(3);
|
||||
AL10.alGetListener(AL10.AL_POSITION, fb);
|
||||
|
||||
// get the emitter offset from origin
|
||||
Vector3 posOffset = Vector3.Subtract(emitter.getPosition(), listener.getPosition());
|
||||
|
||||
// set up orientation matrix
|
||||
Matrix orientation = Matrix.CreateWorld(Vector3.Zero, listener.getForward(), listener.getUp());
|
||||
|
||||
// set up our final position and velocity according to orientation of listener
|
||||
Vector3 finalPos = new Vector3(fb.get(0) + posOffset.X, fb.get(1) + posOffset.Y, fb.get(2) + posOffset.Z);
|
||||
finalPos = Vector3.Transform(finalPos, orientation);
|
||||
Vector3 finalVel = emitter.getVelocity();
|
||||
finalVel = Vector3.Transform(finalVel, orientation);
|
||||
|
||||
// set the position based on relative position
|
||||
AL10.alSource3f(sourceId, AL10.AL_POSITION, finalPos.X, finalPos.Y, finalPos.Z);
|
||||
AL10.alSource3f(sourceId, AL10.AL_VELOCITY, finalVel.X, finalVel.Y, finalVel.Z);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -197,15 +319,9 @@ public class SoundEffectInstance implements IDisposable
|
||||
*/
|
||||
public void Apply3D(AudioListener[] listeners, AudioEmitter emitter)
|
||||
{
|
||||
synchronized(this)
|
||||
for (AudioListener listener : listeners)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
Apply3D(listener, emitter);
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,18 +339,16 @@ public class SoundEffectInstance implements IDisposable
|
||||
* @param disposing
|
||||
* Pass true to release both the managed and unmanaged resources for this SoundEffectInstance. Passing false releases only the unmanaged resources.
|
||||
*/
|
||||
protected void Dispose(boolean disposing)
|
||||
protected synchronized void Dispose(boolean disposing)
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
if (!this.isDisposed)
|
||||
if (!this._isDisposed)
|
||||
{
|
||||
if (this._effect != null)
|
||||
{
|
||||
this.isDisposed = true;
|
||||
if (this.parent != null)
|
||||
{
|
||||
this.parent.ChildDestroyed(this);
|
||||
}
|
||||
this._effect.ChildDestroyed(this);
|
||||
}
|
||||
|
||||
this._isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,52 +363,115 @@ public class SoundEffectInstance implements IDisposable
|
||||
/**
|
||||
* Pauses a SoundEffectInstance.
|
||||
*/
|
||||
public void Pause()
|
||||
public synchronized void Pause()
|
||||
{
|
||||
synchronized(this)
|
||||
if (this._isDisposed)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (!hasSourceId || soundState != SoundState.Playing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
controller.PauseSound(soundBuffer);
|
||||
soundState = SoundState.Paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays or resumes a SoundEffectInstance.
|
||||
*/
|
||||
public void Play()
|
||||
public synchronized void Play()
|
||||
{
|
||||
synchronized(this)
|
||||
if (this._isDisposed)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (getState() == SoundState.Playing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need to check if we're at the instance play limit
|
||||
// if we're resuming from a paused state.
|
||||
if (getState() != SoundState.Paused)
|
||||
{
|
||||
SoundEffectInstancePool.Remove(this);
|
||||
|
||||
if (!SoundEffectInstancePool.SoundsAvailable())
|
||||
{
|
||||
throw new InstancePlayLimitException();
|
||||
}
|
||||
}
|
||||
|
||||
// We need to be sure the latest master volume level is applied before playback.
|
||||
if (hasSourceId)
|
||||
{
|
||||
AL10.alSourcef(sourceId, AL10.AL_GAIN, _volume * SoundEffect.getMasterVolume());
|
||||
}
|
||||
|
||||
if (hasSourceId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isSourceAvailable = controller.ReserveSource(soundBuffer);
|
||||
|
||||
if (!isSourceAvailable)
|
||||
{
|
||||
throw new InstancePlayLimitException();
|
||||
}
|
||||
|
||||
int bufferId = soundBuffer.getOpenALDataBuffer();
|
||||
AL10.alSourcei(soundBuffer.getSourceId(), AL10.AL_BUFFER, bufferId);
|
||||
|
||||
// Send the position, gain, looping, pitch, and distance model to the OpenAL driver.
|
||||
if (!hasSourceId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Distance Model
|
||||
//AL10.alDistanceModel(ALDistanceModel.InverseDistanceClamped);
|
||||
AL10.alDistanceModel(AL11.AL_LINEAR_DISTANCE_CLAMPED); // TODO: verify
|
||||
// Pan
|
||||
AL10.alSource3f(sourceId, AL10.AL_POSITION, _pan, 0, 0.1f);
|
||||
// Volume
|
||||
AL10.alSourcef(sourceId, AL10.AL_GAIN, _alVolume);
|
||||
// Looping
|
||||
AL10.alSourcei(sourceId, AL10.AL_LOOPING, _looped ? 1 : 0);
|
||||
// Pitch
|
||||
AL10.alSourcef(sourceId, AL10.AL_PITCH, XnaPitchToAlPitch(_pitch));
|
||||
|
||||
controller.PlaySound(soundBuffer);
|
||||
//Console.WriteLine ("playing: " + sourceId + " : " + soundEffect.Name);
|
||||
soundState = SoundState.Playing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes playback for a SoundEffectInstance.
|
||||
*/
|
||||
public void Resume()
|
||||
public synchronized void Resume()
|
||||
{
|
||||
synchronized(this)
|
||||
if (this._isDisposed)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
if (!hasSourceId)
|
||||
{
|
||||
Play();
|
||||
return;
|
||||
}
|
||||
|
||||
if (soundState == SoundState.Paused)
|
||||
{
|
||||
controller.ResumeSound(soundBuffer);
|
||||
}
|
||||
|
||||
soundState = SoundState.Playing;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -311,17 +488,39 @@ public class SoundEffectInstance implements IDisposable
|
||||
* @param immediate
|
||||
* Whether to stop playing immediately, or to break out of the loop region and play the release. Specify true to stop playing immediately, or false to break out of the loop region and play the release phase (the remainder of the sound).
|
||||
*/
|
||||
public void Stop(boolean immediate)
|
||||
public synchronized void Stop(boolean immediate)
|
||||
{
|
||||
synchronized(this)
|
||||
if (this._isDisposed)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
throw new ObjectDisposedException(super.getClass().toString(), "FrameworkResources.ObjectDisposedException");
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
throw new NotImplementedException();
|
||||
if (hasSourceId)
|
||||
{
|
||||
//Console.WriteLine ("stop " + sourceId + " : " + soundEffect.Name);
|
||||
controller.StopSound(soundBuffer);
|
||||
}
|
||||
|
||||
soundState = SoundState.Stopped;
|
||||
}
|
||||
|
||||
private class SoundBufferRecycled implements EventHandler<EventArgs>
|
||||
{
|
||||
public void Invoke(Object sender, EventArgs e)
|
||||
{
|
||||
sourceId = 0;
|
||||
hasSourceId = false;
|
||||
soundState = SoundState.Stopped;
|
||||
//Console.WriteLine ("recycled: " + soundEffect.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private class SoundBufferReserved implements EventHandler<EventArgs>
|
||||
{
|
||||
public void Invoke(Object sender, EventArgs e)
|
||||
{
|
||||
sourceId = soundBuffer.getSourceId();
|
||||
hasSourceId = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,141 @@
|
||||
package Microsoft.Xna.Framework.Audio;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author The MonoGame Team
|
||||
* @author Halofreak1990
|
||||
*/
|
||||
final class SoundEffectInstancePool
|
||||
{
|
||||
private static final int MAX_PLAYING_INSTANCES = Integer.MAX_VALUE;
|
||||
|
||||
private static final List<SoundEffectInstance> _playingInstances;
|
||||
private static final List<SoundEffectInstance> _pooledInstances;
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the platform has capacity for more sounds to be played at this time.
|
||||
*
|
||||
* @return
|
||||
* true if more sounds can be played; otherwise, false.
|
||||
*/
|
||||
static boolean SoundsAvailable()
|
||||
{
|
||||
return _playingInstances.size() < MAX_PLAYING_INSTANCES;
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
// Reduce garbage generation by allocating enough capacity for
|
||||
// the maximum playing instances or at least some reasonable value.
|
||||
int maxInstances = MAX_PLAYING_INSTANCES < 1024 ? MAX_PLAYING_INSTANCES : 1024;
|
||||
_playingInstances = new ArrayList<SoundEffectInstance>(maxInstances);
|
||||
_pooledInstances = new ArrayList<SoundEffectInstance>(maxInstances);
|
||||
}
|
||||
|
||||
private SoundEffectInstancePool()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the specified instance to the pool if it is a pooled instance and removes it from the
|
||||
* list of playing instances.
|
||||
*
|
||||
* @param inst
|
||||
* The SoundEffectInstance
|
||||
*/
|
||||
static void Add(SoundEffectInstance inst)
|
||||
{
|
||||
if (inst._isPooled)
|
||||
{
|
||||
_pooledInstances.add(inst);
|
||||
inst._effect = null;
|
||||
}
|
||||
|
||||
_playingInstances.remove(inst);
|
||||
}
|
||||
|
||||
static SoundEffectInstance GetInstance()
|
||||
{
|
||||
SoundEffectInstance inst = null;
|
||||
int count = _pooledInstances.size();
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
// Grab the item at the end of the list so the remove doesn't copy all
|
||||
// the list items down one slot.
|
||||
inst = _pooledInstances.get(count - 1);
|
||||
_pooledInstances.remove(count - 1);
|
||||
|
||||
// Reset used instance to the "default" state.
|
||||
inst._isPooled = true;
|
||||
inst.setVolume(1.0f);
|
||||
inst.setPan(0.0f);
|
||||
inst.setPitch(0.0f);
|
||||
inst.IsLooped(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
inst = new SoundEffectInstance();
|
||||
inst._isPooled = true;
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the SoundEffectInstance to the list of playing instances.
|
||||
*
|
||||
* @param inst
|
||||
* The SoundEffectInstance to add to the playing list.
|
||||
*/
|
||||
static void Remove(SoundEffectInstance inst)
|
||||
{
|
||||
_playingInstances.add(inst);
|
||||
}
|
||||
|
||||
static void Update()
|
||||
{
|
||||
OpenALSoundController.GetInstance().Update();
|
||||
|
||||
SoundEffectInstance inst = null;
|
||||
|
||||
// Cleanup instances which have finished playing.
|
||||
for (int x = 0; x < _playingInstances.size();)
|
||||
{
|
||||
inst = _playingInstances.get(x);
|
||||
|
||||
if (inst.getState() == SoundState.Stopped || inst.IsDisposed() || inst._effect == null)
|
||||
{
|
||||
Add(inst);
|
||||
continue;
|
||||
}
|
||||
else if (inst._effect.IsDisposed())
|
||||
{
|
||||
Add(inst);
|
||||
|
||||
// Instances created through SoundEffect.CreateInstance need to be disposed when
|
||||
// their owner SoundEffect is disposed.
|
||||
if (!inst._isPooled)
|
||||
{
|
||||
inst.Dispose();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateMasterVolume()
|
||||
{
|
||||
for (SoundEffectInstance inst : _playingInstances)
|
||||
{
|
||||
// Re-applying the volume to itself will update
|
||||
// the sound with the current master volume.
|
||||
inst.setVolume(inst.getVolume());
|
||||
}
|
||||
}
|
||||
}
|
40
Microsoft.Xna.Framework/src/resources/Resources.java
Normal file
40
Microsoft.Xna.Framework/src/resources/Resources.java
Normal file
@ -0,0 +1,40 @@
|
||||
package resources;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
public final class Resources
|
||||
{
|
||||
private static final String BUNDLE_NAME = "resources.resources"; //$NON-NLS-1$
|
||||
|
||||
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
|
||||
|
||||
private Resources()
|
||||
{
|
||||
}
|
||||
|
||||
public static String GetString(String key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return RESOURCE_BUNDLE.getString(key);
|
||||
}
|
||||
catch (MissingResourceException e)
|
||||
{
|
||||
return '!' + key + '!';
|
||||
}
|
||||
}
|
||||
|
||||
public static String Format(String key, Object...arguments)
|
||||
{
|
||||
try
|
||||
{
|
||||
String p = RESOURCE_BUNDLE.getString(key);
|
||||
return MessageFormat.format(p,arguments);
|
||||
}
|
||||
catch (MissingResourceException e)
|
||||
{
|
||||
return '!' + key + '!';
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
ArgumentOutOfRange_NeedNonNegNum=Non-negative number required.
|
||||
|
||||
Resources.ObjectDisposedException=This object has already been disposed.
|
Loading…
x
Reference in New Issue
Block a user