fix missing files when compiling

This commit is contained in:
UnknownShadow200 2018-08-06 02:58:00 +10:00
parent a95d0ba3b1
commit ad0f6fabbe
6 changed files with 633 additions and 6 deletions

102
SharpWave/Backends/AL.cs Normal file
View 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) { }
}
}

View 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; }
}
}

View 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;
}
}
}

View 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,
}
}

View 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;
}
}
}

View file

@ -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">