Simplify SharpWave even more. (and fix a memory leak in C client)

This commit is contained in:
UnknownShadow200 2018-08-12 17:06:24 +10:00
parent c731192f1c
commit e5b43b263c
17 changed files with 98 additions and 207 deletions

View File

@ -1,6 +1,6 @@
 
Microsoft Visual Studio Solution File, Format Version 11.00 Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2010 # Visual Studio 2008
# SharpDevelop 4.4 # SharpDevelop 4.4
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassicalSharp", "ClassicalSharp\ClassicalSharp.csproj", "{BEB1C785-5CAD-48FF-A886-876BF0A318D4}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassicalSharp", "ClassicalSharp\ClassicalSharp.csproj", "{BEB1C785-5CAD-48FF-A886-876BF0A318D4}"
EndProject EndProject

View File

@ -3,7 +3,6 @@ using System;
using System.Threading; using System.Threading;
using ClassicalSharp.Events; using ClassicalSharp.Events;
using SharpWave; using SharpWave;
using SharpWave.Codecs;
namespace ClassicalSharp.Audio { namespace ClassicalSharp.Audio {

View File

@ -3,7 +3,6 @@ using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using SharpWave; using SharpWave;
using SharpWave.Codecs.Vorbis;
namespace ClassicalSharp.Audio { namespace ClassicalSharp.Audio {
@ -79,10 +78,9 @@ namespace ClassicalSharp.Audio {
string path = Path.Combine("audio", file); string path = Path.Combine("audio", file);
using (Stream fs = Platform.FileOpen(path)) { using (Stream fs = Platform.FileOpen(path)) {
OggContainer container = new OggContainer(fs);
try { try {
musicOut.SetVolume(game.MusicVolume / 100.0f); musicOut.SetVolume(game.MusicVolume / 100.0f);
musicOut.PlayStreaming(container); musicOut.PlayStreaming(fs);
} catch (InvalidOperationException ex) { } catch (InvalidOperationException ex) {
HandleMusicError(ex); HandleMusicError(ex);
try { musicOut.Dispose(); } catch { } try { musicOut.Dispose(); } catch { }

View File

@ -1,10 +1,9 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT // ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System; using System;
using System.IO; using System.IO;
using ClassicalSharp; using ClassicalSharp;
using ClassicalSharp.Network; using ClassicalSharp.Network;
using SharpWave; using SharpWave;
using SharpWave.Codecs.Vorbis;
namespace Launcher.Patcher { namespace Launcher.Patcher {
@ -60,25 +59,26 @@ namespace Launcher.Patcher {
string path = Path.Combine("audio", prefix + name + ".wav"); string path = Path.Combine("audio", prefix + name + ".wav");
using (Stream dst = Platform.FileCreate(path)) using (Stream dst = Platform.FileCreate(path))
using (MemoryStream src = new MemoryStream(rawData)) using (MemoryStream src = new MemoryStream(rawData))
{ {
dst.SetLength(44); dst.SetLength(44);
RawOut output = new RawOut((FileStream)dst, true); VorbisCodec codec = new VorbisCodec();
output.Create(1); AudioFormat format = codec.ReadHeader(src);
OggContainer container = new OggContainer(src);
output.PlayStreaming(container); foreach (AudioChunk chunk in codec.StreamData(src)) {
dst.Write(chunk.Data, 0, chunk.Length);
}
dst.Position = 0; dst.Position = 0;
BinaryWriter w = new BinaryWriter(dst); BinaryWriter w = new BinaryWriter(dst);
WriteWaveHeader(w, dst, output); WriteWaveHeader(w, dst, format);
} }
} }
void WriteWaveHeader(BinaryWriter w, Stream stream, RawOut data) { void WriteWaveHeader(BinaryWriter w, Stream stream, AudioFormat format) {
WriteFourCC(w, "RIFF"); WriteFourCC(w, "RIFF");
w.Write((int)(stream.Length - 8)); w.Write((int)(stream.Length - 8));
WriteFourCC(w, "WAVE"); WriteFourCC(w, "WAVE");
AudioFormat format = data.Format;
WriteFourCC(w, "fmt "); WriteFourCC(w, "fmt ");
w.Write(16); w.Write(16);

View File

@ -44,9 +44,9 @@ namespace OpenTK.Audio.OpenAL {
public static extern void alGenBuffers(int n, uint* bids); public static extern void alGenBuffers(int n, uint* bids);
[DllImport(lib, CallingConvention = style)] [DllImport(lib, CallingConvention = style)]
public static extern void alDeleteBuffers(int n, uint* bids); public static extern void alDeleteBuffers(int n, uint* bids);
[DllImport(lib, CallingConvention = style)] [DllImport(lib, CallingConvention = style)]
public static extern void alBufferData(uint bid, ALFormat format, IntPtr buffer, int size, int freq); public static extern void alBufferData(uint bid, ALFormat format, IntPtr buffer, int size, int freq);
[DllImport(lib, CallingConvention = style)] [DllImport(lib, CallingConvention = style)]
public static extern IntPtr alcCreateContext(IntPtr device, int* attrlist); public static extern IntPtr alcCreateContext(IntPtr device, int* attrlist);
[DllImport(lib, CallingConvention = style)] [DllImport(lib, CallingConvention = style)]
@ -79,7 +79,6 @@ namespace OpenTK.Audio.OpenAL {
public enum ALError { public enum ALError {
NoError = 0, NoError = 0,
InvalidName = 0xA001, InvalidName = 0xA001,
IllegalEnum = 0xA002,
InvalidEnum = 0xA002, InvalidEnum = 0xA002,
InvalidValue = 0xA003, InvalidValue = 0xA003,
InvalidOperation = 0xA004, InvalidOperation = 0xA004,

View File

@ -2,8 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using SharpWave.Codecs;
using SharpWave.Containers;
namespace SharpWave { namespace SharpWave {
@ -15,6 +13,11 @@ namespace SharpWave {
} }
} }
public sealed class AudioChunk {
public byte[] Data;
public int Length;
}
public delegate void Action(); public delegate void Action();
public abstract class IAudioOutput : IDisposable { public abstract class IAudioOutput : IDisposable {
@ -30,14 +33,12 @@ namespace SharpWave {
public int NumBuffers; public int NumBuffers;
public bool pendingStop; public bool pendingStop;
public void PlayStreaming(IMediaContainer container) { public void PlayStreaming(Stream src) {
container.ReadMetadata(); VorbisCodec codec = new VorbisCodec();
ICodec codec = container.GetAudioCodec(); AudioFormat format = codec.ReadHeader(src);
AudioFormat format = codec.ReadHeader(container);
SetFormat(format); SetFormat(format);
IEnumerator<AudioChunk> chunks = IEnumerator<AudioChunk> chunks = codec.StreamData(src).GetEnumerator();
codec.StreamData(container).GetEnumerator();
bool reachedEnd = false; bool reachedEnd = false;
for (;;) { for (;;) {
@ -59,36 +60,4 @@ namespace SharpWave {
} }
} }
} }
/// <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

@ -1,7 +1,6 @@
using System; using System;
using OpenTK.Audio; using OpenTK.Audio;
using OpenTK.Audio.OpenAL; using OpenTK.Audio.OpenAL;
using SharpWave.Codecs;
namespace SharpWave { namespace SharpWave {

View File

@ -28,8 +28,7 @@ namespace SharpWave {
internal static string GetErrorDescription(uint error) { internal static string GetErrorDescription(uint error) {
StringBuilder message = new StringBuilder(1024); StringBuilder message = new StringBuilder(1024);
uint result = waveOutGetErrorText(error, message, message.Capacity); uint result = waveOutGetErrorText(error, message, message.Capacity);
if(result == 0) if (result == 0) return message.ToString();
return message.ToString();
return "waveOutGetErrorText failed."; return "waveOutGetErrorText failed.";
} }
} }
@ -61,8 +60,5 @@ namespace SharpWave {
public enum WaveHeaderFlags : uint { public enum WaveHeaderFlags : uint {
Done = 0x01, Done = 0x01,
Prepared = 0x02, Prepared = 0x02,
BeginLoop = 0x04,
EndLoop = 0x08,
InQueue = 0x10,
} }
} }

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using SharpWave.Codecs;
namespace SharpWave { namespace SharpWave {

View File

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace SharpWave.Codecs {
public interface ICodec {
AudioFormat ReadHeader(Stream source);
IEnumerable<AudioChunk> StreamData(Stream source);
string Name { get; }
}
public sealed class AudioChunk {
public byte[] Data;
public int Length;
}
}

View File

@ -1,48 +0,0 @@
using System;
using System.IO;
using SharpWave.Codecs;
namespace SharpWave.Containers {
public abstract class IMediaContainer : Stream {
protected Stream stream;
public IMediaContainer(Stream source) { stream = source; }
public abstract void ReadMetadata();
public abstract ICodec GetAudioCodec();
#region Stream implementation
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return false; } }
public override void Flush() { stream.Flush(); }
public override long Length { get { return stream.Length; } }
public override long Position {
get { return stream.Position; }
set { stream.Position = value; }
}
public override long Seek( long offset, SeekOrigin origin ) {
return stream.Seek( offset, origin );
}
public override void SetLength( long value ) {
throw new NotImplementedException( "SharpWave is only a decoder" );
}
public override int Read(byte[] buffer, int offset, int count) {
return stream.Read(buffer, offset, count);
}
public override int ReadByte() { return stream.ReadByte(); }
public override void Write( byte[] buffer, int offset, int count ) {
throw new NotImplementedException( "SharpWave is only a decoder" );
}
#endregion
}
}

View File

@ -61,21 +61,17 @@
<Compile Include="csvorbis\Ogg\SyncState.cs" /> <Compile Include="csvorbis\Ogg\SyncState.cs" />
<Compile Include="csvorbis\StaticCodeBook.cs" /> <Compile Include="csvorbis\StaticCodeBook.cs" />
<Compile Include="csvorbis\VUtils.cs" /> <Compile Include="csvorbis\VUtils.cs" />
<Compile Include="ICodec.cs" />
<Compile Include="IMediaContainer.cs" />
<Compile Include="Backends\AL.cs" /> <Compile Include="Backends\AL.cs" />
<Compile Include="Backends\WinMM.cs" /> <Compile Include="Backends\WinMM.cs" />
<Compile Include="Backends\OpenALOut.cs" /> <Compile Include="Backends\OpenALOut.cs" />
<Compile Include="Backends\WinMmOut.cs" /> <Compile Include="Backends\WinMmOut.cs" />
<Compile Include="Utils.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utils\MemUtils.cs" />
<Compile Include="Backends\IAudioOutput.cs" /> <Compile Include="Backends\IAudioOutput.cs" />
<Compile Include="VolumeMixer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="csvorbis\Ogg" /> <Folder Include="csvorbis\Ogg" />
<Folder Include="csvorbis" /> <Folder Include="csvorbis" />
<Folder Include="Utils" />
<Folder Include="Backends" /> <Folder Include="Backends" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -2,6 +2,37 @@
namespace SharpWave { namespace SharpWave {
public static class MemUtils {
static MemUtils() {
use64Bit = IntPtr.Size == 8;
}
static bool use64Bit;
public static unsafe void memcpy( IntPtr srcPtr, IntPtr dstPtr, int bytes ) {
byte* srcByte, dstByte;
if( use64Bit ) {
ulong* srcLong = (ulong*)srcPtr, dstLong = (ulong*)dstPtr;
while( bytes >= 8 ) {
*dstLong++ = *srcLong++;
bytes -= 8;
}
srcByte = (byte*)srcLong; dstByte = (byte*)dstLong;
} else {
uint* srcInt = (uint*)srcPtr, dstInt = (uint*)dstPtr;
while( bytes >= 4 ) {
*dstInt++ = *srcInt++;
bytes -= 4;
}
srcByte = (byte*)srcInt; dstByte = (byte*)dstInt;
}
for( int i = 0; i < bytes; i++ ) {
*dstByte++ = *srcByte++;
}
}
}
public unsafe static class VolumeMixer { public unsafe static class VolumeMixer {
public static void Mix16( short* samples, int numSamples, int volumePercent ) { public static void Mix16( short* samples, int numSamples, int volumePercent ) {
@ -36,4 +67,4 @@ namespace SharpWave {
} }
} }
} }
} }

View File

@ -1,35 +0,0 @@
using System;
namespace SharpWave {
public static class MemUtils {
static MemUtils() {
use64Bit = IntPtr.Size == 8;
}
static bool use64Bit;
public static unsafe void memcpy( IntPtr srcPtr, IntPtr dstPtr, int bytes ) {
byte* srcByte, dstByte;
if( use64Bit ) {
ulong* srcLong = (ulong*)srcPtr, dstLong = (ulong*)dstPtr;
while( bytes >= 8 ) {
*dstLong++ = *srcLong++;
bytes -= 8;
}
srcByte = (byte*)srcLong; dstByte = (byte*)dstLong;
} else {
uint* srcInt = (uint*)srcPtr, dstInt = (uint*)dstPtr;
while( bytes >= 4 ) {
*dstInt++ = *srcInt++;
bytes -= 4;
}
srcByte = (byte*)srcInt; dstByte = (byte*)dstInt;
}
for( int i = 0; i < bytes; i++ ) {
*dstByte++ = *srcByte++;
}
}
}
}

View File

@ -3,27 +3,10 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using csogg; using csogg;
using csvorbis; using csvorbis;
using SharpWave.Containers;
namespace SharpWave.Codecs.Vorbis { namespace SharpWave {
public sealed class OggContainer : IMediaContainer { public sealed class VorbisCodec {
public OggContainer( Stream source ) : base( source ) {
}
public override void ReadMetadata() {
// this would be a good place to read vorbis headers
}
public override ICodec GetAudioCodec() {
return new VorbisCodec();
}
}
public sealed class VorbisCodec : ICodec {
public string Name { get { return "Xiph.org Vorbis"; } }
public VorbisCodec() { public VorbisCodec() {
chunk = new AudioChunk(); chunk = new AudioChunk();
} }

View File

@ -147,6 +147,13 @@ struct Codebook {
UInt16* Multiplicands; UInt16* Multiplicands;
}; };
static void Codebook_Free(struct Codebook* c) {
Mem_Free(&c->Codewords);
Mem_Free(&c->CodewordLens);
Mem_Free(&c->Values);
Mem_Free(&c->Multiplicands);
}
static UInt32 Codebook_Pow(UInt32 base, UInt32 exp) { static UInt32 Codebook_Pow(UInt32 base, UInt32 exp) {
UInt32 result = 1; /* exponentiation by squaring */ UInt32 result = 1; /* exponentiation by squaring */
while (exp) { while (exp) {
@ -267,6 +274,7 @@ static ReturnCode Codebook_DecodeSetup(struct VorbisState* ctx, struct Codebook*
Mem_Free(&codewordLens); Mem_Free(&codewordLens);
c->LookupType = Vorbis_ReadBits(ctx, 4); c->LookupType = Vorbis_ReadBits(ctx, 4);
c->Multiplicands = NULL;
if (c->LookupType == 0) return 0; if (c->LookupType == 0) return 0;
if (c->LookupType > 2) return VORBIS_ERR_CODEBOOK_LOOKUP; if (c->LookupType > 2) return VORBIS_ERR_CODEBOOK_LOOKUP;
@ -704,7 +712,7 @@ static void Residue_DecodeFrame(struct VorbisState* ctx, struct Residue* r, Int3
bool decodeAny = false; bool decodeAny = false;
Int32 i, j; Int32 i, j;
/* type 2 decodes all channel vectors 2, if at least 1 channel to decode */ /* type 2 decodes all channel vectors, if at least 1 channel to decode */
for (i = 0; i < ch; i++) { for (i = 0; i < ch; i++) {
if (!doNotDecode[i]) decodeAny = true; if (!doNotDecode[i]) decodeAny = true;
} }
@ -976,17 +984,25 @@ static void Vorbis_CalcWindow(struct VorbisState* ctx, UInt32* offset, bool long
} }
void Vorbis_Free(struct VorbisState* ctx) { void Vorbis_Free(struct VorbisState* ctx) {
Int32 i;
for (i = 0; i < ctx->NumCodebooks; i++) {
Codebook_Free(&ctx->Codebooks[i]);
}
Mem_Free(&ctx->Codebooks); Mem_Free(&ctx->Codebooks);
Mem_Free(&ctx->Floors); Mem_Free(&ctx->Floors);
Mem_Free(&ctx->Residues); Mem_Free(&ctx->Residues);
Mem_Free(&ctx->Mappings); Mem_Free(&ctx->Mappings);
Mem_Free(&ctx->Modes); Mem_Free(&ctx->Modes);
Mem_Free(&ctx->WindowShort); Mem_Free(&ctx->WindowShort);
/* TODO: free memory in codebooks, other bits, etc*/
Mem_Free(&ctx->Values[0]);
Mem_Free(&ctx->Values[1]);
/* TODO: free other temp memory, etc*/
} }
static bool Vorbis_ValidBlockSize(UInt32 blockSize) { static bool Vorbis_ValidBlockSize(UInt32 size) {
return blockSize >= 64 && blockSize <= 8192 && Math_IsPowOf2(blockSize); return size >= 64 && size <= VORBIS_MAX_BLOCK_SIZE && Math_IsPowOf2(size);
} }
static ReturnCode Vorbis_DecodeHeader(struct VorbisState* ctx, UInt8 type) { static ReturnCode Vorbis_DecodeHeader(struct VorbisState* ctx, UInt8 type) {
@ -1045,6 +1061,7 @@ static ReturnCode Vorbis_DecodeSetup(struct VorbisState* ctx) {
result = Codebook_DecodeSetup(ctx, &ctx->Codebooks[i]); result = Codebook_DecodeSetup(ctx, &ctx->Codebooks[i]);
if (result) return result; if (result) return result;
} }
ctx->NumCodebooks = count;
count = Vorbis_ReadBits(ctx, 6); count++; count = Vorbis_ReadBits(ctx, 6); count++;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
@ -1113,8 +1130,8 @@ ReturnCode Vorbis_DecodeHeaders(struct VorbisState* ctx) {
if (result) return result; if (result) return result;
/* window calculations can be pre-computed here */ /* window calculations can be pre-computed here */
UInt32 size = ctx->BlockSizes[0] + ctx->BlockSizes[1] * 4, offset = 0; UInt32 count = ctx->BlockSizes[0] + ctx->BlockSizes[1] * 4, offset = 0;
ctx->WindowShort = Mem_Alloc(size, sizeof(Real32), "Vorbis windows"); ctx->WindowShort = Mem_Alloc(count, sizeof(Real32), "Vorbis windows");
Vorbis_CalcWindow(ctx, &offset, false, false, false); Vorbis_CalcWindow(ctx, &offset, false, false, false);
ctx->WindowLong[0][0] = ctx->WindowShort + offset; ctx->WindowLong[0][0] = ctx->WindowShort + offset;
@ -1126,6 +1143,10 @@ ReturnCode Vorbis_DecodeHeaders(struct VorbisState* ctx) {
ctx->WindowLong[1][1] = ctx->WindowShort + offset; ctx->WindowLong[1][1] = ctx->WindowShort + offset;
Vorbis_CalcWindow(ctx, &offset, true, true, true); Vorbis_CalcWindow(ctx, &offset, true, true, true);
count = ctx->Channels * ctx->BlockSizes[1];
ctx->Values[0] = Mem_AllocCleared(count, sizeof(Real32), "Vorbis values");
ctx->Values[1] = Mem_AllocCleared(count, sizeof(Real32), "Vorbis values");
imdct_init(&ctx->imdct[0], ctx->BlockSizes[0]); imdct_init(&ctx->imdct[0], ctx->BlockSizes[0]);
imdct_init(&ctx->imdct[1], ctx->BlockSizes[1]); imdct_init(&ctx->imdct[1], ctx->BlockSizes[1]);
return 0; return 0;
@ -1155,9 +1176,13 @@ ReturnCode Vorbis_DecodeFrame(struct VorbisState* ctx) {
next_window = Vorbis_ReadBits(ctx, 1); next_window = Vorbis_ReadBits(ctx, 1);
} }
ctx->Values = Mem_AllocCleared(ctx->Channels * ctx->CurBlockSize, sizeof(Real32), "audio values"); /* swap prev and cur outputs around */
Real32* tmp = ctx->Values[1]; ctx->Values[1] = ctx->Values[0]; ctx->Values[0] = tmp;
Mem_Set(ctx->Values[0], 0, ctx->Channels * ctx->CurBlockSize);
for (i = 0; i < ctx->Channels; i++) { for (i = 0; i < ctx->Channels; i++) {
ctx->CurOutput[i] = ctx->Values + i * ctx->CurBlockSize; ctx->CurOutput[i] = ctx->Values[0] + i * ctx->CurBlockSize;
ctx->PrevOutput[i] = ctx->Values[1] + i * ctx->PrevBlockSize;
} }
/* decode floor */ /* decode floor */
@ -1292,9 +1317,6 @@ Int32 Vorbis_OutputFrame(struct VorbisState* ctx, Int16* data) {
finish: finish:
ctx->PrevBlockSize = ctx->CurBlockSize; ctx->PrevBlockSize = ctx->CurBlockSize;
for (i = 0; i < VORBIS_MAX_CHANS; i++) {
ctx->PrevOutput[i] = ctx->CurOutput[i];
}
return size; return size;
} }

View File

@ -27,9 +27,9 @@ struct VorbisState {
struct Stream* Source; /* Source for filling Input buffer */ struct Stream* Source; /* Source for filling Input buffer */
UInt8 Channels, ModeNumBits; UInt8 Channels, ModeNumBits;
UInt16 CurBlockSize, PrevBlockSize, DataSize; UInt16 CurBlockSize, PrevBlockSize, DataSize, NumCodebooks;
Int32 SampleRate; Int32 BlockSizes[2]; Int32 SampleRate; Int32 BlockSizes[2];
Real32* Values; Real32* Values[2]; /* swapped each frame */
Real32* PrevOutput[VORBIS_MAX_CHANS]; Real32* PrevOutput[VORBIS_MAX_CHANS];
Real32* CurOutput[VORBIS_MAX_CHANS]; Real32* CurOutput[VORBIS_MAX_CHANS];