mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-01-24 18:13:15 -05:00
fix missing files when compiling
This commit is contained in:
parent
a95d0ba3b1
commit
ad0f6fabbe
6 changed files with 633 additions and 6 deletions
102
SharpWave/Backends/AL.cs
Normal file
102
SharpWave/Backends/AL.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
/* AlFunctions.cs
|
||||
* C header: \OpenAL 1.1 SDK\include\Al.h
|
||||
* Spec: http://www.openal.org/openal_webstf/specs/OpenAL11Specification.pdf
|
||||
* Copyright (c) 2008 Christoph Brandtner and Stefanos Apostolopoulos
|
||||
* See license.txt for license details
|
||||
* http://www.OpenTK.net */
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace OpenTK.Audio.OpenAL {
|
||||
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
public unsafe static class AL {
|
||||
const string lib = "openal32.dll";
|
||||
const CallingConvention style = CallingConvention.Cdecl;
|
||||
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alDistanceModel(ALDistanceModel param);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern ALError alGetError();
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern AlcError alcGetError(IntPtr device);
|
||||
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alGenSources(int n, uint* sids);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alDeleteSources(int n, uint* sids);
|
||||
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alSourcef(uint sid, ALSourcef param, float value);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alGetSourcei(uint sid, ALGetSourcei param, int* value);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alSourcePlay(uint sid);
|
||||
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alSourceQueueBuffers(uint sid, int numEntries, uint* bids);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alSourceUnqueueBuffers(uint sid, int numEntries, uint* bids);
|
||||
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alGenBuffers(int n, uint* bids);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alDeleteBuffers(int n, uint* bids);
|
||||
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alBufferData(uint bid, ALFormat format, IntPtr buffer, int size, int freq);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern IntPtr alcCreateContext(IntPtr device, int* attrlist);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern bool alcMakeContextCurrent(IntPtr context);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern void alcDestroyContext(IntPtr context);
|
||||
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern IntPtr alcOpenDevice(IntPtr deviceName);
|
||||
[DllImport(lib, CallingConvention = style)]
|
||||
public static extern bool alcCloseDevice(IntPtr device);
|
||||
}
|
||||
|
||||
public enum ALSourcef { Gain = 0x100A }
|
||||
public enum ALSourceState { Playing = 0x1012 }
|
||||
public enum ALDistanceModel { None = 0 }
|
||||
|
||||
public enum ALGetSourcei {
|
||||
SourceState = 0x1010,
|
||||
BuffersProcessed = 0x1016,
|
||||
}
|
||||
|
||||
public enum ALFormat {
|
||||
Mono8 = 0x1100,
|
||||
Mono16 = 0x1101,
|
||||
Stereo8 = 0x1102,
|
||||
Stereo16 = 0x1103,
|
||||
}
|
||||
|
||||
public enum ALError {
|
||||
NoError = 0,
|
||||
InvalidName = 0xA001,
|
||||
IllegalEnum = 0xA002,
|
||||
InvalidEnum = 0xA002,
|
||||
InvalidValue = 0xA003,
|
||||
InvalidOperation = 0xA004,
|
||||
OutOfMemory = 0xA005,
|
||||
}
|
||||
|
||||
public enum AlcError {
|
||||
NoError = 0,
|
||||
InvalidDevice = 0xA001,
|
||||
InvalidContext = 0xA002,
|
||||
InvalidEnum = 0xA003,
|
||||
InvalidValue = 0xA004,
|
||||
OutOfMemory = 0xA005,
|
||||
}
|
||||
|
||||
public class AudioException : Exception {
|
||||
public AudioException() : base() { }
|
||||
public AudioException(string message) : base(message) { }
|
||||
}
|
||||
}
|
94
SharpWave/Backends/IAudioOutput.cs
Normal file
94
SharpWave/Backends/IAudioOutput.cs
Normal file
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using SharpWave.Codecs;
|
||||
using SharpWave.Containers;
|
||||
|
||||
namespace SharpWave {
|
||||
|
||||
public struct AudioFormat {
|
||||
public int Channels, BitsPerSample, SampleRate;
|
||||
|
||||
public bool Equals(AudioFormat a) {
|
||||
return Channels == a.Channels && BitsPerSample == a.BitsPerSample && SampleRate == a.SampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void Action();
|
||||
|
||||
public abstract class IAudioOutput : IDisposable {
|
||||
public abstract void Create(int numBuffers);
|
||||
public abstract void SetVolume(float volume);
|
||||
public abstract void Dispose();
|
||||
|
||||
public AudioFormat Format;
|
||||
public abstract void SetFormat(AudioFormat format);
|
||||
public abstract void PlayData(int index, AudioChunk chunk);
|
||||
public abstract bool IsCompleted(int index);
|
||||
public abstract bool IsFinished();
|
||||
|
||||
public int NumBuffers;
|
||||
public bool pendingStop;
|
||||
public void PlayStreaming(IMediaContainer container) {
|
||||
container.ReadMetadata();
|
||||
ICodec codec = container.GetAudioCodec();
|
||||
AudioFormat format = codec.ReadHeader(container);
|
||||
|
||||
SetFormat(format);
|
||||
IEnumerator<AudioChunk> chunks =
|
||||
codec.StreamData(container).GetEnumerator();
|
||||
|
||||
bool noData = false;
|
||||
for (;;) {
|
||||
// Have any of the buffers finished playing
|
||||
if (noData && IsFinished()) return;
|
||||
|
||||
int next = -1;
|
||||
for (int i = 0; i < NumBuffers; i++) {
|
||||
if (IsCompleted(i)) { next = i; break; }
|
||||
}
|
||||
|
||||
if (next == -1) {
|
||||
} else if (pendingStop || !chunks.MoveNext()) {
|
||||
noData = true;
|
||||
} else {
|
||||
PlayData(next, chunks.Current);
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Outputs raw audio to the given stream in the constructor. </summary>
|
||||
public unsafe sealed partial class RawOut : IAudioOutput {
|
||||
public readonly Stream OutStream;
|
||||
public readonly bool LeaveOpen;
|
||||
public Action OnGotMetadata;
|
||||
|
||||
public RawOut(FileStream outStream, bool leaveOpen) {
|
||||
OutStream = outStream;
|
||||
LeaveOpen = leaveOpen;
|
||||
}
|
||||
|
||||
public override void Create(int numBuffers) { NumBuffers = numBuffers; }
|
||||
public override void SetVolume(float volume) { }
|
||||
|
||||
public override void SetFormat(AudioFormat format) {
|
||||
Format = format;
|
||||
if (OnGotMetadata != null) OnGotMetadata();
|
||||
}
|
||||
|
||||
public override void PlayData(int index, AudioChunk chunk) {
|
||||
OutStream.Write(chunk.Data, 0, chunk.Length);
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
if (LeaveOpen) return;
|
||||
OutStream.Close();
|
||||
}
|
||||
|
||||
public override bool IsCompleted(int index) { return true; }
|
||||
public override bool IsFinished() { return true; }
|
||||
}
|
||||
}
|
227
SharpWave/Backends/OpenALOut.cs
Normal file
227
SharpWave/Backends/OpenALOut.cs
Normal file
|
@ -0,0 +1,227 @@
|
|||
using System;
|
||||
using OpenTK.Audio;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using SharpWave.Codecs;
|
||||
|
||||
namespace SharpWave {
|
||||
|
||||
/// <summary> Outputs audio to the default sound playback device using the
|
||||
/// native OpenAL library. Cross platform. </summary>
|
||||
public unsafe sealed class OpenALOut : IAudioOutput {
|
||||
uint source = uint.MaxValue;
|
||||
uint[] bufferIDs;
|
||||
bool[] completed;
|
||||
|
||||
ALFormat dataFormat;
|
||||
float volume = 1;
|
||||
static readonly object contextLock = new object();
|
||||
|
||||
public override void SetVolume(float volume) {
|
||||
this.volume = volume;
|
||||
if (source == uint.MaxValue) return;
|
||||
|
||||
lock (contextLock) {
|
||||
MakeContextCurrent();
|
||||
AL.alSourcef(source, ALSourcef.Gain, volume);
|
||||
CheckError("SetVolume");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Create(int numBuffers) {
|
||||
bufferIDs = new uint[numBuffers];
|
||||
completed = new bool[numBuffers];
|
||||
|
||||
for (int i = 0; i < numBuffers; i++) {
|
||||
completed[i] = true;
|
||||
}
|
||||
NumBuffers = numBuffers;
|
||||
|
||||
lock (contextLock) {
|
||||
if (context == IntPtr.Zero) CreateContext();
|
||||
contextRefs++;
|
||||
|
||||
MakeContextCurrent();
|
||||
AL.alDistanceModel(ALDistanceModel.None);
|
||||
CheckError("DistanceModel");
|
||||
}
|
||||
Console.WriteLine("al context:" + context);
|
||||
}
|
||||
|
||||
public override bool IsCompleted(int index) {
|
||||
int processed = 0;
|
||||
uint buffer = 0;
|
||||
lock (contextLock) {
|
||||
MakeContextCurrent();
|
||||
|
||||
AL.alGetSourcei(source, ALGetSourcei.BuffersProcessed, &processed);
|
||||
CheckError("GetSources");
|
||||
if (processed == 0) return completed[index];
|
||||
|
||||
AL.alSourceUnqueueBuffers(source, 1, &buffer);
|
||||
CheckError("SourceUnqueueBuffers");
|
||||
}
|
||||
|
||||
for (int i = 0; i < NumBuffers; i++) {
|
||||
if (bufferIDs[i] == buffer) completed[i] = true;
|
||||
}
|
||||
return completed[index];
|
||||
}
|
||||
|
||||
public override bool IsFinished() {
|
||||
if (source == uint.MaxValue) return true;
|
||||
for (int i = 0; i < NumBuffers; i++) {
|
||||
if (!IsCompleted(i)) return false;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
lock (contextLock) {
|
||||
MakeContextCurrent();
|
||||
AL.alGetSourcei(source, ALGetSourcei.SourceState, &state);
|
||||
return state != (int)ALSourceState.Playing;
|
||||
}
|
||||
}
|
||||
|
||||
public override void PlayData(int index, AudioChunk chunk) {
|
||||
fixed (byte* data = chunk.Data) {
|
||||
uint buffer = bufferIDs[index];
|
||||
completed[index] = false;
|
||||
|
||||
lock (contextLock) {
|
||||
MakeContextCurrent();
|
||||
AL.alBufferData(buffer, dataFormat, (IntPtr)data,
|
||||
chunk.Length, Format.SampleRate);
|
||||
CheckError("BufferData");
|
||||
|
||||
AL.alSourceQueueBuffers(source, 1, &buffer);
|
||||
CheckError("QueueBuffers");
|
||||
AL.alSourcePlay(source);
|
||||
CheckError("SourcePlay");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckError(string location) {
|
||||
ALError error = AL.alGetError();
|
||||
if (error != ALError.NoError) {
|
||||
throw new InvalidOperationException("OpenAL error: " + error + " at " + location);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
DisposeSource();
|
||||
lock (contextLock) {
|
||||
contextRefs--;
|
||||
if (contextRefs == 0) DestroyContext();
|
||||
}
|
||||
}
|
||||
|
||||
void DisposeSource() {
|
||||
if (source == uint.MaxValue) return;
|
||||
uint sourceU = source;
|
||||
|
||||
fixed (uint* buffers = bufferIDs) {
|
||||
lock (contextLock) {
|
||||
MakeContextCurrent();
|
||||
AL.alDeleteSources(1, &sourceU);
|
||||
source = uint.MaxValue;
|
||||
CheckError("DeleteSources");
|
||||
|
||||
AL.alDeleteBuffers(NumBuffers, buffers);
|
||||
CheckError("DeleteBuffers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetFormat(AudioFormat format) {
|
||||
dataFormat = GetALFormat(format.Channels, format.BitsPerSample);
|
||||
// Don't need to recreate device if it's the same
|
||||
if (Format.Equals(format)) return;
|
||||
Format = format;
|
||||
|
||||
DisposeSource();
|
||||
uint sourceU = 0;
|
||||
|
||||
fixed (uint* buffers = bufferIDs) {
|
||||
lock (contextLock) {
|
||||
MakeContextCurrent();
|
||||
AL.alGenSources(1, &sourceU);
|
||||
source = sourceU;
|
||||
CheckError("GenSources");
|
||||
|
||||
AL.alGenBuffers(NumBuffers, buffers);
|
||||
CheckError("GenBuffers");
|
||||
}
|
||||
}
|
||||
|
||||
if (volume != 1) SetVolume(volume);
|
||||
}
|
||||
|
||||
static ALFormat GetALFormat(int channels, int bitsPerSample) {
|
||||
ALFormat format;
|
||||
switch (channels) {
|
||||
case 1: format = ALFormat.Mono8; break;
|
||||
case 2: format = ALFormat.Stereo8; break;
|
||||
default: throw new NotSupportedException("Unsupported number of channels: " + channels);
|
||||
}
|
||||
|
||||
if (bitsPerSample == 8) return format;
|
||||
if (bitsPerSample == 16) return (ALFormat)(format + 1);
|
||||
throw new NotSupportedException("Unsupported bits per sample: " + bitsPerSample);
|
||||
}
|
||||
|
||||
|
||||
static IntPtr device, context;
|
||||
static int contextRefs = 0;
|
||||
|
||||
static void CreateContext() {
|
||||
device = AL.alcOpenDevice(IntPtr.Zero);
|
||||
if (device == IntPtr.Zero) {
|
||||
throw new AudioException("Unable to open default OpenAL device");
|
||||
}
|
||||
CheckContextErrors();
|
||||
|
||||
context = AL.alcCreateContext(device, null);
|
||||
if (context == IntPtr.Zero) {
|
||||
AL.alcCloseDevice(device);
|
||||
throw new AudioException("Audio context could not be created");
|
||||
}
|
||||
CheckContextErrors();
|
||||
|
||||
MakeContextCurrent();
|
||||
CheckContextErrors();
|
||||
}
|
||||
|
||||
static void CheckContextErrors() {
|
||||
const string format = "Device {0} reported {1}.";
|
||||
AlcError err = AL.alcGetError(device);
|
||||
|
||||
switch (err) {
|
||||
case AlcError.OutOfMemory:
|
||||
throw new OutOfMemoryException(String.Format(format, device, err));
|
||||
case AlcError.InvalidValue:
|
||||
throw new AudioException(String.Format(format, device, err));
|
||||
case AlcError.InvalidDevice:
|
||||
throw new AudioException(String.Format(format, device, err));
|
||||
case AlcError.InvalidContext:
|
||||
throw new AudioException(String.Format(format, device, err));
|
||||
}
|
||||
}
|
||||
|
||||
static void MakeContextCurrent() {
|
||||
AL.alcMakeContextCurrent(context);
|
||||
}
|
||||
|
||||
static void DestroyContext() {
|
||||
if (device == IntPtr.Zero) return;
|
||||
AL.alcMakeContextCurrent(IntPtr.Zero);
|
||||
|
||||
if (context != IntPtr.Zero)
|
||||
AL.alcDestroyContext(context);
|
||||
if (device != IntPtr.Zero)
|
||||
AL.alcCloseDevice(device);
|
||||
|
||||
context = IntPtr.Zero;
|
||||
device = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
68
SharpWave/Backends/WinMM.cs
Normal file
68
SharpWave/Backends/WinMM.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpWave {
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
internal static class WinMM {
|
||||
const string lib = "winmm.dll";
|
||||
|
||||
[DllImport(lib, SetLastError = true)]
|
||||
internal static extern uint waveOutOpen(out IntPtr handle, IntPtr deviceID, ref WaveFormatEx format,
|
||||
IntPtr callback, UIntPtr callbackInstance, uint flags);
|
||||
[DllImport(lib, SetLastError = true)]
|
||||
internal static extern uint waveOutClose(IntPtr handle);
|
||||
[DllImport(lib, SetLastError = true)]
|
||||
internal static extern uint waveOutGetNumDevs();
|
||||
|
||||
[DllImport(lib, SetLastError = true)]
|
||||
internal static extern uint waveOutPrepareHeader(IntPtr handle, IntPtr header, int hdrSize);
|
||||
[DllImport(lib, SetLastError = true)]
|
||||
internal static extern uint waveOutUnprepareHeader(IntPtr handle, IntPtr header, int hdrSize);
|
||||
[DllImport(lib, SetLastError = true)]
|
||||
internal static extern uint waveOutWrite(IntPtr handle, IntPtr header, int hdrSize);
|
||||
|
||||
[DllImport(lib, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
internal static extern uint waveOutGetErrorText(uint error, StringBuilder buffer, int bufferLen);
|
||||
internal static string GetErrorDescription(uint error) {
|
||||
StringBuilder message = new StringBuilder(1024);
|
||||
uint result = waveOutGetErrorText(error, message, message.Capacity);
|
||||
if(result == 0)
|
||||
return message.ToString();
|
||||
return "waveOutGetErrorText failed.";
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct WaveFormatEx {
|
||||
public ushort FormatTag;
|
||||
public ushort Channels;
|
||||
public uint SampleRate;
|
||||
public int AverageBytesPerSecond;
|
||||
public ushort BlockAlign;
|
||||
public ushort BitsPerSample;
|
||||
public ushort ExtraSize;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct WaveHeader {
|
||||
public IntPtr DataBuffer;
|
||||
public int BufferLength;
|
||||
public int BytesRecorded;
|
||||
public IntPtr UserData;
|
||||
public WaveHeaderFlags Flags;
|
||||
public int Loops;
|
||||
public IntPtr Next;
|
||||
public IntPtr Reserved;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum WaveHeaderFlags : uint {
|
||||
Done = 0x01,
|
||||
Prepared = 0x02,
|
||||
BeginLoop = 0x04,
|
||||
EndLoop = 0x08,
|
||||
InQueue = 0x10,
|
||||
}
|
||||
}
|
136
SharpWave/Backends/WinMmOut.cs
Normal file
136
SharpWave/Backends/WinMmOut.cs
Normal file
|
@ -0,0 +1,136 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using SharpWave.Codecs;
|
||||
|
||||
namespace SharpWave {
|
||||
|
||||
/// <summary> Outputs audio to the default sound playback device using the
|
||||
/// native WinMm library. Windows only. </summary>
|
||||
public unsafe sealed class WinMmOut : IAudioOutput {
|
||||
IntPtr devHandle, headers;
|
||||
IntPtr[] dataHandles;
|
||||
int[] dataSizes;
|
||||
int volumePercent = 100, waveHeaderSize;
|
||||
|
||||
public override void SetVolume(float volume) { volumePercent = (int)(volume * 100); }
|
||||
|
||||
public override void Create(int numBuffers) {
|
||||
waveHeaderSize = Marshal.SizeOf(default(WaveHeader));
|
||||
headers = Marshal.AllocHGlobal(waveHeaderSize * numBuffers);
|
||||
dataHandles = new IntPtr[numBuffers];
|
||||
dataSizes = new int[numBuffers];
|
||||
|
||||
for (int i = 0; i < numBuffers; i++) {
|
||||
WaveHeader* hdr = (WaveHeader*)headers + i;
|
||||
hdr->Flags = WaveHeaderFlags.Done;
|
||||
}
|
||||
NumBuffers = numBuffers;
|
||||
}
|
||||
|
||||
public override bool IsCompleted(int index) {
|
||||
WaveHeader* hdr = (WaveHeader*)headers + index;
|
||||
if ((hdr->Flags & WaveHeaderFlags.Done) == 0) return false;
|
||||
|
||||
if ((hdr->Flags & WaveHeaderFlags.Prepared) != 0) {
|
||||
uint result = WinMM.waveOutUnprepareHeader(devHandle, (IntPtr)hdr, waveHeaderSize);
|
||||
CheckError(result, "UnprepareHeader");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const ushort pcmFormat = 1;
|
||||
public override bool IsFinished() {
|
||||
for (int i = 0; i < NumBuffers; i++) {
|
||||
if (!IsCompleted(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SetFormat(AudioFormat format) {
|
||||
// Don't need to recreate device if it's the same
|
||||
if (Format.Equals(format)) return;
|
||||
Format = format;
|
||||
|
||||
Console.WriteLine("init");
|
||||
DisposeDevice();
|
||||
WaveFormatEx fmt = default(WaveFormatEx);
|
||||
|
||||
fmt.Channels = (ushort)format.Channels;
|
||||
fmt.FormatTag = pcmFormat;
|
||||
fmt.BitsPerSample = (ushort)format.BitsPerSample;
|
||||
fmt.BlockAlign = (ushort)(fmt.Channels * fmt.BitsPerSample / 8);
|
||||
fmt.SampleRate = (uint)format.SampleRate;
|
||||
fmt.AverageBytesPerSecond = (int)fmt.SampleRate * fmt.BlockAlign;
|
||||
|
||||
uint devices = WinMM.waveOutGetNumDevs();
|
||||
if (devices == 0) throw new InvalidOperationException("No audio devices found");
|
||||
|
||||
uint result = WinMM.waveOutOpen(out devHandle, (IntPtr)(-1), ref fmt,
|
||||
IntPtr.Zero, UIntPtr.Zero, 0);
|
||||
CheckError(result, "Open");
|
||||
}
|
||||
|
||||
public override void PlayData(int index, AudioChunk chunk) {
|
||||
if (chunk.Length > dataSizes[index]) {
|
||||
IntPtr ptr = dataHandles[index];
|
||||
if (ptr != IntPtr.Zero) Marshal.FreeHGlobal(ptr);
|
||||
dataHandles[index] = Marshal.AllocHGlobal(chunk.Length);
|
||||
}
|
||||
|
||||
IntPtr handle = dataHandles[index];
|
||||
fixed (byte* data = chunk.Data) {
|
||||
MemUtils.memcpy((IntPtr)data, handle, chunk.Length);
|
||||
ApplyVolume(handle, chunk);
|
||||
}
|
||||
|
||||
WaveHeader header = default(WaveHeader);
|
||||
header.DataBuffer = handle;
|
||||
header.BufferLength = chunk.Length;
|
||||
header.Loops = 1;
|
||||
|
||||
WaveHeader* hdr = (WaveHeader*)headers + index;
|
||||
*hdr = header;
|
||||
|
||||
uint result = WinMM.waveOutPrepareHeader(devHandle, (IntPtr)hdr, waveHeaderSize);
|
||||
CheckError(result, "PrepareHeader");
|
||||
result = WinMM.waveOutWrite(devHandle, (IntPtr)hdr, waveHeaderSize);
|
||||
CheckError(result, "Write");
|
||||
}
|
||||
|
||||
void ApplyVolume(IntPtr handle, AudioChunk chunk) {
|
||||
if (volumePercent == 100) return;
|
||||
|
||||
if (Format.BitsPerSample == 16) {
|
||||
VolumeMixer.Mix16((short*)handle, chunk.Length / sizeof(short), volumePercent);
|
||||
} else if (Format.BitsPerSample == 8) {
|
||||
VolumeMixer.Mix8((byte*)handle, chunk.Length, volumePercent);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckError(uint result, string func) {
|
||||
if (result == 0) return;
|
||||
|
||||
string description = WinMM.GetErrorDescription(result);
|
||||
const string format = "{0} at {1} ({2})";
|
||||
string text = String.Format(format, result, func, description);
|
||||
throw new InvalidOperationException(text);
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
Console.WriteLine("dispose");
|
||||
DisposeDevice();
|
||||
for (int i = 0; i < dataHandles.Length; i++)
|
||||
Marshal.FreeHGlobal(dataHandles[i]);
|
||||
}
|
||||
|
||||
void DisposeDevice() {
|
||||
if (devHandle == IntPtr.Zero) return;
|
||||
|
||||
Console.WriteLine("disposing device");
|
||||
uint result = WinMM.waveOutClose(devHandle);
|
||||
CheckError(result, "Close");
|
||||
devHandle = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -63,20 +63,20 @@
|
|||
<Compile Include="csvorbis\VUtils.cs" />
|
||||
<Compile Include="ICodec.cs" />
|
||||
<Compile Include="IMediaContainer.cs" />
|
||||
<Compile Include="Output\AL.cs" />
|
||||
<Compile Include="Output\WinMM.cs" />
|
||||
<Compile Include="Output\OpenALOut.cs" />
|
||||
<Compile Include="Output\WinMmOut.cs" />
|
||||
<Compile Include="Backends\AL.cs" />
|
||||
<Compile Include="Backends\WinMM.cs" />
|
||||
<Compile Include="Backends\OpenALOut.cs" />
|
||||
<Compile Include="Backends\WinMmOut.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Utils\MemUtils.cs" />
|
||||
<Compile Include="Output\IAudioOutput.cs" />
|
||||
<Compile Include="Backends\IAudioOutput.cs" />
|
||||
<Compile Include="VolumeMixer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="csvorbis\Ogg" />
|
||||
<Folder Include="csvorbis" />
|
||||
<Folder Include="Utils" />
|
||||
<Folder Include="Output" />
|
||||
<Folder Include="Backends" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="SharpWave.dll.config">
|
||||
|
|
Loading…
Add table
Reference in a new issue