restructure audio API to support theora audio stream
parent
b1b8c821ca
commit
4d9d3e6422
|
@ -1 +1 @@
|
||||||
Subproject commit 3ed1726b1e294799e85c3b597b114fb3b21cba72
|
Subproject commit dd8c7fa69e678b6182cdaa71458ad08dd31c65da
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using MoonWorks.Math.Float;
|
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
|
@ -8,7 +7,6 @@ namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
internal IntPtr Handle;
|
internal IntPtr Handle;
|
||||||
internal FAudio.FAudioWaveFormatEx Format;
|
internal FAudio.FAudioWaveFormatEx Format;
|
||||||
public bool Loop { get; protected set; } = false;
|
|
||||||
|
|
||||||
protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
|
protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
|
||||||
|
|
||||||
|
@ -238,11 +236,9 @@ namespace MoonWorks.Audio
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Play(bool loop);
|
public abstract void Play();
|
||||||
public abstract void Pause();
|
public abstract void Pause();
|
||||||
public abstract void Stop(bool immediate);
|
public abstract void Stop(bool immediate);
|
||||||
public abstract void Seek(float seconds);
|
|
||||||
public abstract void Seek(uint sampleFrame);
|
|
||||||
|
|
||||||
private void InitDSPSettings(uint srcChannels)
|
private void InitDSPSettings(uint srcChannels)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,8 @@ namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public StaticSound Parent { get; }
|
public StaticSound Parent { get; }
|
||||||
|
|
||||||
|
public bool Loop { get; set; }
|
||||||
|
|
||||||
private SoundState _state = SoundState.Stopped;
|
private SoundState _state = SoundState.Stopped;
|
||||||
public override SoundState State
|
public override SoundState State
|
||||||
{
|
{
|
||||||
|
@ -38,15 +40,13 @@ namespace MoonWorks.Audio
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Play(bool loop = false)
|
public override void Play()
|
||||||
{
|
{
|
||||||
if (State == SoundState.Playing)
|
if (State == SoundState.Playing)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loop = loop;
|
|
||||||
|
|
||||||
if (Loop)
|
if (Loop)
|
||||||
{
|
{
|
||||||
Parent.Handle.LoopCount = 255;
|
Parent.Handle.LoopCount = 255;
|
||||||
|
@ -93,7 +93,7 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PerformSeek(uint sampleFrame)
|
public void Seek(uint sampleFrame)
|
||||||
{
|
{
|
||||||
if (State == SoundState.Playing)
|
if (State == SoundState.Playing)
|
||||||
{
|
{
|
||||||
|
@ -102,20 +102,6 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
|
|
||||||
Parent.Handle.PlayBegin = sampleFrame;
|
Parent.Handle.PlayBegin = sampleFrame;
|
||||||
Play();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Seek(float seconds)
|
|
||||||
{
|
|
||||||
uint sampleFrame =
|
|
||||||
(uint) (Parent.SamplesPerSecond * seconds);
|
|
||||||
|
|
||||||
PerformSeek(sampleFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Seek(uint sampleFrame)
|
|
||||||
{
|
|
||||||
PerformSeek(sampleFrame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Free()
|
public void Free()
|
||||||
|
|
|
@ -11,10 +11,11 @@ namespace MoonWorks.Audio
|
||||||
public abstract class StreamingSound : SoundInstance
|
public abstract class StreamingSound : SoundInstance
|
||||||
{
|
{
|
||||||
private readonly List<IntPtr> queuedBuffers = new List<IntPtr>();
|
private readonly List<IntPtr> queuedBuffers = new List<IntPtr>();
|
||||||
private readonly List<uint> queuedSizes = new List<uint>();
|
|
||||||
private const int MINIMUM_BUFFER_CHECK = 3;
|
private const int MINIMUM_BUFFER_CHECK = 3;
|
||||||
|
|
||||||
public int PendingBufferCount => queuedBuffers.Count;
|
private int PendingBufferCount => queuedBuffers.Count;
|
||||||
|
|
||||||
|
public abstract int BUFFER_SIZE { get; }
|
||||||
|
|
||||||
public StreamingSound(
|
public StreamingSound(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
|
@ -23,16 +24,18 @@ namespace MoonWorks.Audio
|
||||||
ushort blockAlign,
|
ushort blockAlign,
|
||||||
ushort channels,
|
ushort channels,
|
||||||
uint samplesPerSecond
|
uint samplesPerSecond
|
||||||
) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond) { }
|
) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond)
|
||||||
|
{
|
||||||
|
device.AddDynamicSoundInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
public override void Play(bool loop = false)
|
public override void Play()
|
||||||
{
|
{
|
||||||
if (State == SoundState.Playing)
|
if (State == SoundState.Playing)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loop = loop;
|
|
||||||
State = SoundState.Playing;
|
State = SoundState.Playing;
|
||||||
|
|
||||||
Update();
|
Update();
|
||||||
|
@ -60,7 +63,7 @@ namespace MoonWorks.Audio
|
||||||
State = SoundState.Stopped;
|
State = SoundState.Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Update()
|
internal unsafe void Update()
|
||||||
{
|
{
|
||||||
if (State != SoundState.Playing)
|
if (State != SoundState.Playing)
|
||||||
{
|
{
|
||||||
|
@ -74,11 +77,13 @@ namespace MoonWorks.Audio
|
||||||
);
|
);
|
||||||
|
|
||||||
while (PendingBufferCount > state.BuffersQueued)
|
while (PendingBufferCount > state.BuffersQueued)
|
||||||
|
{
|
||||||
lock (queuedBuffers)
|
lock (queuedBuffers)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal(queuedBuffers[0]);
|
NativeMemory.Free((void*) queuedBuffers[0]);
|
||||||
queuedBuffers.RemoveAt(0);
|
queuedBuffers.RemoveAt(0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QueueBuffers();
|
QueueBuffers();
|
||||||
}
|
}
|
||||||
|
@ -95,46 +100,42 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ClearBuffers()
|
protected unsafe void ClearBuffers()
|
||||||
{
|
{
|
||||||
lock (queuedBuffers)
|
lock (queuedBuffers)
|
||||||
{
|
{
|
||||||
foreach (IntPtr buf in queuedBuffers)
|
foreach (IntPtr buf in queuedBuffers)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal(buf);
|
NativeMemory.Free((void*) buf);
|
||||||
}
|
}
|
||||||
queuedBuffers.Clear();
|
queuedBuffers.Clear();
|
||||||
queuedSizes.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void AddBuffer()
|
protected unsafe void AddBuffer()
|
||||||
{
|
{
|
||||||
|
void* buffer = NativeMemory.Alloc((nuint) BUFFER_SIZE);
|
||||||
|
|
||||||
AddBuffer(
|
AddBuffer(
|
||||||
out var buffer,
|
buffer,
|
||||||
out var bufferOffset,
|
BUFFER_SIZE,
|
||||||
out var bufferLength,
|
out int filledLengthInBytes,
|
||||||
out var reachedEnd
|
out bool reachedEnd
|
||||||
);
|
);
|
||||||
|
|
||||||
var lengthInBytes = bufferLength * sizeof(float);
|
|
||||||
|
|
||||||
IntPtr next = Marshal.AllocHGlobal((int) lengthInBytes);
|
|
||||||
Marshal.Copy(buffer, (int) bufferOffset, next, (int) bufferLength);
|
|
||||||
|
|
||||||
lock (queuedBuffers)
|
lock (queuedBuffers)
|
||||||
{
|
{
|
||||||
queuedBuffers.Add(next);
|
queuedBuffers.Add((IntPtr) buffer);
|
||||||
if (State != SoundState.Stopped)
|
if (State != SoundState.Stopped)
|
||||||
{
|
{
|
||||||
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer
|
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer
|
||||||
{
|
{
|
||||||
AudioBytes = lengthInBytes,
|
AudioBytes = (uint) filledLengthInBytes,
|
||||||
pAudioData = next,
|
pAudioData = (IntPtr) buffer,
|
||||||
PlayLength = (
|
PlayLength = (
|
||||||
lengthInBytes /
|
(uint) (filledLengthInBytes /
|
||||||
Format.nChannels /
|
Format.nChannels /
|
||||||
(uint) (Format.wBitsPerSample / 8)
|
(uint) (Format.wBitsPerSample / 8))
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,35 +145,28 @@ namespace MoonWorks.Audio
|
||||||
IntPtr.Zero
|
IntPtr.Zero
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
queuedSizes.Add(lengthInBytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* We have reached the end of the file, what do we do? */
|
/* We have reached the end of the file, what do we do? */
|
||||||
if (reachedEnd)
|
if (reachedEnd)
|
||||||
{
|
{
|
||||||
if (Loop)
|
OnReachedEnd();
|
||||||
{
|
|
||||||
SeekStart();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Stop(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void AddBuffer(
|
protected virtual void OnReachedEnd()
|
||||||
out float[] buffer,
|
{
|
||||||
out uint bufferOffset, /* in floats */
|
Stop(false);
|
||||||
out uint bufferLength, /* in floats */
|
}
|
||||||
|
|
||||||
|
protected unsafe abstract void AddBuffer(
|
||||||
|
void* buffer,
|
||||||
|
int bufferLength, /* in bytes */
|
||||||
|
out int filledLength, /* in bytes */
|
||||||
out bool reachedEnd
|
out bool reachedEnd
|
||||||
);
|
);
|
||||||
|
|
||||||
protected abstract void SeekStart();
|
|
||||||
|
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
Stop(true);
|
Stop(true);
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public class StreamingSoundOgg : StreamingSound
|
public class StreamingSoundOgg : StreamingSoundSeekable
|
||||||
{
|
{
|
||||||
// FIXME: what should this value be?
|
// FIXME: what should this value be?
|
||||||
public const int BUFFER_SIZE = 1024 * 128;
|
public override int BUFFER_SIZE => 1024 * 128;
|
||||||
|
|
||||||
private IntPtr VorbisHandle;
|
private IntPtr VorbisHandle;
|
||||||
private IntPtr FileDataPtr;
|
private IntPtr FileDataPtr;
|
||||||
|
@ -17,15 +18,15 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
public override SoundState State { get; protected set; }
|
public override SoundState State { get; protected set; }
|
||||||
|
|
||||||
public static StreamingSoundOgg Load(AudioDevice device, string filePath)
|
public unsafe static StreamingSoundOgg Load(AudioDevice device, string filePath)
|
||||||
{
|
{
|
||||||
var fileData = File.ReadAllBytes(filePath);
|
var fileData = File.ReadAllBytes(filePath);
|
||||||
var fileDataPtr = Marshal.AllocHGlobal(fileData.Length);
|
var fileDataPtr = NativeMemory.Alloc((nuint) fileData.Length);
|
||||||
Marshal.Copy(fileData, 0, fileDataPtr, fileData.Length);
|
Marshal.Copy(fileData, 0, (IntPtr) fileDataPtr, fileData.Length);
|
||||||
var vorbisHandle = FAudio.stb_vorbis_open_memory(fileDataPtr, fileData.Length, out int error, IntPtr.Zero);
|
var vorbisHandle = FAudio.stb_vorbis_open_memory((IntPtr) fileDataPtr, fileData.Length, out int error, IntPtr.Zero);
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
{
|
{
|
||||||
((GCHandle) fileDataPtr).Free();
|
NativeMemory.Free(fileDataPtr);
|
||||||
Logger.LogError("Error opening OGG file!");
|
Logger.LogError("Error opening OGG file!");
|
||||||
Logger.LogError("Error: " + error);
|
Logger.LogError("Error: " + error);
|
||||||
throw new AudioLoadException("Error opening OGG file!");
|
throw new AudioLoadException("Error opening OGG file!");
|
||||||
|
@ -34,7 +35,7 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
return new StreamingSoundOgg(
|
return new StreamingSoundOgg(
|
||||||
device,
|
device,
|
||||||
fileDataPtr,
|
(IntPtr) fileDataPtr,
|
||||||
vorbisHandle,
|
vorbisHandle,
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
|
@ -42,7 +43,7 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
internal StreamingSoundOgg(
|
internal StreamingSoundOgg(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
IntPtr fileDataPtr, // MUST BE AN ALLOCHGLOBAL HANDLE!!
|
IntPtr fileDataPtr, // MUST BE A NATIVE MEMORY HANDLE!!
|
||||||
IntPtr vorbisHandle,
|
IntPtr vorbisHandle,
|
||||||
FAudio.stb_vorbis_info info
|
FAudio.stb_vorbis_info info
|
||||||
) : base(
|
) : base(
|
||||||
|
@ -58,11 +59,9 @@ namespace MoonWorks.Audio
|
||||||
VorbisHandle = vorbisHandle;
|
VorbisHandle = vorbisHandle;
|
||||||
Info = info;
|
Info = info;
|
||||||
buffer = new float[BUFFER_SIZE];
|
buffer = new float[BUFFER_SIZE];
|
||||||
|
|
||||||
device.AddDynamicSoundInstance(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PerformSeek(uint sampleFrame)
|
public override void Seek(uint sampleFrame)
|
||||||
{
|
{
|
||||||
if (State == SoundState.Playing)
|
if (State == SoundState.Playing)
|
||||||
{
|
{
|
||||||
|
@ -80,49 +79,32 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Seek(float seconds)
|
protected unsafe override void AddBuffer(
|
||||||
{
|
void* buffer,
|
||||||
uint sampleFrame = (uint) (Info.sample_rate * seconds);
|
int bufferLengthInBytes,
|
||||||
PerformSeek(sampleFrame);
|
out int filledLengthInBytes,
|
||||||
}
|
|
||||||
|
|
||||||
public override void Seek(uint sampleFrame)
|
|
||||||
{
|
|
||||||
PerformSeek(sampleFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void AddBuffer(
|
|
||||||
out float[] buffer,
|
|
||||||
out uint bufferOffset,
|
|
||||||
out uint bufferLength,
|
|
||||||
out bool reachedEnd
|
out bool reachedEnd
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
buffer = this.buffer;
|
var lengthInFloats = bufferLengthInBytes / sizeof(float);
|
||||||
|
|
||||||
/* NOTE: this function returns samples per channel, not total samples */
|
/* NOTE: this function returns samples per channel, not total samples */
|
||||||
var samples = FAudio.stb_vorbis_get_samples_float_interleaved(
|
var samples = FAudio.stb_vorbis_get_samples_float_interleaved(
|
||||||
VorbisHandle,
|
VorbisHandle,
|
||||||
Info.channels,
|
Info.channels,
|
||||||
buffer,
|
(IntPtr) buffer,
|
||||||
buffer.Length
|
lengthInFloats
|
||||||
);
|
);
|
||||||
|
|
||||||
var sampleCount = samples * Info.channels;
|
var sampleCount = samples * Info.channels;
|
||||||
bufferOffset = 0;
|
reachedEnd = sampleCount < lengthInFloats;
|
||||||
bufferLength = (uint) sampleCount;
|
filledLengthInBytes = sampleCount * sizeof(float);
|
||||||
reachedEnd = sampleCount < buffer.Length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SeekStart()
|
protected unsafe override void Destroy()
|
||||||
{
|
|
||||||
FAudio.stb_vorbis_seek_start(VorbisHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Destroy()
|
|
||||||
{
|
{
|
||||||
FAudio.stb_vorbis_close(VorbisHandle);
|
FAudio.stb_vorbis_close(VorbisHandle);
|
||||||
Marshal.FreeHGlobal(FileDataPtr);
|
NativeMemory.Free((void*) FileDataPtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
namespace MoonWorks.Audio
|
||||||
|
{
|
||||||
|
public abstract class StreamingSoundSeekable : StreamingSound
|
||||||
|
{
|
||||||
|
public bool Loop { get; set; }
|
||||||
|
|
||||||
|
protected StreamingSoundSeekable(AudioDevice device, ushort formatTag, ushort bitsPerSample, ushort blockAlign, ushort channels, uint samplesPerSecond) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Seek(uint sampleFrame);
|
||||||
|
|
||||||
|
protected override void OnReachedEnd()
|
||||||
|
{
|
||||||
|
if (Loop)
|
||||||
|
{
|
||||||
|
Seek(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Stop(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
using System;
|
||||||
|
using MoonWorks.Audio;
|
||||||
|
|
||||||
|
namespace MoonWorks.Video
|
||||||
|
{
|
||||||
|
public unsafe class StreamingSoundTheora : StreamingSound
|
||||||
|
{
|
||||||
|
public IntPtr VideoHandle;
|
||||||
|
|
||||||
|
public override SoundState State { get => throw new System.NotImplementedException(); protected set => throw new System.NotImplementedException(); }
|
||||||
|
|
||||||
|
public override int BUFFER_SIZE => 4096 * 2;
|
||||||
|
|
||||||
|
internal StreamingSoundTheora(
|
||||||
|
AudioDevice device,
|
||||||
|
IntPtr videoHandle,
|
||||||
|
int channels,
|
||||||
|
uint sampleRate
|
||||||
|
) : base(
|
||||||
|
device,
|
||||||
|
3, /* float type */
|
||||||
|
32, /* size of float */
|
||||||
|
(ushort) (4 * channels),
|
||||||
|
(ushort) channels,
|
||||||
|
sampleRate
|
||||||
|
) {
|
||||||
|
VideoHandle = videoHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override unsafe void AddBuffer(
|
||||||
|
void* buffer,
|
||||||
|
int bufferLength,
|
||||||
|
out int filledLength,
|
||||||
|
out bool reachedEnd
|
||||||
|
) {
|
||||||
|
int samples = Theorafile.tf_readaudio(
|
||||||
|
VideoHandle,
|
||||||
|
(IntPtr) buffer,
|
||||||
|
bufferLength
|
||||||
|
);
|
||||||
|
|
||||||
|
filledLength = samples * sizeof(float);
|
||||||
|
reachedEnd = Theorafile.tf_eos(VideoHandle) == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using MoonWorks.Audio;
|
||||||
using MoonWorks.Graphics;
|
using MoonWorks.Graphics;
|
||||||
|
|
||||||
namespace MoonWorks.Video
|
namespace MoonWorks.Video
|
||||||
|
@ -15,7 +16,7 @@ namespace MoonWorks.Video
|
||||||
|
|
||||||
public unsafe class Video : IDisposable
|
public unsafe class Video : IDisposable
|
||||||
{
|
{
|
||||||
private IntPtr Handle;
|
internal IntPtr Handle;
|
||||||
|
|
||||||
public bool Loop { get; private set; }
|
public bool Loop { get; private set; }
|
||||||
public float Volume { get; private set; }
|
public float Volume { get; private set; }
|
||||||
|
@ -40,15 +41,20 @@ namespace MoonWorks.Video
|
||||||
private Texture vTexture = null;
|
private Texture vTexture = null;
|
||||||
private Sampler LinearSampler;
|
private Sampler LinearSampler;
|
||||||
|
|
||||||
|
private AudioDevice AudioDevice = null;
|
||||||
|
private StreamingSoundTheora audioStream = null;
|
||||||
|
|
||||||
private Stopwatch timer;
|
private Stopwatch timer;
|
||||||
private double lastTimestamp;
|
private double lastTimestamp;
|
||||||
private double timeElapsed;
|
private double timeElapsed;
|
||||||
|
|
||||||
private bool disposed;
|
private bool disposed;
|
||||||
|
|
||||||
public Video(GraphicsDevice graphicsDevice, string filename)
|
/* TODO: is there some way for us to load the data into memory? */
|
||||||
|
public Video(GraphicsDevice graphicsDevice, AudioDevice audioDevice, string filename)
|
||||||
{
|
{
|
||||||
GraphicsDevice = graphicsDevice;
|
GraphicsDevice = graphicsDevice;
|
||||||
|
AudioDevice = audioDevice;
|
||||||
|
|
||||||
if (!System.IO.File.Exists(filename))
|
if (!System.IO.File.Exists(filename))
|
||||||
{
|
{
|
||||||
|
@ -148,10 +154,14 @@ namespace MoonWorks.Video
|
||||||
Loop = loop;
|
Loop = loop;
|
||||||
timer.Start();
|
timer.Start();
|
||||||
|
|
||||||
|
if (audioStream != null)
|
||||||
|
{
|
||||||
|
audioStream.Play();
|
||||||
|
}
|
||||||
|
|
||||||
State = VideoState.Playing;
|
State = VideoState.Playing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Pause()
|
public void Pause()
|
||||||
{
|
{
|
||||||
if (State != VideoState.Playing)
|
if (State != VideoState.Playing)
|
||||||
|
@ -161,6 +171,11 @@ namespace MoonWorks.Video
|
||||||
|
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
|
|
||||||
|
if (audioStream != null)
|
||||||
|
{
|
||||||
|
audioStream.Pause();
|
||||||
|
}
|
||||||
|
|
||||||
State = VideoState.Paused;
|
State = VideoState.Paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +193,13 @@ namespace MoonWorks.Video
|
||||||
lastTimestamp = 0;
|
lastTimestamp = 0;
|
||||||
timeElapsed = 0;
|
timeElapsed = 0;
|
||||||
|
|
||||||
|
if (audioStream != null)
|
||||||
|
{
|
||||||
|
audioStream.Stop(true);
|
||||||
|
audioStream.Dispose();
|
||||||
|
audioStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
State = VideoState.Stopped;
|
State = VideoState.Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +238,13 @@ namespace MoonWorks.Video
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
timer.Reset();
|
timer.Reset();
|
||||||
|
|
||||||
|
if (audioStream != null)
|
||||||
|
{
|
||||||
|
audioStream.Stop();
|
||||||
|
audioStream.Dispose();
|
||||||
|
audioStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
Theorafile.tf_reset(Handle);
|
Theorafile.tf_reset(Handle);
|
||||||
|
|
||||||
if (Loop)
|
if (Loop)
|
||||||
|
@ -224,7 +253,6 @@ namespace MoonWorks.Video
|
||||||
InitializeTheoraStream();
|
InitializeTheoraStream();
|
||||||
|
|
||||||
timer.Start();
|
timer.Start();
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -271,12 +299,11 @@ namespace MoonWorks.Video
|
||||||
while (Theorafile.tf_readvideo(Handle, (IntPtr) yuvData, 1) == 0);
|
while (Theorafile.tf_readvideo(Handle, (IntPtr) yuvData, 1) == 0);
|
||||||
|
|
||||||
// Grab the first bit of audio. We're trying to start the decoding ASAP.
|
// Grab the first bit of audio. We're trying to start the decoding ASAP.
|
||||||
if (Theorafile.tf_hasaudio(Handle) == 1)
|
if (AudioDevice != null && Theorafile.tf_hasaudio(Handle) == 1)
|
||||||
{
|
{
|
||||||
int channels, samplerate;
|
int channels, sampleRate;
|
||||||
Theorafile.tf_audioinfo(Handle, out channels, out samplerate);
|
Theorafile.tf_audioinfo(Handle, out channels, out sampleRate);
|
||||||
|
audioStream = new StreamingSoundTheora(AudioDevice, Handle, channels, (uint) sampleRate);
|
||||||
// TODO: audio stream
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentFrame = -1;
|
currentFrame = -1;
|
||||||
|
|
Loading…
Reference in New Issue