mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-16 02:56:09 -04:00
rewrite sharpwave somewhat
This commit is contained in:
parent
daaa68cea2
commit
36c425906f
@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InteropPatcher", "InteropPa
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Launcher2", "Launcher2\Launcher2.csproj", "{3E84ACC1-27B4-401B-A359-6AAE4DF6C9B5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpWave", "SharpWave\SharpWave.csproj", "{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -40,6 +42,14 @@ Global
|
||||
{3E84ACC1-27B4-401B-A359-6AAE4DF6C9B5}.Debug_DX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3E84ACC1-27B4-401B-A359-6AAE4DF6C9B5}.Release_DX|Any CPU.Build.0 = Release|Any CPU
|
||||
{3E84ACC1-27B4-401B-A359-6AAE4DF6C9B5}.Release_DX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}.Debug_DX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}.Debug_DX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}.Release_DX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}.Release_DX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -1,156 +1,153 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using System.Threading;
|
||||
using ClassicalSharp.Events;
|
||||
using SharpWave;
|
||||
using SharpWave.Codecs;
|
||||
|
||||
namespace ClassicalSharp.Audio {
|
||||
|
||||
public sealed partial class AudioPlayer {
|
||||
|
||||
Soundboard digBoard, stepBoard;
|
||||
const int maxSounds = 6;
|
||||
|
||||
public void SetSounds(int volume) {
|
||||
if (volume > 0) InitSound();
|
||||
else DisposeSound();
|
||||
}
|
||||
|
||||
void InitSound() {
|
||||
if (digBoard == null) InitSoundboards();
|
||||
monoOutputs = new IAudioOutput[maxSounds];
|
||||
stereoOutputs = new IAudioOutput[maxSounds];
|
||||
}
|
||||
|
||||
void InitSoundboards() {
|
||||
digBoard = new Soundboard();
|
||||
digBoard.Init("dig_", files);
|
||||
stepBoard = new Soundboard();
|
||||
stepBoard.Init("step_", files);
|
||||
}
|
||||
|
||||
void PlayBlockSound(object sender, BlockChangedEventArgs e) {
|
||||
if (e.Block == 0) {
|
||||
PlayDigSound(BlockInfo.DigSounds[e.OldBlock]);
|
||||
} else if (!game.ClassicMode) {
|
||||
PlayDigSound(BlockInfo.StepSounds[e.Block]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayDigSound(byte type) { PlaySound(type, digBoard); }
|
||||
|
||||
public void PlayStepSound(byte type) { PlaySound(type, stepBoard); }
|
||||
|
||||
AudioChunk chunk = new AudioChunk();
|
||||
void PlaySound(byte type, Soundboard board) {
|
||||
if (type == SoundType.None || monoOutputs == null) return;
|
||||
Sound snd = board.PickRandomSound(type);
|
||||
if (snd == null) return;
|
||||
|
||||
chunk.Channels = snd.Channels;
|
||||
chunk.BitsPerSample = snd.BitsPerSample;
|
||||
chunk.BytesOffset = 0;
|
||||
chunk.BytesUsed = snd.Data.Length;
|
||||
chunk.Data = snd.Data;
|
||||
|
||||
float volume = game.SoundsVolume / 100.0f;
|
||||
if (board == digBoard) {
|
||||
if (type == SoundType.Metal) chunk.SampleRate = (snd.SampleRate * 6) / 5;
|
||||
else chunk.SampleRate = (snd.SampleRate * 4) / 5;
|
||||
} else {
|
||||
volume *= 0.50f;
|
||||
|
||||
if (type == SoundType.Metal) chunk.SampleRate = (snd.SampleRate * 7) / 5;
|
||||
else chunk.SampleRate = snd.SampleRate;
|
||||
}
|
||||
|
||||
if (snd.Channels == 1) {
|
||||
PlayCurrentSound(monoOutputs, volume);
|
||||
} else if (snd.Channels == 2) {
|
||||
PlayCurrentSound(stereoOutputs, volume);
|
||||
}
|
||||
}
|
||||
|
||||
IAudioOutput firstSoundOut;
|
||||
void PlayCurrentSound(IAudioOutput[] outputs, float volume) {
|
||||
for (int i = 0; i < monoOutputs.Length; i++) {
|
||||
IAudioOutput output = outputs[i];
|
||||
if (output == null) output = MakeSoundOutput(outputs, i);
|
||||
if (!output.DoneRawAsync()) continue;
|
||||
|
||||
LastChunk l = output.Last;
|
||||
if (l.Channels == 0 || (l.Channels == chunk.Channels && l.BitsPerSample == chunk.BitsPerSample
|
||||
&& l.SampleRate == chunk.SampleRate)) {
|
||||
PlaySound(output, volume); return;
|
||||
}
|
||||
}
|
||||
|
||||
// This time we try to play the sound on all possible devices,
|
||||
// even if it requires the expensive case of recreating a device
|
||||
for (int i = 0; i < monoOutputs.Length; i++) {
|
||||
IAudioOutput output = outputs[i];
|
||||
if (!output.DoneRawAsync()) continue;
|
||||
|
||||
PlaySound(output, volume); return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IAudioOutput MakeSoundOutput(IAudioOutput[] outputs, int i) {
|
||||
IAudioOutput output = GetPlatformOut();
|
||||
output.Create(1, firstSoundOut);
|
||||
if (firstSoundOut == null)
|
||||
firstSoundOut = output;
|
||||
|
||||
outputs[i] = output;
|
||||
return output;
|
||||
}
|
||||
|
||||
void PlaySound(IAudioOutput output, float volume) {
|
||||
try {
|
||||
output.SetVolume(volume);
|
||||
output.PlayRawAsync(chunk);
|
||||
} catch (InvalidOperationException ex) {
|
||||
ErrorHandler.LogError("AudioPlayer.PlayCurrentSound()", ex);
|
||||
if (ex.Message == "No audio devices found")
|
||||
game.Chat.Add("&cNo audio devices found, disabling sounds.");
|
||||
else
|
||||
game.Chat.Add("&cAn error occured when trying to play sounds, disabling sounds.");
|
||||
|
||||
SetSounds(0);
|
||||
game.SoundsVolume = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DisposeSound() {
|
||||
DisposeOutputs(ref monoOutputs);
|
||||
DisposeOutputs(ref stereoOutputs);
|
||||
if (firstSoundOut != null) {
|
||||
firstSoundOut.Dispose();
|
||||
firstSoundOut = null;
|
||||
}
|
||||
}
|
||||
|
||||
void DisposeOutputs(ref IAudioOutput[] outputs) {
|
||||
if (outputs == null) return;
|
||||
bool soundPlaying = true;
|
||||
|
||||
while (soundPlaying) {
|
||||
soundPlaying = false;
|
||||
for (int i = 0; i < outputs.Length; i++) {
|
||||
if (outputs[i] == null) continue;
|
||||
soundPlaying |= !outputs[i].DoneRawAsync();
|
||||
}
|
||||
if (soundPlaying)
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < outputs.Length; i++) {
|
||||
if (outputs[i] == null || outputs[i] == firstSoundOut) continue;
|
||||
outputs[i].Dispose();
|
||||
}
|
||||
outputs = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using System.Threading;
|
||||
using ClassicalSharp.Events;
|
||||
using SharpWave;
|
||||
using SharpWave.Codecs;
|
||||
|
||||
namespace ClassicalSharp.Audio {
|
||||
|
||||
public sealed partial class AudioPlayer {
|
||||
|
||||
Soundboard digBoard, stepBoard;
|
||||
const int maxSounds = 6;
|
||||
|
||||
public void SetSounds(int volume) {
|
||||
if (volume > 0) InitSound();
|
||||
else DisposeSound();
|
||||
}
|
||||
|
||||
void InitSound() {
|
||||
if (digBoard == null) InitSoundboards();
|
||||
monoOutputs = new IAudioOutput[maxSounds];
|
||||
stereoOutputs = new IAudioOutput[maxSounds];
|
||||
}
|
||||
|
||||
void InitSoundboards() {
|
||||
digBoard = new Soundboard();
|
||||
digBoard.Init("dig_", files);
|
||||
stepBoard = new Soundboard();
|
||||
stepBoard.Init("step_", files);
|
||||
}
|
||||
|
||||
void PlayBlockSound(object sender, BlockChangedEventArgs e) {
|
||||
if (e.Block == 0) {
|
||||
PlayDigSound(BlockInfo.DigSounds[e.OldBlock]);
|
||||
} else if (!game.ClassicMode) {
|
||||
PlayDigSound(BlockInfo.StepSounds[e.Block]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayDigSound(byte type) { PlaySound(type, digBoard); }
|
||||
|
||||
public void PlayStepSound(byte type) { PlaySound(type, stepBoard); }
|
||||
|
||||
AudioFormat format;
|
||||
AudioChunk chunk = new AudioChunk();
|
||||
void PlaySound(byte type, Soundboard board) {
|
||||
if (type == SoundType.None || monoOutputs == null) return;
|
||||
Sound snd = board.PickRandomSound(type);
|
||||
if (snd == null) return;
|
||||
|
||||
format = snd.Format;
|
||||
chunk.Data = snd.Data;
|
||||
chunk.Length = snd.Data.Length;
|
||||
|
||||
float volume = game.SoundsVolume / 100.0f;
|
||||
if (board == digBoard) {
|
||||
if (type == SoundType.Metal) format.SampleRate = (format.SampleRate * 6) / 5;
|
||||
else format.SampleRate = (format.SampleRate * 4) / 5;
|
||||
} else {
|
||||
volume *= 0.50f;
|
||||
if (type == SoundType.Metal) format.SampleRate = (format.SampleRate * 7) / 5;
|
||||
}
|
||||
|
||||
if (format.Channels == 1) {
|
||||
PlayCurrentSound(monoOutputs, volume);
|
||||
} else if (format.Channels == 2) {
|
||||
PlayCurrentSound(stereoOutputs, volume);
|
||||
}
|
||||
}
|
||||
|
||||
IAudioOutput firstSoundOut;
|
||||
void PlayCurrentSound(IAudioOutput[] outputs, float volume) {
|
||||
for (int i = 0; i < monoOutputs.Length; i++) {
|
||||
IAudioOutput output = outputs[i];
|
||||
if (output == null) {
|
||||
outputs[i] = GetPlatformOut();
|
||||
output = outputs[i];
|
||||
}
|
||||
|
||||
if (!output.IsFinished()) continue;
|
||||
AudioFormat fmt = output.Format;
|
||||
if (fmt.Channels == 0 || fmt.Equals(format)) {
|
||||
PlaySound(output, volume); return;
|
||||
}
|
||||
}
|
||||
|
||||
// This time we try to play the sound on all possible devices,
|
||||
// even if it requires the expensive case of recreating a device
|
||||
for (int i = 0; i < monoOutputs.Length; i++) {
|
||||
IAudioOutput output = outputs[i];
|
||||
if (!output.IsFinished()) continue;
|
||||
|
||||
PlaySound(output, volume); return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IAudioOutput MakeSoundOutput(IAudioOutput[] outputs, int i) {
|
||||
IAudioOutput output = GetPlatformOut();
|
||||
output.Create(1);
|
||||
outputs[i] = output;
|
||||
return output;
|
||||
}
|
||||
|
||||
void PlaySound(IAudioOutput output, float volume) {
|
||||
try {
|
||||
output.SetVolume(volume);
|
||||
output.SetFormat(format);
|
||||
output.PlayData(0, chunk);
|
||||
} catch (InvalidOperationException ex) {
|
||||
ErrorHandler.LogError("AudioPlayer.PlayCurrentSound()", ex);
|
||||
if (ex.Message == "No audio devices found")
|
||||
game.Chat.Add("&cNo audio devices found, disabling sounds.");
|
||||
else
|
||||
game.Chat.Add("&cAn error occured when trying to play sounds, disabling sounds.");
|
||||
|
||||
SetSounds(0);
|
||||
game.SoundsVolume = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DisposeSound() {
|
||||
DisposeOutputs(ref monoOutputs);
|
||||
DisposeOutputs(ref stereoOutputs);
|
||||
if (firstSoundOut != null) {
|
||||
firstSoundOut.Dispose();
|
||||
firstSoundOut = null;
|
||||
}
|
||||
}
|
||||
|
||||
void DisposeOutputs(ref IAudioOutput[] outputs) {
|
||||
if (outputs == null) return;
|
||||
bool soundPlaying = true;
|
||||
|
||||
while (soundPlaying) {
|
||||
soundPlaying = false;
|
||||
for (int i = 0; i < outputs.Length; i++) {
|
||||
if (outputs[i] == null) continue;
|
||||
soundPlaying |= !outputs[i].IsFinished();
|
||||
}
|
||||
if (soundPlaying)
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < outputs.Length; i++) {
|
||||
if (outputs[i] == null) continue;
|
||||
outputs[i].Dispose();
|
||||
}
|
||||
outputs = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ namespace ClassicalSharp.Audio {
|
||||
|
||||
void DisposeOf(ref IAudioOutput output, ref Thread thread) {
|
||||
if (output == null) return;
|
||||
output.Stop();
|
||||
output.pendingStop = true;
|
||||
thread.Join();
|
||||
|
||||
output.Dispose();
|
||||
|
@ -2,11 +2,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpWave;
|
||||
|
||||
namespace ClassicalSharp.Audio {
|
||||
|
||||
public class Sound {
|
||||
public int SampleRate, BitsPerSample, Channels;
|
||||
public AudioFormat Format;
|
||||
public byte[] Data;
|
||||
}
|
||||
|
||||
@ -97,12 +98,12 @@ namespace ClassicalSharp.Audio {
|
||||
static void HandleFormat(BinaryReader r, ref int size, Sound snd) {
|
||||
if (r.ReadUInt16() != 1)
|
||||
throw new InvalidDataException("Only PCM audio is supported.");
|
||||
size -= 2;
|
||||
|
||||
snd.Channels = r.ReadUInt16(); size -= 2;
|
||||
snd.SampleRate = r.ReadInt32(); size -= 4;
|
||||
r.ReadInt32(); r.ReadUInt16(); size -= 6;
|
||||
snd.BitsPerSample = r.ReadUInt16(); size -= 2;
|
||||
snd.Format.Channels = r.ReadUInt16();
|
||||
snd.Format.SampleRate = r.ReadInt32();
|
||||
r.ReadInt32(); r.ReadUInt16();
|
||||
snd.Format.BitsPerSample = r.ReadUInt16();
|
||||
size -= 16;
|
||||
}
|
||||
|
||||
unsafe string GetFourCC(BinaryReader r) {
|
||||
|
@ -69,9 +69,6 @@
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
|
||||
<ItemGroup>
|
||||
<Reference Include="SharpWave">
|
||||
<HintPath>SharpWave.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
@ -450,6 +447,10 @@
|
||||
<Project>{4A4110EE-21CA-4715-AF67-0C8B7CE0642F}</Project>
|
||||
<Name>InteropPatcher</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SharpWave\SharpWave.csproj">
|
||||
<Project>{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}</Project>
|
||||
<Name>SharpWave</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Target Name="AfterBuild">
|
||||
<Exec Command="$(OutputPath)InteropPatcher.exe $(OutDir)ClassicalSharp.exe" />
|
||||
|
Binary file not shown.
@ -48,9 +48,6 @@
|
||||
<StartAction>Project</StartAction>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="SharpWave">
|
||||
<HintPath>..\ClassicalSharp\SharpWave.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Security" />
|
||||
@ -265,5 +262,11 @@
|
||||
<Folder Include="Utils" />
|
||||
<Folder Include="Patcher" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SharpWave\SharpWave.csproj">
|
||||
<Project>{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}</Project>
|
||||
<Name>SharpWave</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
|
||||
</Project>
|
@ -77,15 +77,16 @@ namespace Launcher.Patcher {
|
||||
WriteFourCC(w, "RIFF");
|
||||
w.Write((int)(stream.Length - 8));
|
||||
WriteFourCC(w, "WAVE");
|
||||
AudioFormat format = data.Format;
|
||||
|
||||
WriteFourCC(w, "fmt ");
|
||||
w.Write(16);
|
||||
w.Write((ushort)1); // audio format, PCM
|
||||
w.Write((ushort)data.Last.Channels);
|
||||
w.Write(data.Last.SampleRate);
|
||||
w.Write((data.Last.SampleRate * data.Last.Channels * data.Last.BitsPerSample) / 8); // byte rate
|
||||
w.Write((ushort)((data.Last.Channels * data.Last.BitsPerSample) / 8)); // block align
|
||||
w.Write((ushort)data.Last.BitsPerSample);
|
||||
w.Write((ushort)format.Channels);
|
||||
w.Write(format.SampleRate);
|
||||
w.Write((format.SampleRate * format.Channels * format.BitsPerSample) / 8); // byte rate
|
||||
w.Write((ushort)((format.Channels * format.BitsPerSample) / 8)); // block align
|
||||
w.Write((ushort)format.BitsPerSample);
|
||||
|
||||
WriteFourCC(w, "data");
|
||||
w.Write((int)(stream.Length - 44));
|
||||
|
17
SharpWave/ICodec.cs
Normal file
17
SharpWave/ICodec.cs
Normal file
@ -0,0 +1,17 @@
|
||||
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;
|
||||
}
|
||||
}
|
48
SharpWave/IMediaContainer.cs
Normal file
48
SharpWave/IMediaContainer.cs
Normal file
@ -0,0 +1,48 @@
|
||||
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
|
||||
}
|
||||
}
|
31
SharpWave/Properties/AssemblyInfo.cs
Normal file
31
SharpWave/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#region Using directives
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#endregion
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("SharpWave")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("SharpWave")]
|
||||
[assembly: AssemblyCopyright("Copyright 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// This sets the default COM visibility of types in the assembly to invisible.
|
||||
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all the values or you can use the default the Revision and
|
||||
// Build Numbers by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
87
SharpWave/SharpWave.csproj
Normal file
87
SharpWave/SharpWave.csproj
Normal file
@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{77EA9D1E-4995-4D05-A9C7-29173CB5DC72}</ProjectGuid>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>SharpWave</RootNamespace>
|
||||
<AssemblyName>SharpWave</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<NoStdLib>False</NoStdLib>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<RegisterForComInterop>False</RegisterForComInterop>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<BaseAddress>4194304</BaseAddress>
|
||||
<FileAlignment>4096</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>Full</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<DebugType>None</DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="csvorbis\Block.cs" />
|
||||
<Compile Include="csvorbis\CodeBook.cs" />
|
||||
<Compile Include="csvorbis\Comment.cs" />
|
||||
<Compile Include="csvorbis\csorbisException.cs" />
|
||||
<Compile Include="csvorbis\DspState.cs" />
|
||||
<Compile Include="csvorbis\FuncFloor.cs" />
|
||||
<Compile Include="csvorbis\FuncMapping.cs" />
|
||||
<Compile Include="csvorbis\FuncResidue.cs" />
|
||||
<Compile Include="csvorbis\Info.cs" />
|
||||
<Compile Include="csvorbis\Mdct.cs" />
|
||||
<Compile Include="csvorbis\VorbisCodec.cs" />
|
||||
<Compile Include="csvorbis\Ogg\Buffer.cs" />
|
||||
<Compile Include="csvorbis\Ogg\Packet.cs" />
|
||||
<Compile Include="csvorbis\Ogg\Page.cs" />
|
||||
<Compile Include="csvorbis\Ogg\StreamState.cs" />
|
||||
<Compile Include="csvorbis\Ogg\SyncState.cs" />
|
||||
<Compile Include="csvorbis\StaticCodeBook.cs" />
|
||||
<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="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Utils\MemUtils.cs" />
|
||||
<Compile Include="Output\IAudioOutput.cs" />
|
||||
<Compile Include="VolumeMixer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="csvorbis\Ogg" />
|
||||
<Folder Include="csvorbis" />
|
||||
<Folder Include="Utils" />
|
||||
<Folder Include="Output" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="SharpWave.dll.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
6
SharpWave/SharpWave.dll.config
Normal file
6
SharpWave/SharpWave.dll.config
Normal file
@ -0,0 +1,6 @@
|
||||
<configuration>
|
||||
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
|
||||
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
|
||||
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
|
||||
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
|
||||
</configuration>
|
35
SharpWave/Utils/MemUtils.cs
Normal file
35
SharpWave/Utils/MemUtils.cs
Normal file
@ -0,0 +1,35 @@
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
SharpWave/VolumeMixer.cs
Normal file
39
SharpWave/VolumeMixer.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace SharpWave {
|
||||
|
||||
public unsafe static class VolumeMixer {
|
||||
|
||||
public static void Mix16( short* samples, int numSamples, int volumePercent ) {
|
||||
int numBulkSamples = numSamples & ~0x07;
|
||||
|
||||
// Unrolled loop, do 8 samples per iteration
|
||||
for( int i = 0; i < numBulkSamples; i += 8 ) {
|
||||
samples[0] = (short)(samples[0] * volumePercent / 100);
|
||||
samples[1] = (short)(samples[1] * volumePercent / 100);
|
||||
samples[2] = (short)(samples[2] * volumePercent / 100);
|
||||
samples[3] = (short)(samples[3] * volumePercent / 100);
|
||||
|
||||
samples[4] = (short)(samples[4] * volumePercent / 100);
|
||||
samples[5] = (short)(samples[5] * volumePercent / 100);
|
||||
samples[6] = (short)(samples[6] * volumePercent / 100);
|
||||
samples[7] = (short)(samples[7] * volumePercent / 100);
|
||||
|
||||
samples += 8;
|
||||
}
|
||||
|
||||
// Fixup the few last samples
|
||||
for( int i = numBulkSamples; i < numSamples; i++ ) {
|
||||
samples[0] = (short)(samples[0] * volumePercent / 100);
|
||||
samples++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Mix8( byte* samples, int numSamples, int volumePercent ) {
|
||||
for( int i = 0; i < numSamples; i++ ) {
|
||||
samples[0] = (byte)(127 + (samples[0] - 127) * volumePercent / 100);
|
||||
samples++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
SharpWave/csvorbis/Block.cs
Normal file
90
SharpWave/csvorbis/Block.cs
Normal file
@ -0,0 +1,90 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using csogg;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
public class Block
|
||||
{
|
||||
//necessary stream state for linking to the framing abstraction
|
||||
internal float[][] pcm = new float[0][]; // this is a pointer into local storage
|
||||
internal csBuffer opb = new csBuffer();
|
||||
|
||||
internal int lW;
|
||||
internal int W;
|
||||
internal int nW;
|
||||
internal int pcmend;
|
||||
internal int mode;
|
||||
|
||||
internal int eofflag;
|
||||
internal long granulepos;
|
||||
internal long sequence;
|
||||
internal DspState vd; // For read-only access of configuration
|
||||
|
||||
public void init(DspState vd) {
|
||||
this.vd = vd;
|
||||
}
|
||||
|
||||
public int synthesis(Packet op)
|
||||
{
|
||||
Info vi = vd.vi;
|
||||
opb.readinit(op.packet_base, op.packet, op.bytes);
|
||||
opb.read(1);
|
||||
// read our mode and pre/post windowsize
|
||||
mode = opb.read(vd.modebits);
|
||||
W = vi.mode_param[mode].blockflag;
|
||||
if(W != 0) {
|
||||
lW = opb.read(1); nW = opb.read(1);
|
||||
} else {
|
||||
lW = 0; nW = 0;
|
||||
}
|
||||
|
||||
// more setup
|
||||
granulepos = op.granulepos;
|
||||
sequence = op.packetno-3; // first block is third packet
|
||||
eofflag = op.e_o_s;
|
||||
|
||||
// alloc pcm passback storage
|
||||
pcmend = vi.blocksizes[W];
|
||||
if(pcm.Length<vi.channels)
|
||||
pcm = new float[vi.channels][];
|
||||
|
||||
for(int i = 0; i < vi.channels; i++) {
|
||||
if( pcm[i] == null || pcm[i].Length < pcmend ) {
|
||||
pcm[i] = new float[pcmend];
|
||||
} else {
|
||||
for(int j = 0; j < pcmend; j++)
|
||||
pcm[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// unpack_header enforces range checking
|
||||
int type = vi.map_type[vi.mode_param[mode].mapping];
|
||||
return(FuncMapping.mapping_P[type].inverse(this, vd.mode[mode]));
|
||||
}
|
||||
}
|
||||
}
|
403
SharpWave/csvorbis/CodeBook.cs
Normal file
403
SharpWave/csvorbis/CodeBook.cs
Normal file
@ -0,0 +1,403 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using csogg;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
class CodeBook
|
||||
{
|
||||
internal int dim; // codebook dimensions (elements per vector)
|
||||
internal int entries; // codebook entries
|
||||
internal StaticCodeBook c = new StaticCodeBook();
|
||||
|
||||
internal float[] valuelist; // list of dim*entries actual entry values
|
||||
internal DecodeAux decode_tree;
|
||||
|
||||
internal int[] t = new int[15]; // decodevs_add is synchronized for re-using t.
|
||||
|
||||
internal int decodevs_add(float[]a, int offset, csBuffer b, int n)
|
||||
{
|
||||
int step = n/dim;
|
||||
int entry;
|
||||
int i,j,o;
|
||||
|
||||
if(t.Length<step)
|
||||
{
|
||||
t = new int[step];
|
||||
}
|
||||
|
||||
for(i = 0; i < step; i++)
|
||||
{
|
||||
entry = decode(b);
|
||||
if(entry == -1)return(-1);
|
||||
t[i] = entry*dim;
|
||||
}
|
||||
for(i = 0,o = 0;i<dim;i++,o += step)
|
||||
{
|
||||
for(j = 0;j<step;j++)
|
||||
{
|
||||
a[offset+o+j] += valuelist[t[j]+i];
|
||||
}
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
internal int decodev_add(float[]a, int offset, csBuffer b,int n)
|
||||
{
|
||||
int i,j,k,entry;
|
||||
int t;
|
||||
|
||||
if(dim>8)
|
||||
{
|
||||
for(i = 0;i<n;)
|
||||
{
|
||||
entry = decode(b);
|
||||
if(entry == -1)return(-1);
|
||||
t = entry*dim;
|
||||
for(j = 0;j<dim;)
|
||||
{
|
||||
a[offset+(i++)] += valuelist[t+(j++)];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i<n;)
|
||||
{
|
||||
entry = decode(b);
|
||||
if(entry == -1)return(-1);
|
||||
t = entry*dim;
|
||||
j = 0;
|
||||
for(k = 0; k < dim; k++)
|
||||
{
|
||||
a[offset+(i++)] += valuelist[t+(j++)];
|
||||
}
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
internal int decodev_set(float[] a,int offset, csBuffer b, int n)
|
||||
{
|
||||
int i,j,entry;
|
||||
int t;
|
||||
|
||||
for(i = 0;i<n;)
|
||||
{
|
||||
entry = decode(b);
|
||||
if(entry == -1)return(-1);
|
||||
t = entry*dim;
|
||||
for(j = 0;j<dim;)
|
||||
{
|
||||
a[offset+i++] = valuelist[t+(j++)];
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
internal int decodevv_add(float[][] a, int offset,int ch, csBuffer b,int n)
|
||||
{
|
||||
int i,j,entry;
|
||||
int chptr = 0;
|
||||
//System.out.println("decodevv_add: a = "+a+",b = "+b+",valuelist = "+valuelist);
|
||||
|
||||
for(i = offset/ch;i<(offset+n)/ch;)
|
||||
{
|
||||
entry = decode(b);
|
||||
if(entry == -1)return(-1);
|
||||
|
||||
int t = entry*dim;
|
||||
for(j = 0;j<dim;j++)
|
||||
{
|
||||
a[chptr][i] += valuelist[t+j];
|
||||
chptr++;
|
||||
if(chptr == ch)
|
||||
{
|
||||
chptr = 0;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
// Decode side is specced and easier, because we don't need to find
|
||||
// matches using different criteria; we simply read and map. There are
|
||||
// two things we need to do 'depending':
|
||||
//
|
||||
// We may need to support interleave. We don't really, but it's
|
||||
// convenient to do it here rather than rebuild the vector later.
|
||||
//
|
||||
// Cascades may be additive or multiplicitive; this is not inherent in
|
||||
// the codebook, but set in the code using the codebook. Like
|
||||
// interleaving, it's easiest to do it here.
|
||||
// stage == 0 -> declarative (set the value)
|
||||
// stage == 1 -> additive
|
||||
// stage == 2 -> multiplicitive
|
||||
|
||||
// returns the entry number or -1 on eof
|
||||
internal int decode(csBuffer b)
|
||||
{
|
||||
int ptr = 0;
|
||||
DecodeAux t = decode_tree;
|
||||
int lok = b.look(t.tabn);
|
||||
//System.err.println(this+" "+t+" lok = "+lok+", tabn = "+t.tabn);
|
||||
|
||||
if(lok >= 0)
|
||||
{
|
||||
ptr = t.tab[lok];
|
||||
b.adv(t.tabl[lok]);
|
||||
if(ptr <= 0)
|
||||
{
|
||||
return -ptr;
|
||||
}
|
||||
}
|
||||
do
|
||||
{
|
||||
switch(b.read1())
|
||||
{
|
||||
case 0:
|
||||
ptr = t.ptr0[ptr];
|
||||
break;
|
||||
case 1:
|
||||
ptr = t.ptr1[ptr];
|
||||
break;
|
||||
case -1:
|
||||
default:
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
while(ptr>0);
|
||||
return(-ptr);
|
||||
}
|
||||
|
||||
// returns the entry number or -1 on eof
|
||||
internal int decodevs(float[] a, int index, csBuffer b, int step,int addmul)
|
||||
{
|
||||
int entry = decode(b);
|
||||
if(entry == -1)return(-1);
|
||||
switch(addmul)
|
||||
{
|
||||
case -1:
|
||||
for(int i = 0,o = 0;i<dim;i++,o += step)
|
||||
a[index+o] = valuelist[entry*dim+i];
|
||||
break;
|
||||
case 0:
|
||||
for(int i = 0,o = 0;i<dim;i++,o += step)
|
||||
a[index+o] += valuelist[entry*dim+i];
|
||||
break;
|
||||
case 1:
|
||||
for(int i = 0,o = 0;i<dim;i++,o += step)
|
||||
a[index+o] *= valuelist[entry*dim+i];
|
||||
break;
|
||||
default:
|
||||
//nothing
|
||||
break;
|
||||
}
|
||||
return(entry);
|
||||
}
|
||||
|
||||
internal void init_decode(StaticCodeBook s)
|
||||
{
|
||||
c = s;
|
||||
entries = s.entries;
|
||||
dim = s.dim;
|
||||
valuelist = s.unquantize();
|
||||
|
||||
decode_tree = make_decode_tree();
|
||||
}
|
||||
|
||||
// given a list of word lengths, generate a list of codewords. Works
|
||||
// for length ordered or unordered, always assigns the lowest valued
|
||||
// codewords first. Extended to handle unused entries (length 0)
|
||||
internal static int[] make_words(int[] l, int n)
|
||||
{
|
||||
int[] marker = new int[33];
|
||||
int[] r = new int[n];
|
||||
//memset(marker,0,sizeof(marker));
|
||||
|
||||
for(int i = 0;i<n;i++)
|
||||
{
|
||||
int length = l[i];
|
||||
if(length>0)
|
||||
{
|
||||
int entry = marker[length];
|
||||
|
||||
// when we claim a node for an entry, we also claim the nodes
|
||||
// below it (pruning off the imagined tree that may have dangled
|
||||
// from it) as well as blocking the use of any nodes directly
|
||||
// above for leaves
|
||||
|
||||
// update ourself
|
||||
if(length<32 && ((uint)entry>>length) != 0)
|
||||
{
|
||||
// error condition; the lengths must specify an overpopulated tree
|
||||
//free(r);
|
||||
return(null);
|
||||
}
|
||||
r[i] = entry;
|
||||
|
||||
// Look to see if the next shorter marker points to the node
|
||||
// above. if so, update it and repeat.
|
||||
{
|
||||
for(int j = length;j>0;j--)
|
||||
{
|
||||
if((marker[j]&1) != 0)
|
||||
{
|
||||
// have to jump branches
|
||||
if(j == 1)marker[1]++;
|
||||
else marker[j] = marker[j-1]<<1;
|
||||
break; // invariant says next upper marker would already
|
||||
// have been moved if it was on the same path
|
||||
}
|
||||
marker[j]++;
|
||||
}
|
||||
}
|
||||
|
||||
// prune the tree; the implicit invariant says all the longer
|
||||
// markers were dangling from our just-taken node. Dangle them
|
||||
// from our *new* node.
|
||||
for(int j = length+1;j<33;j++)
|
||||
{
|
||||
if(((uint)marker[j]>>1) == entry)
|
||||
{
|
||||
entry = marker[j];
|
||||
marker[j] = marker[j-1]<<1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bitreverse the words because our bitwise packer/unpacker is LSb
|
||||
// endian
|
||||
for(int i = 0;i<n;i++)
|
||||
{
|
||||
int temp = 0;
|
||||
for(int j = 0;j<l[i];j++)
|
||||
{
|
||||
temp <<= 1;
|
||||
temp = (int)((uint)temp | ((uint)r[i]>>j)&1);
|
||||
}
|
||||
r[i] = temp;
|
||||
}
|
||||
|
||||
return(r);
|
||||
}
|
||||
|
||||
// build the decode helper tree from the codewords
|
||||
internal DecodeAux make_decode_tree()
|
||||
{
|
||||
int top = 0;
|
||||
DecodeAux t = new DecodeAux();
|
||||
int[] ptr0 = t.ptr0 = new int[entries*2];
|
||||
int[] ptr1 = t.ptr1 = new int[entries*2];
|
||||
int[] codelist = make_words(c.lengthlist, c.entries);
|
||||
|
||||
if(codelist == null)return(null);
|
||||
t.aux = entries*2;
|
||||
|
||||
for(int i = 0;i<entries;i++)
|
||||
{
|
||||
if(c.lengthlist[i]>0)
|
||||
{
|
||||
int ptr = 0;
|
||||
int j;
|
||||
for(j = 0;j<c.lengthlist[i]-1;j++)
|
||||
{
|
||||
int bit = (int)(((uint)codelist[i]>>j)&1);
|
||||
if(bit == 0)
|
||||
{
|
||||
if(ptr0[ptr] == 0)
|
||||
{
|
||||
ptr0[ptr] = ++top;
|
||||
}
|
||||
ptr = ptr0[ptr];
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ptr1[ptr] == 0)
|
||||
{
|
||||
ptr1[ptr] = ++top;
|
||||
}
|
||||
ptr = ptr1[ptr];
|
||||
}
|
||||
}
|
||||
|
||||
if((((uint)codelist[i]>>j)&1) == 0){ ptr0[ptr] = -i; }
|
||||
else{ ptr1[ptr] = -i; }
|
||||
|
||||
}
|
||||
}
|
||||
//free(codelist);
|
||||
|
||||
t.tabn = VUtils.ilog(entries)-4;
|
||||
|
||||
if(t.tabn<5)t.tabn = 5;
|
||||
int n = 1<<t.tabn;
|
||||
t.tab = new int[n];
|
||||
t.tabl = new int[n];
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
int p = 0;
|
||||
int j = 0;
|
||||
for(j = 0; j < t.tabn && (p > 0 || j == 0); j++)
|
||||
{
|
||||
if ((i&(1<<j)) != 0)
|
||||
{
|
||||
p = ptr1[p];
|
||||
}
|
||||
else
|
||||
{
|
||||
p = ptr0[p];
|
||||
}
|
||||
}
|
||||
t.tab[i] = p; // -code
|
||||
t.tabl[i] = j; // length
|
||||
}
|
||||
|
||||
return(t);
|
||||
}
|
||||
}
|
||||
|
||||
class DecodeAux
|
||||
{
|
||||
internal int[] tab;
|
||||
internal int[] tabl;
|
||||
internal int tabn;
|
||||
|
||||
internal int[] ptr0;
|
||||
internal int[] ptr1;
|
||||
internal int aux; // number of tree entries
|
||||
}
|
||||
}
|
91
SharpWave/csvorbis/Comment.cs
Normal file
91
SharpWave/csvorbis/Comment.cs
Normal file
@ -0,0 +1,91 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using csogg;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
public class Comment
|
||||
{
|
||||
// unlimited user comment fields. libvorbis writes 'libvorbis'
|
||||
// whatever vendor is set to in encode
|
||||
public byte[][] user_comments;
|
||||
public int comments;
|
||||
public byte[] vendor;
|
||||
|
||||
public void init()
|
||||
{
|
||||
user_comments = null;
|
||||
comments = 0;
|
||||
vendor = null;
|
||||
}
|
||||
|
||||
internal int unpack(csBuffer opb)
|
||||
{
|
||||
int vendorlen = opb.read(32);
|
||||
vendor = new byte[vendorlen + 1];
|
||||
opb.read(vendor, vendorlen);
|
||||
comments = opb.read(32);
|
||||
user_comments = new byte[comments + 1][];
|
||||
|
||||
for(int i = 0; i < comments; i++) {
|
||||
int len = opb.read(32);
|
||||
user_comments[i] = new byte[len+1];
|
||||
opb.read(user_comments[i], len);
|
||||
}
|
||||
|
||||
opb.read(1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
internal void clear()
|
||||
{
|
||||
user_comments = null;
|
||||
vendor = null;
|
||||
}
|
||||
|
||||
public string getVendor()
|
||||
{
|
||||
return Encoding.UTF8.GetString(vendor);
|
||||
}
|
||||
|
||||
public string getComment(int i)
|
||||
{
|
||||
Encoding AE = Encoding.UTF8;
|
||||
if(comments <= i)return null;
|
||||
return Encoding.UTF8.GetString(user_comments[i]);
|
||||
}
|
||||
|
||||
public string toString()
|
||||
{
|
||||
String sum = "Vendor: " + getVendor();
|
||||
for(int i = 0; i < comments; i++)
|
||||
sum = sum + "\nComment: " + getComment(i);
|
||||
sum = sum + "\n";
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
319
SharpWave/csvorbis/DspState.cs
Normal file
319
SharpWave/csvorbis/DspState.cs
Normal file
@ -0,0 +1,319 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using csogg;
|
||||
using csvorbis;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
public class DspState
|
||||
{
|
||||
static float M_PI = 3.1415926539f;
|
||||
|
||||
internal Info vi;
|
||||
internal int modebits;
|
||||
|
||||
float[][] pcm;
|
||||
//float[][] pcmret;
|
||||
int pcm_storage;
|
||||
int pcm_current;
|
||||
int pcm_returned;
|
||||
|
||||
int lW;
|
||||
int W;
|
||||
int centerW;
|
||||
|
||||
long granulepos;
|
||||
public long sequence;
|
||||
|
||||
// local lookup storage
|
||||
//!! Envelope ve = new Envelope(); // envelope
|
||||
internal float[][][][] wnd; // block, leadin, leadout
|
||||
//vorbis_look_transform **transform[2]; // block, type
|
||||
internal Object[] transform;
|
||||
internal CodeBook[] fullbooks;
|
||||
// backend lookups are tied to the mode, not the backend or naked mapping
|
||||
internal Object[] mode;
|
||||
|
||||
public DspState()
|
||||
{
|
||||
transform = new Object[2];
|
||||
wnd = new float[2][][][];
|
||||
wnd[0] = new float[2][][];
|
||||
wnd[0][0] = new float[2][];
|
||||
wnd[0][1] = new float[2][];
|
||||
wnd[0][0][0] = new float[2];
|
||||
wnd[0][0][1] = new float[2];
|
||||
wnd[0][1][0] = new float[2];
|
||||
wnd[0][1][1] = new float[2];
|
||||
wnd[1] = new float[2][][];
|
||||
wnd[1][0] = new float[2][];
|
||||
wnd[1][1] = new float[2][];
|
||||
wnd[1][0][0] = new float[2];
|
||||
wnd[1][0][1] = new float[2];
|
||||
wnd[1][1][0] = new float[2];
|
||||
wnd[1][1][1] = new float[2];
|
||||
}
|
||||
|
||||
internal static float[] window(int wnd, int left, int right)
|
||||
{
|
||||
float[] ret = new float[wnd];
|
||||
// The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi)
|
||||
|
||||
int leftbegin = wnd/4-left/2;
|
||||
int rightbegin = wnd-wnd/4-right/2;
|
||||
|
||||
for(int i = 0;i<left;i++)
|
||||
{
|
||||
float x = (float)((i+.5)/left*M_PI/2.0);
|
||||
x = (float)Math.Sin(x);
|
||||
x *= x;
|
||||
x *= (float)(M_PI/2.0);
|
||||
x = (float)Math.Sin(x);
|
||||
ret[i+leftbegin] = x;
|
||||
}
|
||||
|
||||
for(int i = leftbegin+left;i<rightbegin;i++)
|
||||
{
|
||||
ret[i] = 1.0f;
|
||||
}
|
||||
|
||||
for(int i = 0;i<right;i++)
|
||||
{
|
||||
float x = (float)((right-i-.5)/right*M_PI/2.0);
|
||||
x = (float)Math.Sin(x);
|
||||
x *= x;
|
||||
x *= (float)(M_PI/2.0);
|
||||
x = (float)Math.Sin(x);
|
||||
ret[i+rightbegin] = x;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void init(Info vi) {
|
||||
this.vi = vi;
|
||||
modebits = VUtils.ilog2(vi.modes);
|
||||
|
||||
transform[0] = new Mdct();
|
||||
transform[1] = new Mdct();
|
||||
((Mdct)transform[0]).init(vi.blocksizes[0]);
|
||||
((Mdct)transform[1]).init(vi.blocksizes[1]);
|
||||
|
||||
wnd[0][0][0] = window(vi.blocksizes[0],vi.blocksizes[0]/2,vi.blocksizes[0]/2);
|
||||
wnd[1][0][0] = window(vi.blocksizes[1],vi.blocksizes[0]/2,vi.blocksizes[0]/2);
|
||||
wnd[1][0][1] = window(vi.blocksizes[1],vi.blocksizes[0]/2,vi.blocksizes[1]/2);
|
||||
wnd[1][1][0] = window(vi.blocksizes[1],vi.blocksizes[1]/2,vi.blocksizes[0]/2);
|
||||
wnd[1][1][1] = window(vi.blocksizes[1],vi.blocksizes[1]/2,vi.blocksizes[1]/2);
|
||||
|
||||
fullbooks = new CodeBook[vi.books];
|
||||
for(int i = 0;i<vi.books;i++) {
|
||||
fullbooks[i] = new CodeBook();
|
||||
fullbooks[i].init_decode(vi.book_param[i]);
|
||||
}
|
||||
|
||||
// initialize the storage vectors to a decent size greater than the minimum
|
||||
|
||||
pcm_storage = 8192; // we'll assume later that we have a minimum of twice
|
||||
// the blocksize of accumulated samples in analysis
|
||||
pcm = new float[vi.channels][];
|
||||
for(int i = 0;i<vi.channels;i++) {
|
||||
pcm[i] = new float[pcm_storage];
|
||||
}
|
||||
|
||||
// all 1 (large block) or 0 (small block)
|
||||
// explicitly set for the sake of clarity
|
||||
lW = 0; // previous window size
|
||||
W = 0; // current window size
|
||||
|
||||
// all vector indexes; multiples of samples_per_envelope_step
|
||||
centerW = vi.blocksizes[1]/2;
|
||||
pcm_current = centerW;
|
||||
|
||||
// initialize all the mapping/backend lookups
|
||||
mode = new Object[vi.modes];
|
||||
|
||||
for(int i = 0;i<vi.modes;i++) {
|
||||
int mapnum = vi.mode_param[i].mapping;
|
||||
int maptype = vi.map_type[mapnum];
|
||||
|
||||
mode[i] = FuncMapping.mapping_P[maptype].look(this,vi.mode_param[i],
|
||||
vi.map_param[mapnum]);
|
||||
}
|
||||
}
|
||||
|
||||
public void synthesis_init(Info vi) {
|
||||
init(vi);
|
||||
// Adjust centerW to allow an easier mechanism for determining output
|
||||
pcm_returned = centerW;
|
||||
centerW -= vi.blocksizes[W]/4+vi.blocksizes[lW]/4;
|
||||
granulepos = -1;
|
||||
sequence = -1;
|
||||
}
|
||||
|
||||
DspState(Info vi) : this() {
|
||||
synthesis_init(vi);
|
||||
}
|
||||
|
||||
// Unike in analysis, the window is only partially applied for each
|
||||
// block. The time domain envelope is not yet handled at the point of
|
||||
// calling (as it relies on the previous block).
|
||||
|
||||
public void synthesis_blockin(Block vb)
|
||||
{
|
||||
// Shift out any PCM/multipliers that we returned previously
|
||||
// centerW is currently the center of the last block added
|
||||
if(centerW>vi.blocksizes[1]/2 && pcm_returned>8192)
|
||||
{
|
||||
// don't shift too much; we need to have a minimum PCM buffer of
|
||||
// 1/2 long block
|
||||
|
||||
int shiftPCM = centerW-vi.blocksizes[1]/2;
|
||||
shiftPCM = (pcm_returned<shiftPCM?pcm_returned:shiftPCM);
|
||||
|
||||
pcm_current -= shiftPCM;
|
||||
centerW -= shiftPCM;
|
||||
pcm_returned -= shiftPCM;
|
||||
if(shiftPCM != 0)
|
||||
{
|
||||
for(int i = 0;i<vi.channels;i++)
|
||||
{
|
||||
Array.Copy(pcm[i], shiftPCM, pcm[i], 0, pcm_current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lW = W;
|
||||
W = vb.W;
|
||||
|
||||
if(sequence+1 != vb.sequence)
|
||||
granulepos = -1; // out of sequence; lose count
|
||||
|
||||
sequence = vb.sequence;
|
||||
|
||||
int sizeW = vi.blocksizes[W];
|
||||
int _centerW = centerW+vi.blocksizes[lW]/4+sizeW/4;
|
||||
int beginW = _centerW-sizeW/2;
|
||||
int endW = beginW+sizeW;
|
||||
int beginSl = 0;
|
||||
int endSl = 0;
|
||||
|
||||
// Do we have enough PCM/mult storage for the block?
|
||||
if(endW>pcm_storage)
|
||||
{
|
||||
// expand the storage
|
||||
pcm_storage = endW+vi.blocksizes[1];
|
||||
for(int i = 0;i<vi.channels;i++)
|
||||
{
|
||||
float[] foo = new float[pcm_storage];
|
||||
Array.Copy(pcm[i], 0, foo, 0, pcm[i].Length);
|
||||
pcm[i] = foo;
|
||||
}
|
||||
}
|
||||
|
||||
// overlap/add PCM
|
||||
switch(W)
|
||||
{
|
||||
case 0:
|
||||
beginSl = 0;
|
||||
endSl = vi.blocksizes[0]/2;
|
||||
break;
|
||||
case 1:
|
||||
beginSl = vi.blocksizes[1]/4-vi.blocksizes[lW]/4;
|
||||
endSl = beginSl+vi.blocksizes[lW]/2;
|
||||
break;
|
||||
}
|
||||
|
||||
for(int j = 0;j<vi.channels;j++)
|
||||
{
|
||||
int _pcm = beginW;
|
||||
// the overlap/add section
|
||||
int i = 0;
|
||||
for(i = beginSl;i<endSl;i++)
|
||||
{
|
||||
pcm[j][_pcm+i] += vb.pcm[j][i];
|
||||
}
|
||||
// the remaining section
|
||||
for(;i<sizeW;i++)
|
||||
{
|
||||
pcm[j][_pcm+i] = vb.pcm[j][i];
|
||||
}
|
||||
}
|
||||
|
||||
// track the frame number... This is for convenience, but also
|
||||
// making sure our last packet doesn't end with added padding. If
|
||||
// the last packet is partial, the number of samples we'll have to
|
||||
// return will be past the vb->granulepos.
|
||||
//
|
||||
// This is not foolproof! It will be confused if we begin
|
||||
// decoding at the last page after a seek or hole. In that case,
|
||||
// we don't have a starting point to judge where the last frame
|
||||
// is. For this reason, vorbisfile will always try to make sure
|
||||
// it reads the last two marked pages in proper sequence
|
||||
|
||||
if(granulepos == -1)
|
||||
{
|
||||
granulepos = vb.granulepos;
|
||||
}
|
||||
else
|
||||
{
|
||||
granulepos += (_centerW-centerW);
|
||||
if(vb.granulepos != -1 && granulepos != vb.granulepos)
|
||||
{
|
||||
if(granulepos>vb.granulepos && vb.eofflag != 0)
|
||||
{
|
||||
// partial last frame. Strip the padding off
|
||||
_centerW = _centerW - (int)(granulepos-vb.granulepos);
|
||||
}// else{ Shouldn't happen *unless* the bitstream is out of
|
||||
// spec. Either way, believe the bitstream }
|
||||
granulepos = vb.granulepos;
|
||||
}
|
||||
}
|
||||
|
||||
// Update, cleanup
|
||||
centerW = _centerW;
|
||||
pcm_current = endW;
|
||||
}
|
||||
|
||||
public int synthesis_pcmout(ref float[][] _pcm, int[] index)
|
||||
{
|
||||
if(pcm_returned<centerW)
|
||||
{
|
||||
for(int i = 0;i<vi.channels;i++)
|
||||
index[i] = pcm_returned;
|
||||
_pcm = pcm;
|
||||
return(centerW-pcm_returned);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
public void synthesis_read(int bytes) {
|
||||
if(bytes != 0 && pcm_returned + bytes>centerW)
|
||||
return;
|
||||
pcm_returned += bytes;
|
||||
}
|
||||
}
|
||||
}
|
560
SharpWave/csvorbis/FuncFloor.cs
Normal file
560
SharpWave/csvorbis/FuncFloor.cs
Normal file
@ -0,0 +1,560 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using csogg;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
abstract class FuncFloor
|
||||
{
|
||||
public static FuncFloor[] floor_P = {null,new Floor1()};
|
||||
|
||||
public abstract Object unpack(Info vi, csBuffer opb);
|
||||
public abstract Object look(DspState vd, InfoMode mi, Object i);
|
||||
public abstract Object inverse1(Block vb, Object i, Object memo);
|
||||
public abstract int inverse2(Block vb, Object i, Object memo, float[] fout);
|
||||
}
|
||||
|
||||
class Floor1 : FuncFloor
|
||||
{
|
||||
static int VIF_POSIT = 63;
|
||||
|
||||
public override Object unpack(Info vi , csBuffer opb)
|
||||
{
|
||||
int count = 0,maxclass = -1,rangebits;
|
||||
InfoFloor1 info = new InfoFloor1();
|
||||
|
||||
/* read partitions */
|
||||
info.partitions = opb.read(5); /* only 0 to 31 legal */
|
||||
for(int j = 0;j<info.partitions;j++)
|
||||
{
|
||||
info.partitionclass[j] = opb.read(4); /* only 0 to 15 legal */
|
||||
if(maxclass<info.partitionclass[j])
|
||||
maxclass = info.partitionclass[j];
|
||||
}
|
||||
|
||||
/* read partition classes */
|
||||
for(int j = 0;j<maxclass+1;j++)
|
||||
{
|
||||
info.class_dim[j] = opb.read(3)+1; /* 1 to 8 */
|
||||
info.class_subs[j] = opb.read(2); /* 0,1,2,3 bits */
|
||||
if(info.class_subs[j]<0)
|
||||
{
|
||||
//goto err_out;
|
||||
info.free();
|
||||
return(null);
|
||||
}
|
||||
if(info.class_subs[j] != 0)
|
||||
{
|
||||
info.class_book[j] = opb.read(8);
|
||||
}
|
||||
if(info.class_book[j]<0 || info.class_book[j] >= vi.books)
|
||||
{
|
||||
//goto err_out;
|
||||
info.free();
|
||||
return(null);
|
||||
}
|
||||
for(int k = 0;k<(1<<info.class_subs[j]);k++)
|
||||
{
|
||||
info.class_subbook[j][k] = opb.read(8)-1;
|
||||
if(info.class_subbook[j][k]<-1 || info.class_subbook[j][k] >= vi.books)
|
||||
{
|
||||
//goto err_out;
|
||||
info.free();
|
||||
return(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* read the post list */
|
||||
info.mult = opb.read(2)+1; /* only 1,2,3,4 legal now */
|
||||
rangebits = opb.read(4);
|
||||
|
||||
for(int j = 0,k = 0;j<info.partitions;j++)
|
||||
{
|
||||
count += info.class_dim[info.partitionclass[j]];
|
||||
for(;k<count;k++)
|
||||
{
|
||||
int t = info.postlist[k+2] = opb.read(rangebits);
|
||||
if(t<0 || t >= (1<<rangebits))
|
||||
{
|
||||
//goto err_out;
|
||||
info.free();
|
||||
return(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
info.postlist[0] = 0;
|
||||
info.postlist[1] = 1<<rangebits;
|
||||
|
||||
return(info);
|
||||
// err_out:
|
||||
// info.free();
|
||||
// return(null);
|
||||
}
|
||||
|
||||
public override Object look(DspState vd, InfoMode mi, Object i)
|
||||
{
|
||||
int _n = 0;
|
||||
|
||||
int[] sortpointer = new int[VIF_POSIT+2];
|
||||
|
||||
// Info vi = vd.vi;
|
||||
|
||||
InfoFloor1 info = (InfoFloor1)i;
|
||||
LookFloor1 look = new LookFloor1();
|
||||
look.vi = info;
|
||||
look.n = info.postlist[1];
|
||||
|
||||
/* we drop each position value in-between already decoded values,
|
||||
and use linear interpolation to predict each new value past the
|
||||
edges. The positions are read in the order of the position
|
||||
list... we precompute the bounding positions in the lookup. Of
|
||||
course, the neighbors can change (if a position is declined), but
|
||||
this is an initial mapping */
|
||||
|
||||
for(int j = 0;j<info.partitions;j++)
|
||||
{
|
||||
_n += info.class_dim[info.partitionclass[j]];
|
||||
}
|
||||
_n += 2;
|
||||
look.posts = _n;
|
||||
|
||||
/* also store a sorted position index */
|
||||
for(int j = 0;j<_n;j++)
|
||||
{
|
||||
sortpointer[j] = j;
|
||||
}
|
||||
// qsort(sortpointer,n,sizeof(int),icomp); // !!
|
||||
|
||||
int foo;
|
||||
for(int j = 0; j<_n-1; j++)
|
||||
{
|
||||
for(int k = j; k<_n; k++)
|
||||
{
|
||||
if(info.postlist[sortpointer[j]]>info.postlist[sortpointer[k]])
|
||||
{
|
||||
foo = sortpointer[k];
|
||||
sortpointer[k] = sortpointer[j];
|
||||
sortpointer[j] = foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* points from sort order back to range number */
|
||||
for(int j = 0;j<_n;j++)
|
||||
{
|
||||
look.forward_index[j] = sortpointer[j];
|
||||
}
|
||||
/* points from range order to sorted position */
|
||||
for(int j = 0;j<_n;j++)
|
||||
{
|
||||
look.reverse_index[look.forward_index[j]] = j;
|
||||
}
|
||||
/* we actually need the post values too */
|
||||
for(int j = 0;j<_n;j++)
|
||||
{
|
||||
look.sorted_index[j] = info.postlist[look.forward_index[j]];
|
||||
}
|
||||
|
||||
|
||||
/* quantize values to multiplier spec */
|
||||
switch(info.mult)
|
||||
{
|
||||
case 1: /* 1024 -> 256 */
|
||||
look.quant_q = 256;
|
||||
break;
|
||||
case 2: /* 1024 -> 128 */
|
||||
look.quant_q = 128;
|
||||
break;
|
||||
case 3: /* 1024 -> 86 */
|
||||
look.quant_q = 86;
|
||||
break;
|
||||
case 4: /* 1024 -> 64 */
|
||||
look.quant_q = 64;
|
||||
break;
|
||||
default:
|
||||
look.quant_q = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* discover our neighbors for decode where we don't use fit flags
|
||||
(that would push the neighbors outward) */
|
||||
for(int j = 0;j<_n-2;j++)
|
||||
{
|
||||
int lo = 0;
|
||||
int hi = 1;
|
||||
int lx = 0;
|
||||
int hx = look.n;
|
||||
int currentx = info.postlist[j+2];
|
||||
for(int k = 0;k<j+2;k++)
|
||||
{
|
||||
int x = info.postlist[k];
|
||||
if(x>lx && x<currentx)
|
||||
{
|
||||
lo = k;
|
||||
lx = x;
|
||||
}
|
||||
if(x<hx && x>currentx)
|
||||
{
|
||||
hi = k;
|
||||
hx = x;
|
||||
}
|
||||
}
|
||||
look.loneighbor[j] = lo;
|
||||
look.hineighbor[j] = hi;
|
||||
}
|
||||
|
||||
return look;
|
||||
}
|
||||
|
||||
public override Object inverse1(Block vb, Object ii, Object memo)
|
||||
{
|
||||
LookFloor1 look = (LookFloor1)ii;
|
||||
InfoFloor1 info = look.vi;
|
||||
CodeBook[] books = vb.vd.fullbooks;
|
||||
|
||||
/* unpack wrapped/predicted values from stream */
|
||||
if(vb.opb.read(1) == 1)
|
||||
{
|
||||
int[] fit_value = null;
|
||||
if(memo is int[])
|
||||
{
|
||||
fit_value = (int[])memo;
|
||||
}
|
||||
if(fit_value == null || fit_value.Length<look.posts)
|
||||
{
|
||||
fit_value = new int[look.posts];
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i<fit_value.Length; i++) fit_value[i] = 0;
|
||||
}
|
||||
|
||||
fit_value[0] = vb.opb.read(VUtils.ilog(look.quant_q-1));
|
||||
fit_value[1] = vb.opb.read(VUtils.ilog(look.quant_q-1));
|
||||
|
||||
/* partition by partition */
|
||||
for(int i = 0,j = 2;i<info.partitions;i++)
|
||||
{
|
||||
int clss = info.partitionclass[i];
|
||||
int cdim = info.class_dim[clss];
|
||||
int csubbits = info.class_subs[clss];
|
||||
int csub = 1<<csubbits;
|
||||
int cval = 0;
|
||||
|
||||
/* decode the partition's first stage cascade value */
|
||||
if(csubbits != 0)
|
||||
{
|
||||
cval = books[info.class_book[clss]].decode(vb.opb);
|
||||
|
||||
if(cval == -1)
|
||||
{
|
||||
//goto eop;
|
||||
return(null);
|
||||
}
|
||||
}
|
||||
|
||||
for(int k = 0;k<cdim;k++)
|
||||
{
|
||||
int book = info.class_subbook[clss][cval&(csub-1)];
|
||||
cval = (int)((uint)cval >> csubbits);
|
||||
if(book >= 0)
|
||||
{
|
||||
if((fit_value[j+k] = books[book].decode(vb.opb)) == -1)
|
||||
{
|
||||
return(null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fit_value[j+k] = 0;
|
||||
}
|
||||
}
|
||||
j += cdim;
|
||||
}
|
||||
|
||||
/* unwrap positive values and reconsitute via linear interpolation */
|
||||
for(int i = 2;i<look.posts;i++)
|
||||
{
|
||||
int predicted = render_point(info.postlist[look.loneighbor[i-2]],
|
||||
info.postlist[look.hineighbor[i-2]],
|
||||
fit_value[look.loneighbor[i-2]],
|
||||
fit_value[look.hineighbor[i-2]],
|
||||
info.postlist[i]);
|
||||
int hiroom = look.quant_q-predicted;
|
||||
int loroom = predicted;
|
||||
int room = (hiroom<loroom?hiroom:loroom)<<1;
|
||||
int val = fit_value[i];
|
||||
|
||||
if(val != 0)
|
||||
{
|
||||
if(val >= room)
|
||||
{
|
||||
if(hiroom>loroom)
|
||||
{
|
||||
val = val-loroom;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = -1-(val-hiroom);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if((val&1) != 0)
|
||||
{
|
||||
val = (int)(-((uint)(val+1) >> 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
val >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
fit_value[i] = val+predicted;
|
||||
fit_value[look.loneighbor[i-2]] &= 0x7fff;
|
||||
fit_value[look.hineighbor[i-2]] &= 0x7fff;
|
||||
}
|
||||
else
|
||||
{
|
||||
fit_value[i] = predicted|0x8000;
|
||||
}
|
||||
}
|
||||
return(fit_value);
|
||||
}
|
||||
|
||||
// eop:
|
||||
// return(NULL);
|
||||
return(null);
|
||||
}
|
||||
|
||||
private static int render_point(int x0,int x1,int y0,int y1,int x)
|
||||
{
|
||||
y0 &= 0x7fff; /* mask off flag */
|
||||
y1 &= 0x7fff;
|
||||
|
||||
int dy = y1-y0;
|
||||
int adx = x1-x0;
|
||||
int ady = Math.Abs(dy);
|
||||
int err = ady*(x-x0);
|
||||
|
||||
int off = (int)(err/adx);
|
||||
if(dy<0)return(y0-off);
|
||||
return(y0+off);
|
||||
}
|
||||
|
||||
public override int inverse2(Block vb, Object i, Object memo, float[] fout)
|
||||
{
|
||||
LookFloor1 look = (LookFloor1)i;
|
||||
InfoFloor1 info = look.vi;
|
||||
int n = vb.vd.vi.blocksizes[vb.mode]/2;
|
||||
|
||||
if(memo != null)
|
||||
{
|
||||
/* render the lines */
|
||||
int[] fit_value = (int[] )memo;
|
||||
int hx = 0, lx = 0;
|
||||
int ly = fit_value[0]*info.mult;
|
||||
for(int j = 1;j<look.posts;j++)
|
||||
{
|
||||
int current = look.forward_index[j];
|
||||
int hy = fit_value[current]&0x7fff;
|
||||
if(hy == fit_value[current])
|
||||
{
|
||||
hy *= info.mult;
|
||||
hx = info.postlist[current];
|
||||
render_line(lx,hx,ly,hy,fout);
|
||||
lx = hx; ly = hy;
|
||||
}
|
||||
}
|
||||
for(int j = hx;j<n;j++)
|
||||
fout[j] *= fout[j-1]; /* be certain */
|
||||
return(1);
|
||||
}
|
||||
for(int j = 0; j<n; j++)
|
||||
fout[j] = 0.0f;
|
||||
return(0);
|
||||
}
|
||||
|
||||
private static float[] FLOOR_fromdB_LOOKUP = {
|
||||
1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F,
|
||||
1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F,
|
||||
1.7623575e-07F, 1.8768855e-07F, 1.9988561e-07F, 2.128753e-07F,
|
||||
2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F,
|
||||
2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F,
|
||||
3.7516214e-07F, 3.9954229e-07F, 4.2550680e-07F, 4.5315863e-07F,
|
||||
4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F,
|
||||
6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F,
|
||||
7.9862701e-07F, 8.5052630e-07F, 9.0579828e-07F, 9.6466216e-07F,
|
||||
1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F,
|
||||
1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F,
|
||||
1.7000785e-06F, 1.8105592e-06F, 1.9282195e-06F, 2.0535261e-06F,
|
||||
2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F,
|
||||
2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F,
|
||||
3.6190449e-06F, 3.8542308e-06F, 4.1047004e-06F, 4.3714470e-06F,
|
||||
4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F,
|
||||
5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F,
|
||||
7.7040476e-06F, 8.2047000e-06F, 8.7378876e-06F, 9.3057248e-06F,
|
||||
9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F,
|
||||
1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F,
|
||||
1.6400004e-05F, 1.7465768e-05F, 1.8600792e-05F, 1.9809576e-05F,
|
||||
2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F,
|
||||
2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F,
|
||||
3.4911534e-05F, 3.7180282e-05F, 3.9596466e-05F, 4.2169667e-05F,
|
||||
4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F,
|
||||
5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F,
|
||||
7.4317983e-05F, 7.9147585e-05F, 8.4291040e-05F, 8.9768747e-05F,
|
||||
9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F,
|
||||
0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F,
|
||||
0.00015820453F, 0.00016848555F, 0.00017943469F, 0.00019109536F,
|
||||
0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F,
|
||||
0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F,
|
||||
0.00033677814F, 0.00035866388F, 0.00038197188F, 0.00040679456F,
|
||||
0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F,
|
||||
0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F,
|
||||
0.00071691700F, 0.00076350630F, 0.00081312324F, 0.00086596457F,
|
||||
0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F,
|
||||
0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F,
|
||||
0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F,
|
||||
0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F,
|
||||
0.0025254795F, 0.0026895994F, 0.0028643847F, 0.0030505286F,
|
||||
0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F,
|
||||
0.0041792066F, 0.0044507950F, 0.0047400328F, 0.0050480668F,
|
||||
0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F,
|
||||
0.0069158225F, 0.0073652516F, 0.0078438871F, 0.0083536271F,
|
||||
0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F,
|
||||
0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F,
|
||||
0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F,
|
||||
0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F,
|
||||
0.024362330F, 0.025945531F, 0.027631618F, 0.029427276F,
|
||||
0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F,
|
||||
0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F,
|
||||
0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F,
|
||||
0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F,
|
||||
0.085821044F, 0.091398179F, 0.097337747F, 0.10366330F,
|
||||
0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F,
|
||||
0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F,
|
||||
0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F,
|
||||
0.23501402F, 0.25028656F, 0.26655159F, 0.28387361F,
|
||||
0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F,
|
||||
0.38890521F, 0.41417847F, 0.44109412F, 0.46975890F,
|
||||
0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F,
|
||||
0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F,
|
||||
0.82788260F, 0.88168307F, 0.9389798F, 1.0F
|
||||
};
|
||||
|
||||
private static void render_line(int x0, int x1,int y0,int y1,float[] d)
|
||||
{
|
||||
int dy = y1-y0;
|
||||
int adx = x1-x0;
|
||||
int ady = Math.Abs(dy);
|
||||
int bbase = dy/adx;
|
||||
int sy = (dy < 0) ? bbase-1 : bbase+1;
|
||||
int x = x0;
|
||||
int y = y0;
|
||||
int err = 0;
|
||||
ady -= Math.Abs(bbase*adx);
|
||||
|
||||
d[x] *= FLOOR_fromdB_LOOKUP[y];
|
||||
while(++x < x1) {
|
||||
err = err + ady;
|
||||
if(err >= adx)
|
||||
{
|
||||
err -= adx;
|
||||
y += sy;
|
||||
}
|
||||
else
|
||||
{
|
||||
y += bbase;
|
||||
}
|
||||
d[x] *= FLOOR_fromdB_LOOKUP[y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InfoFloor1
|
||||
{
|
||||
const int VIF_POSIT = 63;
|
||||
const int VIF_CLASS = 16;
|
||||
const int VIF_PARTS = 31;
|
||||
|
||||
internal int partitions; /* 0 to 31 */
|
||||
internal int[] partitionclass = new int[VIF_PARTS]; /* 0 to 15 */
|
||||
|
||||
internal int[] class_dim = new int[VIF_CLASS]; /* 1 to 8 */
|
||||
internal int[] class_subs = new int[VIF_CLASS]; /* 0,1,2,3 (bits: 1<<n poss) */
|
||||
internal int[] class_book = new int[VIF_CLASS]; /* subs ^ dim entries */
|
||||
internal int[][] class_subbook = new int[VIF_CLASS][]; /* [VIF_CLASS][subs] */
|
||||
|
||||
|
||||
internal int mult; /* 1 2 3 or 4 */
|
||||
internal int[] postlist = new int[VIF_POSIT+2]; /* first two implicit */
|
||||
|
||||
internal InfoFloor1()
|
||||
{
|
||||
for(int i = 0; i<class_subbook.Length; i++)
|
||||
{
|
||||
class_subbook[i] = new int[8];
|
||||
}
|
||||
}
|
||||
|
||||
internal void free()
|
||||
{
|
||||
partitionclass = null;
|
||||
class_dim = null;
|
||||
class_subs = null;
|
||||
class_book = null;
|
||||
class_subbook = null;
|
||||
postlist = null;
|
||||
}
|
||||
}
|
||||
|
||||
class LookFloor1
|
||||
{
|
||||
static int VIF_POSIT = 63;
|
||||
|
||||
internal int[] sorted_index = new int[VIF_POSIT+2];
|
||||
internal int[] forward_index = new int[VIF_POSIT+2];
|
||||
internal int[] reverse_index = new int[VIF_POSIT+2];
|
||||
internal int[] hineighbor = new int[VIF_POSIT];
|
||||
internal int[] loneighbor = new int[VIF_POSIT];
|
||||
internal int posts;
|
||||
|
||||
internal int n;
|
||||
internal int quant_q;
|
||||
internal InfoFloor1 vi;
|
||||
|
||||
void free()
|
||||
{
|
||||
sorted_index = null;
|
||||
forward_index = null;
|
||||
reverse_index = null;
|
||||
hineighbor = null;
|
||||
loneighbor = null;
|
||||
}
|
||||
}
|
||||
}
|
267
SharpWave/csvorbis/FuncMapping.cs
Normal file
267
SharpWave/csvorbis/FuncMapping.cs
Normal file
@ -0,0 +1,267 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using csogg;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
abstract class FuncMapping
|
||||
{
|
||||
public static FuncMapping[] mapping_P = {new Mapping0()};
|
||||
|
||||
public abstract Object unpack(Info info , csBuffer buffer);
|
||||
public abstract Object look(DspState vd, InfoMode vm, Object m);
|
||||
public abstract int inverse(Block vd, Object lm);
|
||||
}
|
||||
|
||||
class Mapping0 : FuncMapping
|
||||
{
|
||||
public override Object look(DspState vd, InfoMode vm, Object m)
|
||||
{
|
||||
Info vi = vd.vi;
|
||||
LookMapping0 looks = new LookMapping0();
|
||||
InfoMapping0 info = looks.map = (InfoMapping0)m;
|
||||
looks.mode = vm;
|
||||
|
||||
looks.floor_look = new Object[info.submaps];
|
||||
looks.residue_look = new Object[info.submaps];
|
||||
|
||||
looks.floor_func = new FuncFloor[info.submaps];
|
||||
looks.residue_func = new FuncResidue[info.submaps];
|
||||
|
||||
for(int i = 0;i<info.submaps;i++)
|
||||
{
|
||||
int timenum = info.timesubmap[i];
|
||||
int floornum = info.floorsubmap[i];
|
||||
int resnum = info.residuesubmap[i];
|
||||
|
||||
looks.floor_func[i] = FuncFloor.floor_P[vi.floor_type[floornum]];
|
||||
looks.floor_look[i] = looks.floor_func[i].
|
||||
look(vd,vm,vi.floor_param[floornum]);
|
||||
|
||||
looks.residue_func[i] = FuncResidue.residue_P[vi.residue_type[resnum]];
|
||||
looks.residue_look[i] = looks.residue_func[i].
|
||||
look(vd,vm,vi.residue_param[resnum]);
|
||||
}
|
||||
|
||||
looks.ch = vi.channels;
|
||||
return(looks);
|
||||
}
|
||||
|
||||
public override Object unpack(Info vi, csBuffer opb) {
|
||||
InfoMapping0 info = new InfoMapping0();
|
||||
info.submaps = opb.read(1) != 0
|
||||
? opb.read(4) + 1 : 1;
|
||||
|
||||
if(opb.read(1) != 0) {
|
||||
info.coupling_steps = opb.read(8)+1;
|
||||
|
||||
for(int i = 0;i<info.coupling_steps;i++) {
|
||||
info.coupling_mag[i] = opb.read(VUtils.ilog2(vi.channels));
|
||||
info.coupling_ang[i] = opb.read(VUtils.ilog2(vi.channels));
|
||||
}
|
||||
}
|
||||
opb.read(2);
|
||||
|
||||
if(info.submaps>1) {
|
||||
for(int i = 0;i<vi.channels;i++)
|
||||
info.chmuxlist[i] = opb.read(4);
|
||||
}
|
||||
|
||||
for(int i = 0;i<info.submaps;i++)
|
||||
{
|
||||
info.timesubmap[i] = opb.read(8);
|
||||
info.floorsubmap[i] = opb.read(8);
|
||||
info.residuesubmap[i] = opb.read(8);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
float[][] pcmbundle = null;
|
||||
int[] zerobundle = null;
|
||||
int[] nonzero = null;
|
||||
Object[] floormemo = null;
|
||||
|
||||
public override int inverse(Block vb, Object l)
|
||||
{
|
||||
//System.err.println("Mapping0.inverse");
|
||||
DspState vd = vb.vd;
|
||||
Info vi = vd.vi;
|
||||
LookMapping0 look = (LookMapping0)l;
|
||||
InfoMapping0 info = look.map;
|
||||
InfoMode mode = look.mode;
|
||||
int n = vb.pcmend = vi.blocksizes[vb.W];
|
||||
|
||||
float[] window = vd.wnd[vb.W][vb.lW][vb.nW];
|
||||
if(pcmbundle == null || pcmbundle.Length<vi.channels)
|
||||
{
|
||||
pcmbundle = new float[vi.channels][];
|
||||
nonzero = new int[vi.channels];
|
||||
zerobundle = new int[vi.channels];
|
||||
floormemo = new Object[vi.channels];
|
||||
}
|
||||
|
||||
// recover the spectral envelope; store it in the PCM vector for now
|
||||
for(int i = 0;i<vi.channels;i++)
|
||||
{
|
||||
float[] pcm = vb.pcm[i];
|
||||
int submap = info.chmuxlist[i];
|
||||
|
||||
floormemo[i] = look.floor_func[submap].inverse1(vb,look.
|
||||
floor_look[submap],
|
||||
floormemo[i]
|
||||
);
|
||||
if(floormemo[i] != null)
|
||||
nonzero[i] = 1;
|
||||
else
|
||||
nonzero[i] = 0;
|
||||
for(int j = 0; j<n/2; j++)
|
||||
pcm[j] = 0;
|
||||
}
|
||||
|
||||
for(int i = 0; i<info.coupling_steps; i++)
|
||||
{
|
||||
if(nonzero[info.coupling_mag[i]] != 0 || nonzero[info.coupling_ang[i]] != 0) {
|
||||
nonzero[info.coupling_mag[i]] = 1;
|
||||
nonzero[info.coupling_ang[i]] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// recover the residue, apply directly to the spectral envelope
|
||||
for(int i = 0;i<info.submaps;i++) {
|
||||
int ch_in_bundle = 0;
|
||||
for(int j = 0;j<vi.channels;j++) {
|
||||
if(info.chmuxlist[j] != i) continue;
|
||||
|
||||
zerobundle[ch_in_bundle] = nonzero[j] != 0 ? 1 : 0;
|
||||
pcmbundle[ch_in_bundle++] = vb.pcm[j];
|
||||
}
|
||||
|
||||
look.residue_func[i].inverse(vb,look.residue_look[i],
|
||||
pcmbundle,zerobundle,ch_in_bundle);
|
||||
}
|
||||
InverseCoupling(n, vb, info);
|
||||
|
||||
// compute and apply spectral envelope
|
||||
for(int i = 0;i<vi.channels;i++) {
|
||||
float[] pcm = vb.pcm[i];
|
||||
int submap = info.chmuxlist[i];
|
||||
look.floor_func[submap].inverse2(vb,look.floor_look[submap],floormemo[i],pcm);
|
||||
}
|
||||
|
||||
// transform the PCM data; takes PCM vector, vb; modifies PCM vector
|
||||
// only MDCT right now....
|
||||
for(int i = 0;i<vi.channels;i++) {
|
||||
float[] pcm = vb.pcm[i];
|
||||
((Mdct)vd.transform[vb.W]).backward(pcm,pcm);
|
||||
}
|
||||
|
||||
// window the data
|
||||
for(int i = 0;i<vi.channels;i++) {
|
||||
float[] pcm = vb.pcm[i];
|
||||
if(nonzero[i] != 0) {
|
||||
for(int j = 0;j<n;j++)
|
||||
pcm[j] *= window[j];
|
||||
} else {
|
||||
for(int j = 0;j<n;j++)
|
||||
pcm[j] = 0.0f;
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
void InverseCoupling(int n, Block vb, InfoMapping0 info) {
|
||||
for( int i = info.coupling_steps - 1;i >= 0; i-- ) {
|
||||
float[] pcmM = vb.pcm[info.coupling_mag[i]];
|
||||
float[] pcmA = vb.pcm[info.coupling_ang[i]];
|
||||
|
||||
for( int j = 0; j < n / 2; j++ ) {
|
||||
float mag = pcmM[j];
|
||||
float ang = pcmA[j];
|
||||
|
||||
if( mag > 0 ) {
|
||||
if( ang > 0 ) {
|
||||
pcmM[j] = mag;
|
||||
pcmA[j] = mag-ang;
|
||||
} else {
|
||||
pcmA[j] = mag;
|
||||
pcmM[j] = mag+ang;
|
||||
}
|
||||
} else {
|
||||
if( ang > 0 ) {
|
||||
pcmM[j] = mag;
|
||||
pcmA[j] = mag+ang;
|
||||
} else {
|
||||
pcmA[j] = mag;
|
||||
pcmM[j] = mag-ang;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InfoMapping0
|
||||
{
|
||||
internal int submaps; // <= 16
|
||||
internal int[] chmuxlist = new int[256]; // up to 256 channels in a Vorbis stream
|
||||
|
||||
internal int[] timesubmap = new int[16]; // [mux]
|
||||
internal int[] floorsubmap = new int[16]; // [mux] submap to floors
|
||||
internal int[] residuesubmap = new int[16];// [mux] submap to residue
|
||||
internal int[] psysubmap = new int[16]; // [mux]; encode only
|
||||
|
||||
internal int coupling_steps;
|
||||
internal int[] coupling_mag = new int[256];
|
||||
internal int[] coupling_ang = new int[256];
|
||||
|
||||
internal void free()
|
||||
{
|
||||
chmuxlist = null;
|
||||
timesubmap = null;
|
||||
floorsubmap = null;
|
||||
residuesubmap = null;
|
||||
psysubmap = null;
|
||||
|
||||
coupling_mag = null;
|
||||
coupling_ang = null;
|
||||
}
|
||||
}
|
||||
|
||||
class LookMapping0
|
||||
{
|
||||
internal InfoMode mode;
|
||||
internal InfoMapping0 map;
|
||||
internal Object[] floor_look;
|
||||
internal Object[] residue_look;
|
||||
|
||||
internal FuncFloor[] floor_func;
|
||||
internal FuncResidue[] residue_func;
|
||||
|
||||
internal int ch;
|
||||
}
|
||||
}
|
312
SharpWave/csvorbis/FuncResidue.cs
Normal file
312
SharpWave/csvorbis/FuncResidue.cs
Normal file
@ -0,0 +1,312 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using csogg;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
abstract class FuncResidue
|
||||
{
|
||||
public static FuncResidue[] residue_P = {new Residue0(), new Residue1(), new Residue2()};
|
||||
|
||||
public abstract Object unpack(Info vi, csBuffer opb);
|
||||
public abstract Object look(DspState vd, InfoMode vm, Object vr);
|
||||
|
||||
public abstract int inverse(Block vb, Object vl, float[][] fin, int[] nonzero,int ch);
|
||||
}
|
||||
|
||||
class Residue0 : FuncResidue
|
||||
{
|
||||
public override Object unpack(Info vi, csBuffer opb)
|
||||
{
|
||||
int acc = 0;
|
||||
InfoResidue0 info = new InfoResidue0();
|
||||
|
||||
info.begin = opb.read(24);
|
||||
info.end = opb.read(24);
|
||||
info.grouping = opb.read(24)+1;
|
||||
info.partitions = opb.read(6)+1;
|
||||
info.groupbook = opb.read(8);
|
||||
|
||||
for(int j = 0;j<info.partitions;j++) {
|
||||
int cascade = opb.read(3);
|
||||
if(opb.read(1) != 0)
|
||||
cascade |= opb.read(5) << 3;
|
||||
info.secondstages[j] = cascade;
|
||||
acc += VUtils.icount(cascade);
|
||||
}
|
||||
|
||||
for(int j = 0;j<acc;j++)
|
||||
info.booklist[j] = opb.read(8);
|
||||
return info;
|
||||
}
|
||||
|
||||
public override Object look(DspState vd, InfoMode vm, Object vr)
|
||||
{
|
||||
InfoResidue0 info = (InfoResidue0)vr;
|
||||
LookResidue0 look = new LookResidue0();
|
||||
int acc = 0;
|
||||
int dim;
|
||||
int maxstage = 0;
|
||||
look.info = info;
|
||||
look.map = vm.mapping;
|
||||
|
||||
look.parts = info.partitions;
|
||||
look.fullbooks = vd.fullbooks;
|
||||
look.phrasebook = vd.fullbooks[info.groupbook];
|
||||
|
||||
dim = look.phrasebook.dim;
|
||||
|
||||
look.partbooks = new int[look.parts][];
|
||||
|
||||
for(int j = 0;j<look.parts;j++)
|
||||
{
|
||||
int stages = VUtils.ilog(info.secondstages[j]);
|
||||
if(stages != 0)
|
||||
{
|
||||
if(stages>maxstage)maxstage = stages;
|
||||
look.partbooks[j] = new int[stages];
|
||||
for(int k = 0; k<stages; k++)
|
||||
{
|
||||
if((info.secondstages[j]&(1<<k)) != 0)
|
||||
{
|
||||
look.partbooks[j][k] = info.booklist[acc++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
look.partvals = (int)Math.Round(Math.Pow(look.parts,dim));
|
||||
look.stages = maxstage;
|
||||
look.decodemap = new int[look.partvals][];
|
||||
for(int j = 0;j<look.partvals;j++)
|
||||
{
|
||||
int val = j;
|
||||
int mult = look.partvals/look.parts;
|
||||
look.decodemap[j] = new int[dim];
|
||||
|
||||
for(int k = 0;k<dim;k++)
|
||||
{
|
||||
int deco = val/mult;
|
||||
val -= deco*mult;
|
||||
mult /= look.parts;
|
||||
look.decodemap[j][k] = deco;
|
||||
}
|
||||
}
|
||||
return(look);
|
||||
}
|
||||
|
||||
static int[][][] partword = new int[2][][];
|
||||
|
||||
internal static int _01inverse(Block vb, Object vl, float[][] fin, int ch, int decodepart)
|
||||
{
|
||||
int i,j,k,l,s;
|
||||
LookResidue0 look = (LookResidue0 )vl;
|
||||
InfoResidue0 info = look.info;
|
||||
|
||||
// move all this setup out later
|
||||
int samples_per_partition = info.grouping;
|
||||
int partitions_per_word = look.phrasebook.dim;
|
||||
int n = info.end-info.begin;
|
||||
|
||||
int partvals = n/samples_per_partition;
|
||||
int partwords = (partvals+partitions_per_word-1)/partitions_per_word;
|
||||
|
||||
if(partword.Length<ch)
|
||||
{
|
||||
partword = new int[ch][][];
|
||||
for(j = 0;j<ch;j++)
|
||||
{
|
||||
partword[j] = new int[partwords][];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(j = 0;j<ch;j++)
|
||||
{
|
||||
if(partword[j] == null || partword[j].Length<partwords)
|
||||
partword[j] = new int[partwords][];
|
||||
}
|
||||
}
|
||||
|
||||
for(s = 0;s<look.stages;s++)
|
||||
{
|
||||
// each loop decodes on partition codeword containing
|
||||
// partitions_pre_word partitions
|
||||
for(i = 0,l = 0;i<partvals;l++)
|
||||
{
|
||||
if(s == 0)
|
||||
{
|
||||
// fetch the partition word for each channel
|
||||
for(j = 0;j<ch;j++)
|
||||
{
|
||||
int temp = look.phrasebook.decode(vb.opb);
|
||||
partword[j][l] = look.decodemap[temp];
|
||||
}
|
||||
}
|
||||
|
||||
// now we decode residual values for the partitions
|
||||
for(k = 0;k<partitions_per_word && i<partvals;k++,i++)
|
||||
for(j = 0;j<ch;j++)
|
||||
{
|
||||
int offset = info.begin+i*samples_per_partition;
|
||||
if((info.secondstages[partword[j][l][k]]&(1<<s)) != 0)
|
||||
{
|
||||
CodeBook stagebook = look.fullbooks[look.partbooks[partword[j][l][k]][s]];
|
||||
if(stagebook != null)
|
||||
{
|
||||
if(decodepart == 0)
|
||||
stagebook.decodevs_add(fin[j],offset,vb.opb,samples_per_partition);
|
||||
else if(decodepart == 1)
|
||||
stagebook.decodev_add(fin[j], offset, vb.opb,samples_per_partition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
internal static int _2inverse(Block vb, Object vl, float[][] fin, int ch)
|
||||
{
|
||||
int i,k,l,s;
|
||||
LookResidue0 look = (LookResidue0 )vl;
|
||||
InfoResidue0 info = look.info;
|
||||
|
||||
// move all this setup out later
|
||||
int samples_per_partition = info.grouping;
|
||||
int partitions_per_word = look.phrasebook.dim;
|
||||
int n = info.end-info.begin;
|
||||
|
||||
int partvals = n/samples_per_partition;
|
||||
int partwords = (partvals+partitions_per_word-1)/partitions_per_word;
|
||||
|
||||
int[][] partword = new int[partwords][];
|
||||
for(s = 0;s<look.stages;s++)
|
||||
{
|
||||
for(i = 0,l = 0;i<partvals;l++)
|
||||
{
|
||||
if(s == 0)
|
||||
{
|
||||
// fetch the partition word for each channel
|
||||
int temp = look.phrasebook.decode(vb.opb);
|
||||
partword[l] = look.decodemap[temp];
|
||||
}
|
||||
|
||||
// now we decode residual values for the partitions
|
||||
for(k = 0;k<partitions_per_word && i<partvals;k++,i++)
|
||||
{
|
||||
int offset = info.begin+i*samples_per_partition;
|
||||
if((info.secondstages[partword[l][k]]&(1<<s)) != 0)
|
||||
{
|
||||
CodeBook stagebook = look.fullbooks[look.partbooks[partword[l][k]][s]];
|
||||
if(stagebook != null)
|
||||
stagebook.decodevv_add(fin, offset, ch, vb.opb,samples_per_partition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
public override int inverse(Block vb, Object vl, float[][] fin, int[] nonzero, int ch)
|
||||
{
|
||||
//System.err.println("Residue0.inverse");
|
||||
int used = 0;
|
||||
for(int i = 0;i<ch;i++)
|
||||
{
|
||||
if(nonzero[i] != 0)
|
||||
{
|
||||
fin[used++] = fin[i];
|
||||
}
|
||||
}
|
||||
if(used != 0)
|
||||
return(_01inverse(vb, vl, fin, used, 0));
|
||||
else
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
class LookResidue0
|
||||
{
|
||||
internal InfoResidue0 info;
|
||||
internal int map;
|
||||
|
||||
internal int parts;
|
||||
internal int stages;
|
||||
internal CodeBook[] fullbooks;
|
||||
internal CodeBook phrasebook;
|
||||
internal int[][] partbooks;
|
||||
|
||||
internal int partvals;
|
||||
internal int[][] decodemap;
|
||||
}
|
||||
|
||||
class InfoResidue0
|
||||
{
|
||||
// block-partitioned VQ coded straight residue
|
||||
internal int begin;
|
||||
internal int end;
|
||||
|
||||
// first stage (lossless partitioning)
|
||||
internal int grouping; // group n vectors per partition
|
||||
internal int partitions; // possible codebooks for a partition
|
||||
internal int groupbook; // huffbook for partitioning
|
||||
internal int[] secondstages = new int[64]; // expanded out to pointers in lookup
|
||||
internal int[] booklist = new int[256]; // list of second stage books
|
||||
}
|
||||
|
||||
class Residue1 : Residue0
|
||||
{
|
||||
public override int inverse(Block vb, Object vl, float[][] fin, int[] nonzero, int ch)
|
||||
{
|
||||
int used = 0;
|
||||
for(int i = 0; i<ch; i++) {
|
||||
if(nonzero[i] != 0)
|
||||
fin[used++] = fin[i];
|
||||
}
|
||||
|
||||
if(used != 0)
|
||||
return(_01inverse(vb, vl, fin, used, 1));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Residue2 : Residue0
|
||||
{
|
||||
public override int inverse(Block vb, Object vl, float[][] fin, int[] nonzero, int ch) {
|
||||
int i = 0;
|
||||
for(i = 0;i<ch;i++)
|
||||
if(nonzero[i] != 0)
|
||||
break;
|
||||
|
||||
if(i == ch)
|
||||
return(0); /* no nonzero vectors */
|
||||
|
||||
return(_2inverse(vb, vl, fin, ch));
|
||||
}
|
||||
}
|
||||
}
|
204
SharpWave/csvorbis/Info.cs
Normal file
204
SharpWave/csvorbis/Info.cs
Normal file
@ -0,0 +1,204 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using csogg;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
class InfoMode
|
||||
{
|
||||
internal int blockflag;
|
||||
internal int windowtype;
|
||||
internal int transformtype;
|
||||
internal int mapping;
|
||||
}
|
||||
|
||||
public class Info
|
||||
{
|
||||
private static int OV_ENOTAUDIO = -135;
|
||||
|
||||
public int version, channels, rate;
|
||||
|
||||
// Vorbis supports only short and long blocks, but allows the
|
||||
// encoder to choose the sizes
|
||||
internal int[] blocksizes = new int[2];
|
||||
|
||||
// modes are the primary means of supporting on-the-fly different
|
||||
// blocksizes, different channel mappings (LR or mid-side),
|
||||
// different residue backends, etc. Each mode consists of a
|
||||
// blocksize flag and a mapping (along with the mapping setup
|
||||
|
||||
internal int modes, maps, times, floors, residues, books;
|
||||
|
||||
internal InfoMode[] mode_param = null;
|
||||
|
||||
internal int[] map_type = null;
|
||||
internal Object[] map_param = null;
|
||||
|
||||
internal int[] floor_type = null;
|
||||
internal Object[] floor_param = null;
|
||||
|
||||
internal int[] residue_type = null;
|
||||
internal Object[] residue_param = null;
|
||||
|
||||
internal StaticCodeBook[] book_param = null;
|
||||
|
||||
// used by synthesis, which has a full, alloced vi
|
||||
public void init() {
|
||||
rate = 0;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
mode_param = null;
|
||||
map_param = null;
|
||||
floor_param = null;
|
||||
residue_param = null;
|
||||
book_param = null;
|
||||
}
|
||||
|
||||
// Header packing/unpacking
|
||||
void unpack_info(csBuffer opb) {
|
||||
version = opb.read(32);
|
||||
channels = opb.read(8);
|
||||
rate = opb.read(32);
|
||||
|
||||
opb.read(32); // bitrate_upper
|
||||
opb.read(32); // bitrate_nominal
|
||||
opb.read(32); // bitrate_lower
|
||||
|
||||
blocksizes[0] = 1<<opb.read(4);
|
||||
blocksizes[1] = 1<<opb.read(4);
|
||||
opb.read(1);
|
||||
}
|
||||
|
||||
void unpack_books(csBuffer opb) {
|
||||
books = opb.read(8) + 1;
|
||||
CheckEntries(ref book_param, books);
|
||||
for(int i = 0;i < books;i++) {
|
||||
book_param[i] = new StaticCodeBook();
|
||||
book_param[i].unpack(opb);
|
||||
}
|
||||
|
||||
times = opb.read(6) + 1;
|
||||
for(int i = 0;i<times;i++)
|
||||
opb.read(16);
|
||||
|
||||
floors = opb.read(6) + 1;
|
||||
CheckEntries(ref floor_param, ref floor_type, floors);
|
||||
for(int i = 0;i<floors;i++) {
|
||||
floor_type[i] = opb.read(16);
|
||||
floor_param[i] = FuncFloor.floor_P[floor_type[i]].unpack(this,opb);
|
||||
}
|
||||
|
||||
residues = opb.read(6) + 1;
|
||||
CheckEntries(ref residue_param, ref residue_type, residues);
|
||||
for(int i = 0;i<residues;i++) {
|
||||
residue_type[i] = opb.read(16);
|
||||
residue_param[i] = FuncResidue.residue_P[residue_type[i]].unpack(this,opb);
|
||||
}
|
||||
|
||||
maps = opb.read(6) + 1;
|
||||
CheckEntries(ref map_param, ref map_type, maps);
|
||||
for(int i = 0;i<maps;i++) {
|
||||
map_type[i] = opb.read(16);
|
||||
map_param[i] = FuncMapping.mapping_P[map_type[i]].unpack(this,opb);
|
||||
}
|
||||
|
||||
modes = opb.read(6) + 1;
|
||||
CheckEntries(ref mode_param, modes);
|
||||
for(int i = 0;i<modes;i++) {
|
||||
mode_param[i] = new InfoMode();
|
||||
mode_param[i].blockflag = opb.read(1);
|
||||
mode_param[i].windowtype = opb.read(16);
|
||||
mode_param[i].transformtype = opb.read(16);
|
||||
mode_param[i].mapping = opb.read(8);
|
||||
}
|
||||
opb.read(1);
|
||||
}
|
||||
|
||||
void CheckEntries<T>(ref T[] array, int count) {
|
||||
if(array == null || array.Length != count) {
|
||||
array = new T[count];
|
||||
}
|
||||
}
|
||||
|
||||
void CheckEntries<T>(ref T[] array, ref int[] types, int count) {
|
||||
if(array == null || array.Length != count) {
|
||||
array = new T[count];
|
||||
types = new int[count];
|
||||
}
|
||||
}
|
||||
|
||||
public void synthesis_headerin(Comment vc, Packet op) {
|
||||
if(op == null) return;
|
||||
|
||||
csBuffer opb = new csBuffer();
|
||||
opb.readinit(op.packet_base, op.packet, op.bytes);
|
||||
byte[] buffer = new byte[6];
|
||||
int packtype = opb.read(8);
|
||||
opb.read(buffer, 6);
|
||||
|
||||
if(buffer[0] != 'v' || buffer[1] != 'o' || buffer[2] != 'r' ||
|
||||
buffer[3] != 'b' || buffer[4] != 'i' || buffer[5] != 's') {
|
||||
throw new InvalidOperationException("Expected vorbis header");
|
||||
}
|
||||
|
||||
switch(packtype) {
|
||||
case 0x01: // least significant *bit* is read first
|
||||
unpack_info(opb);
|
||||
break;
|
||||
case 0x03: // least significant *bit* is read first
|
||||
vc.unpack(opb);
|
||||
break;
|
||||
case 0x05: // least significant *bit* is read first
|
||||
unpack_books(opb);
|
||||
break;
|
||||
default:
|
||||
// Not a valid vorbis header type
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public int blocksize(Packet op) {
|
||||
csBuffer opb = new csBuffer();
|
||||
opb.readinit(op.packet_base, op.packet, op.bytes);
|
||||
|
||||
/* Check the packet type */
|
||||
if(opb.read(1) != 0)
|
||||
return(OV_ENOTAUDIO);
|
||||
|
||||
int modebits = VUtils.ilog2(modes);
|
||||
int mode = opb.read(modebits);
|
||||
return blocksizes[mode_param[mode].blockflag];
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "version:" + version + ", channels:" + channels
|
||||
+ ", rate:" + rate;
|
||||
}
|
||||
}
|
||||
}
|
249
SharpWave/csvorbis/Mdct.cs
Normal file
249
SharpWave/csvorbis/Mdct.cs
Normal file
@ -0,0 +1,249 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using csogg;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
class Mdct
|
||||
{
|
||||
|
||||
//static private float cPI3_8 = 0.38268343236508977175f;
|
||||
//static private float cPI2_8 = 0.70710678118654752441f;
|
||||
//static private float cPI1_8 = 0.92387953251128675613f;
|
||||
|
||||
int n;
|
||||
int log2n;
|
||||
|
||||
float[] trig;
|
||||
int[] bitrev;
|
||||
|
||||
float scale;
|
||||
|
||||
internal void init(int n)
|
||||
{
|
||||
bitrev = new int[n/4];
|
||||
trig = new float[n+n/4];
|
||||
|
||||
int n2 = (int)((uint)n >> 1);
|
||||
log2n = (int)Math.Round(Math.Log(n)/Math.Log(2));
|
||||
this.n = n;
|
||||
|
||||
|
||||
int AE = 0;
|
||||
int AO = 1;
|
||||
int BE = AE+n/2;
|
||||
int BO = BE+1;
|
||||
int CE = BE+n/2;
|
||||
int CO = CE+1;
|
||||
// trig lookups...
|
||||
for(int i = 0;i<n/4;i++)
|
||||
{
|
||||
trig[AE+i*2] = (float)Math.Cos((Math.PI/n)*(4*i));
|
||||
trig[AO+i*2] = (float)-Math.Sin((Math.PI/n)*(4*i));
|
||||
trig[BE+i*2] = (float)Math.Cos((Math.PI/(2*n))*(2*i+1));
|
||||
trig[BO+i*2] = (float)Math.Sin((Math.PI/(2*n))*(2*i+1));
|
||||
}
|
||||
for(int i = 0;i<n/8;i++)
|
||||
{
|
||||
trig[CE+i*2] = (float)Math.Cos((Math.PI/n)*(4*i+2));
|
||||
trig[CO+i*2] = (float)-Math.Sin((Math.PI/n)*(4*i+2));
|
||||
}
|
||||
|
||||
{
|
||||
int mask = (1<<(log2n-1))-1;
|
||||
int msb = 1<<(log2n-2);
|
||||
for(int i = 0;i<n/8;i++)
|
||||
{
|
||||
int acc = 0;
|
||||
for(int j = 0; (((uint)msb) >> j) != 0; j++)
|
||||
if(((((uint)msb>>j))&i) != 0)
|
||||
acc |= 1 << j;
|
||||
bitrev[i*2] = ((~acc)&mask);
|
||||
// bitrev[i*2] = ((~acc)&mask)-1;
|
||||
bitrev[i*2+1] = acc;
|
||||
}
|
||||
}
|
||||
scale = 4.0f/n;
|
||||
}
|
||||
|
||||
float[] _x = new float[1024];
|
||||
float[] _w = new float[1024];
|
||||
|
||||
internal void backward(float[] fin, float[] fout)
|
||||
{
|
||||
if(_x.Length < n/2){_x = new float[n/2];}
|
||||
if(_w.Length < n/2){_w = new float[n/2];}
|
||||
float[] x = _x;
|
||||
float[] w = _w;
|
||||
int n2 = (int)((uint)n >> 1);
|
||||
int n4 = (int)((uint)n >> 2);
|
||||
int n8 = (int)((uint)n >> 3);
|
||||
|
||||
// rotate + step 1
|
||||
int inO = 1;
|
||||
int xO = 0;
|
||||
int A = n2;
|
||||
|
||||
for(int i = 0;i<n8;i++) {
|
||||
A -= 2;
|
||||
x[xO++] = -fin[inO+2]*trig[A+1] - fin[inO]*trig[A];
|
||||
x[xO++] = fin[inO]*trig[A+1] - fin[inO+2]*trig[A];
|
||||
inO += 4;
|
||||
}
|
||||
|
||||
inO = n2-4;
|
||||
|
||||
for(int i = 0;i<n8;i++) {
|
||||
A -= 2;
|
||||
x[xO++] = fin[inO]*trig[A+1] + fin[inO+2]*trig[A];
|
||||
x[xO++] = fin[inO]*trig[A] - fin[inO+2]*trig[A+1];
|
||||
inO -= 4;
|
||||
}
|
||||
|
||||
float[] xxx = mdct_kernel(x,w,n,n2,n4,n8);
|
||||
int xx = 0;
|
||||
|
||||
// step 8
|
||||
int B = n2;
|
||||
int o1 = n4,o2 = o1-1;
|
||||
int o3 = n4+n2,o4 = o3-1;
|
||||
|
||||
for(int i = 0;i<n4;i++)
|
||||
{
|
||||
float temp1 = (xxx[xx] * trig[B+1] - xxx[xx+1] * trig[B]);
|
||||
float temp2 = -(xxx[xx] * trig[B] + xxx[xx+1] * trig[B+1]);
|
||||
|
||||
fout[o1] = -temp1;
|
||||
fout[o2] = temp1;
|
||||
fout[o3] = temp2;
|
||||
fout[o4] = temp2;
|
||||
|
||||
o1++;
|
||||
o2--;
|
||||
o3++;
|
||||
o4--;
|
||||
xx += 2;
|
||||
B += 2;
|
||||
}
|
||||
}
|
||||
internal float[] mdct_kernel(float[] x, float[] w,
|
||||
int n, int n2, int n4, int n8)
|
||||
{
|
||||
// step 2
|
||||
|
||||
int xA = n4;
|
||||
int xB = 0;
|
||||
int w2 = n4;
|
||||
int A = n2;
|
||||
|
||||
for(int i = 0;i<n4;)
|
||||
{
|
||||
float xx0 = x[xA] - x[xB];
|
||||
w[w2+i] = x[xA++] + x[xB++];
|
||||
float xx1 = x[xA] - x[xB];
|
||||
A -= 4;
|
||||
|
||||
w[i++] = xx0 * trig[A] + xx1 * trig[A+1];
|
||||
w[i] = xx1 * trig[A] - xx0 * trig[A+1];
|
||||
|
||||
w[w2+i] = x[xA++]+x[xB++];
|
||||
i++;
|
||||
}
|
||||
|
||||
// step 3
|
||||
for(int i = 0;i<log2n-3;i++)
|
||||
{
|
||||
int k0 = (int)((uint)n >> (i+2));
|
||||
int k1 = 1 << (i+3);
|
||||
int wbase = n2-2;
|
||||
|
||||
A = 0;
|
||||
float[] temp;
|
||||
|
||||
for(int r = 0; r<((uint)k0>>2); r++)
|
||||
{
|
||||
int w1 = wbase;
|
||||
w2 = w1-(k0>>1);
|
||||
float AEv = trig[A],wA;
|
||||
float AOv = trig[A+1],wB;
|
||||
wbase -= 2;
|
||||
|
||||
k0++;
|
||||
for(int s = 0;s<(2<<i);s++)
|
||||
{
|
||||
wB = w[w1] -w[w2];
|
||||
x[w1] = w[w1] +w[w2];
|
||||
|
||||
wA = w[++w1] -w[++w2];
|
||||
x[w1] = w[w1] +w[w2];
|
||||
|
||||
x[w2] = wA*AEv - wB*AOv;
|
||||
x[w2-1] = wB*AEv + wA*AOv;
|
||||
|
||||
w1 -= k0;
|
||||
w2 -= k0;
|
||||
}
|
||||
k0--;
|
||||
A += k1;
|
||||
}
|
||||
|
||||
temp = w;
|
||||
w = x;
|
||||
x = temp;
|
||||
}
|
||||
|
||||
// step 4, 5, 6, 7
|
||||
int C = n;
|
||||
int bit = 0;
|
||||
int x1 = 0;
|
||||
int x2 = n2-1;
|
||||
|
||||
for(int i = 0;i<n8;i++)
|
||||
{
|
||||
int t1 = bitrev[bit++];
|
||||
int t2 = bitrev[bit++];
|
||||
|
||||
float wA = w[t1]-w[t2+1];
|
||||
float wB = w[t1-1]+w[t2];
|
||||
float wC = w[t1]+w[t2+1];
|
||||
float wD = w[t1-1]-w[t2];
|
||||
|
||||
float wACE = wA* trig[C];
|
||||
float wBCE = wB* trig[C++];
|
||||
float wACO = wA* trig[C];
|
||||
float wBCO = wB* trig[C++];
|
||||
|
||||
x[x1++] = ( wC+wACO+wBCE)*.5f;
|
||||
x[x2--] = (-wD+wBCO-wACE)*.5f;
|
||||
x[x1++] = ( wD+wBCO-wACE)*.5f;
|
||||
x[x2--] = ( wC-wACO-wBCE)*.5f;
|
||||
}
|
||||
return(x);
|
||||
}
|
||||
}
|
||||
}
|
220
SharpWave/csvorbis/Ogg/Buffer.cs
Normal file
220
SharpWave/csvorbis/Ogg/Buffer.cs
Normal file
@ -0,0 +1,220 @@
|
||||
/* csogg
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace csogg
|
||||
{
|
||||
public class csBuffer
|
||||
{
|
||||
private static uint[] mask={
|
||||
0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f,
|
||||
0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff,
|
||||
0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff,
|
||||
0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff,
|
||||
0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff,
|
||||
0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff,
|
||||
0x3fffffff,0x7fffffff,0xffffffff
|
||||
};
|
||||
int ptr = 0;
|
||||
byte[] buffer = null;
|
||||
int endbit = 0;
|
||||
int endbyte = 0;
|
||||
int storage = 0;
|
||||
|
||||
public void read (byte[] s, int bytes)
|
||||
{
|
||||
int i = 0;
|
||||
while(bytes--!=0)
|
||||
{
|
||||
s[i++]=(byte)(read(8));
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
ptr = 0;
|
||||
buffer[0] = (byte)'\0';
|
||||
endbit = endbyte = 0;
|
||||
}
|
||||
|
||||
public void readinit(byte[] buf, int start, int bytes)
|
||||
{
|
||||
ptr = start;
|
||||
buffer = buf;
|
||||
endbit = endbyte = 0;
|
||||
storage = bytes;
|
||||
}
|
||||
|
||||
public int look(int bits)
|
||||
{
|
||||
int ret;
|
||||
uint m = mask[bits];
|
||||
|
||||
bits += endbit;
|
||||
|
||||
if(endbyte + 4 >= storage)
|
||||
{
|
||||
if(endbyte+(bits-1)/8 >= storage)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
ret = ((buffer[ptr]) & 0xff) >> endbit;
|
||||
|
||||
if(bits > 8)
|
||||
{
|
||||
ret |= ((buffer[ptr+1]) & 0xff) << (8 - endbit);
|
||||
if(bits > 16)
|
||||
{
|
||||
ret |= ((buffer[ptr+2])&0xff) << (16-endbit);
|
||||
if(bits > 24)
|
||||
{
|
||||
ret |= ((buffer[ptr+3])&0xff) << (24-endbit);
|
||||
if((bits > 32) && (endbit != 0))
|
||||
{
|
||||
ret |= ((buffer[ptr+4])&0xff) << (32-endbit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = (int)(m & ret);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
public int look1()
|
||||
{
|
||||
if(endbyte >= storage)
|
||||
return(-1);
|
||||
return((buffer[ptr] >> endbit) & 1);
|
||||
}
|
||||
|
||||
public void adv(int bits)
|
||||
{
|
||||
bits += endbit;
|
||||
ptr += bits / 8;
|
||||
endbyte += bits / 8;
|
||||
endbit = bits & 7;
|
||||
}
|
||||
|
||||
public void adv1()
|
||||
{
|
||||
++endbit;
|
||||
if(endbit > 7)
|
||||
{
|
||||
endbit = 0;
|
||||
ptr++;
|
||||
endbyte++;
|
||||
}
|
||||
}
|
||||
|
||||
public int read(int bits)
|
||||
{
|
||||
int ret;
|
||||
uint m=mask[bits];
|
||||
|
||||
bits += endbit;
|
||||
|
||||
if(endbyte+4 >= storage)
|
||||
{
|
||||
ret = -1;
|
||||
if(endbyte + (bits-1)/8 >= storage)
|
||||
{
|
||||
ptr += bits/8;
|
||||
endbyte += bits/8;
|
||||
endbit = bits&7;
|
||||
return(ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret = ((buffer[ptr]) & 0xff) >> endbit;
|
||||
if(bits > 8)
|
||||
{
|
||||
ret|=((buffer[ptr+1])&0xff)<<(8-endbit);
|
||||
if(bits > 16)
|
||||
{
|
||||
ret|=((buffer[ptr+2])&0xff)<<(16-endbit);
|
||||
if(bits > 24)
|
||||
{
|
||||
ret|=((buffer[ptr+3])&0xff)<<(24-endbit);
|
||||
|
||||
if((bits > 32) && (endbit != 0))
|
||||
{
|
||||
ret|=((buffer[ptr+4])&0xff)<<(32-endbit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret &= (int)m;
|
||||
|
||||
ptr += bits/8;
|
||||
endbyte += bits/8;
|
||||
endbit = bits&7;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
public int read1()
|
||||
{
|
||||
int ret;
|
||||
if(endbyte>=storage)
|
||||
{
|
||||
ret = -1;
|
||||
endbit++;
|
||||
if(endbit > 7)
|
||||
{
|
||||
endbit = 0;
|
||||
ptr++;
|
||||
endbyte++;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
ret=(buffer[ptr] >> endbit) & 1;
|
||||
|
||||
endbit++;
|
||||
if(endbit > 7)
|
||||
{
|
||||
endbit = 0;
|
||||
ptr++;
|
||||
endbyte++;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
public int bytes()
|
||||
{
|
||||
return(endbyte+(endbit+7)/8);
|
||||
}
|
||||
|
||||
public int bits()
|
||||
{
|
||||
return(endbyte*8+endbit);
|
||||
}
|
||||
|
||||
public byte[] buf()
|
||||
{
|
||||
return(buffer);
|
||||
}
|
||||
}
|
||||
}
|
42
SharpWave/csvorbis/Ogg/Packet.cs
Normal file
42
SharpWave/csvorbis/Ogg/Packet.cs
Normal file
@ -0,0 +1,42 @@
|
||||
/* csogg
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace csogg
|
||||
{
|
||||
public class Packet
|
||||
{
|
||||
public byte[] packet_base;
|
||||
public int packet, bytes;
|
||||
public int b_o_s, e_o_s;
|
||||
|
||||
public long granulepos;
|
||||
public long packetno; // sequence number for decode; the framing
|
||||
// knows where there's a hole in the data,
|
||||
// but we need coupling so that the codec
|
||||
// (which is in a seperate abstraction
|
||||
// layer) also knows about the gap
|
||||
}
|
||||
}
|
137
SharpWave/csvorbis/Ogg/Page.cs
Normal file
137
SharpWave/csvorbis/Ogg/Page.cs
Normal file
@ -0,0 +1,137 @@
|
||||
/* csogg
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace csogg
|
||||
{
|
||||
public class Page
|
||||
{
|
||||
private static uint[] crc_lookup=new uint[256];
|
||||
|
||||
private static uint crc_entry(uint index)
|
||||
{
|
||||
uint r = index << 24;
|
||||
for(int i=0; i<8; i++)
|
||||
{
|
||||
if((r& 0x80000000)!=0)
|
||||
{
|
||||
r=(r << 1)^0x04c11db7; /* The same as the ethernet generator
|
||||
polynomial, although we use an
|
||||
unreflected alg and an init/final
|
||||
of 0, not 0xffffffff */
|
||||
}
|
||||
else
|
||||
{
|
||||
r <<= 1;
|
||||
}
|
||||
}
|
||||
return (r & 0xffffffff);
|
||||
}
|
||||
|
||||
public byte[] header_base;
|
||||
public int header;
|
||||
public int header_len;
|
||||
public byte[] body_base;
|
||||
public int body;
|
||||
public int body_len;
|
||||
|
||||
internal int version()
|
||||
{
|
||||
return header_base[header+4]&0xff;
|
||||
}
|
||||
internal int continued()
|
||||
{
|
||||
return (header_base[header+5]&0x01);
|
||||
}
|
||||
public int bos()
|
||||
{
|
||||
return (header_base[header+5]&0x02);
|
||||
}
|
||||
public int eos()
|
||||
{
|
||||
return (header_base[header+5]&0x04);
|
||||
}
|
||||
public long granulepos()
|
||||
{
|
||||
long foo = header_base[header+13]&0xff;
|
||||
foo = (foo<<8) | (uint)(header_base[header+12]&0xff);
|
||||
foo = (foo<<8) | (uint)(header_base[header+11]&0xff);
|
||||
foo = (foo<<8) | (uint)(header_base[header+10]&0xff);
|
||||
foo = (foo<<8) | (uint)(header_base[header+9]&0xff);
|
||||
foo = (foo<<8) | (uint)(header_base[header+8]&0xff);
|
||||
foo = (foo<<8) | (uint)(header_base[header+7]&0xff);
|
||||
foo = (foo<<8) | (uint)(header_base[header+6]&0xff);
|
||||
return(foo);
|
||||
}
|
||||
public int serialno()
|
||||
{
|
||||
return (header_base[header+14]&0xff)|
|
||||
((header_base[header+15]&0xff)<<8)|
|
||||
((header_base[header+16]&0xff)<<16)|
|
||||
((header_base[header+17]&0xff)<<24);
|
||||
}
|
||||
internal int pageno()
|
||||
{
|
||||
return (header_base[header+18]&0xff)|
|
||||
((header_base[header+19]&0xff)<<8)|
|
||||
((header_base[header+20]&0xff)<<16)|
|
||||
((header_base[header+21]&0xff)<<24);
|
||||
}
|
||||
|
||||
internal void checksum()
|
||||
{
|
||||
uint crc_reg=0;
|
||||
uint a, b;
|
||||
|
||||
for(int i=0;i<header_len;i++)
|
||||
{
|
||||
a = header_base[header+i] & 0xffu;
|
||||
b = (crc_reg >> 24) & 0xff;
|
||||
crc_reg = (crc_reg<<8)^crc_lookup[a^b];
|
||||
//crc_reg = (crc_reg<<8)^(uint)(crc_lookup[((crc_reg >> 24)&0xff)^(header_base[header+i]&0xff)]);
|
||||
}
|
||||
for(int i=0;i<body_len;i++)
|
||||
{
|
||||
a = body_base[body+i] & 0xffu;
|
||||
b = (crc_reg >> 24) & 0xff;
|
||||
crc_reg = (crc_reg<<8)^crc_lookup[a^b];
|
||||
|
||||
//crc_reg = (crc_reg<<8)^(uint)(crc_lookup[((crc_reg >> 24)&0xff)^(body_base[body+i]&0xff)]);
|
||||
}
|
||||
header_base[header+22]=(byte)crc_reg/*&0xff*/;
|
||||
header_base[header+23]=(byte)(crc_reg>>8)/*&0xff*/;
|
||||
header_base[header+24]=(byte)(crc_reg>>16)/*&0xff*/;
|
||||
header_base[header+25]=(byte)(crc_reg>>24)/*&0xff*/;
|
||||
}
|
||||
|
||||
public Page()
|
||||
{
|
||||
for(uint i=0; i<crc_lookup.Length; i++)
|
||||
{
|
||||
crc_lookup[i]=crc_entry(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
387
SharpWave/csvorbis/Ogg/StreamState.cs
Normal file
387
SharpWave/csvorbis/Ogg/StreamState.cs
Normal file
@ -0,0 +1,387 @@
|
||||
/* csogg
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace csogg
|
||||
{
|
||||
public class StreamState
|
||||
{
|
||||
byte[] body_data; /* bytes from packet bodies */
|
||||
int body_storage; /* storage elements allocated */
|
||||
int body_fill; /* elements stored; fill mark */
|
||||
private int body_returned; /* elements of fill returned */
|
||||
|
||||
|
||||
int[] lacing_vals; /* The values that will go to the segment table */
|
||||
long[] granule_vals; /* pcm_pos values for headers. Not compact
|
||||
this way, but it is simple coupled to the
|
||||
lacing fifo */
|
||||
int lacing_storage;
|
||||
int lacing_fill;
|
||||
int lacing_packet;
|
||||
int lacing_returned;
|
||||
|
||||
public int e_o_s; /* set when we have buffered the last packet in the
|
||||
logical bitstream */
|
||||
int serialno;
|
||||
int pageno;
|
||||
long packetno; /* sequence number for decode; the framing
|
||||
knows where there's a hole in the data,
|
||||
but we need coupling so that the codec
|
||||
(which is in a seperate abstraction
|
||||
layer) also knows about the gap */
|
||||
long granulepos;
|
||||
|
||||
StreamState(int serialno) : this()
|
||||
{
|
||||
init(serialno);
|
||||
}
|
||||
|
||||
public StreamState()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
body_storage=16*1024;
|
||||
body_data=new byte[body_storage];
|
||||
lacing_storage=1024;
|
||||
lacing_vals=new int[lacing_storage];
|
||||
granule_vals=new long[lacing_storage];
|
||||
}
|
||||
public void init(int serialno)
|
||||
{
|
||||
if(body_data==null){ init(); }
|
||||
else
|
||||
{
|
||||
for(int i=0; i<body_data.Length; i++) body_data[i]=0;
|
||||
for(int i=0; i<lacing_vals.Length; i++) lacing_vals[i]=0;
|
||||
for(int i=0; i<granule_vals.Length; i++) granule_vals[i]=0;
|
||||
}
|
||||
this.serialno=serialno;
|
||||
}
|
||||
public void clear()
|
||||
{
|
||||
body_data=null;
|
||||
lacing_vals=null;
|
||||
granule_vals=null;
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
void body_expand(int needed)
|
||||
{
|
||||
if(body_storage<=body_fill+needed)
|
||||
{
|
||||
body_storage+=(needed+1024);
|
||||
byte[] foo=new byte[body_storage];
|
||||
Array.Copy(body_data, 0, foo, 0, body_data.Length);
|
||||
body_data=foo;
|
||||
}
|
||||
}
|
||||
void lacing_expand(int needed)
|
||||
{
|
||||
if(lacing_storage<=lacing_fill+needed)
|
||||
{
|
||||
lacing_storage+=(needed+32);
|
||||
int[] foo=new int[lacing_storage];
|
||||
Array.Copy(lacing_vals, 0, foo, 0, lacing_vals.Length);
|
||||
lacing_vals=foo;
|
||||
|
||||
long[] bar=new long[lacing_storage];
|
||||
Array.Copy(granule_vals, 0, bar, 0, granule_vals.Length);
|
||||
granule_vals=bar;
|
||||
}
|
||||
}
|
||||
|
||||
/* submit data to the internal buffer of the framing engine */
|
||||
public int packetin(Packet op)
|
||||
{
|
||||
int lacing_val=op.bytes/255+1;
|
||||
|
||||
if(body_returned!=0)
|
||||
{
|
||||
/* advance packet data according to the body_returned pointer. We
|
||||
had to keep it around to return a pointer into the buffer last
|
||||
call */
|
||||
|
||||
body_fill-=body_returned;
|
||||
if(body_fill!=0)
|
||||
{
|
||||
Array.Copy(body_data, body_returned, body_data, 0, body_fill);
|
||||
}
|
||||
body_returned=0;
|
||||
}
|
||||
|
||||
/* make sure we have the buffer storage */
|
||||
body_expand(op.bytes);
|
||||
lacing_expand(lacing_val);
|
||||
|
||||
/* Copy in the submitted packet. Yes, the copy is a waste; this is
|
||||
the liability of overly clean abstraction for the time being. It
|
||||
will actually be fairly easy to eliminate the extra copy in the
|
||||
future */
|
||||
|
||||
Array.Copy(op.packet_base, op.packet, body_data, body_fill, op.bytes);
|
||||
body_fill+=op.bytes;
|
||||
//System.out.println("add: "+body_fill);
|
||||
|
||||
/* Store lacing vals for this packet */
|
||||
int j;
|
||||
for(j=0;j<lacing_val-1;j++)
|
||||
{
|
||||
lacing_vals[lacing_fill+j]=255;
|
||||
granule_vals[lacing_fill+j]=granulepos;
|
||||
}
|
||||
lacing_vals[lacing_fill+j]=(op.bytes)%255;
|
||||
granulepos=granule_vals[lacing_fill+j]=op.granulepos;
|
||||
|
||||
/* flag the first segment as the beginning of the packet */
|
||||
lacing_vals[lacing_fill]|= 0x100;
|
||||
|
||||
lacing_fill+=lacing_val;
|
||||
|
||||
/* for the sake of completeness */
|
||||
packetno++;
|
||||
|
||||
if(op.e_o_s!=0)e_o_s=1;
|
||||
return(0);
|
||||
}
|
||||
|
||||
public int packetout(Packet op)
|
||||
{
|
||||
|
||||
/* The last part of decode. We have the stream broken into packet
|
||||
segments. Now we need to group them into packets (or return the
|
||||
out of sync markers) */
|
||||
|
||||
int ptr=lacing_returned;
|
||||
|
||||
if(lacing_packet<=ptr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
if((lacing_vals[ptr]&0x400)!=0)
|
||||
{
|
||||
/* We lost sync here; let the app know */
|
||||
lacing_returned++;
|
||||
|
||||
/* we need to tell the codec there's a gap; it might need to
|
||||
handle previous packet dependencies. */
|
||||
packetno++;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* Gather the whole packet. We'll have no holes or a partial packet */
|
||||
{
|
||||
int size=lacing_vals[ptr]&0xff;
|
||||
int bytes=0;
|
||||
|
||||
op.packet_base=body_data;
|
||||
op.packet=body_returned;
|
||||
op.e_o_s=lacing_vals[ptr]&0x200; /* last packet of the stream? */
|
||||
op.b_o_s=lacing_vals[ptr]&0x100; /* first packet of the stream? */
|
||||
bytes+=size;
|
||||
|
||||
while(size==255)
|
||||
{
|
||||
int val=lacing_vals[++ptr];
|
||||
size=val&0xff;
|
||||
if((val&0x200)!=0)op.e_o_s=0x200;
|
||||
bytes+=size;
|
||||
}
|
||||
|
||||
op.packetno=packetno;
|
||||
op.granulepos=granule_vals[ptr];
|
||||
op.bytes=bytes;
|
||||
|
||||
//System.out.println(this+" # body_returned="+body_returned);
|
||||
body_returned+=bytes;
|
||||
//System.out.println(this+"## body_returned="+body_returned);
|
||||
|
||||
lacing_returned=ptr+1;
|
||||
}
|
||||
packetno++;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
// add the incoming page to the stream state; we decompose the page
|
||||
// into packet segments here as well.
|
||||
public int pagein(Page og)
|
||||
{
|
||||
byte[] header_base=og.header_base;
|
||||
int header=og.header;
|
||||
byte[] body_base=og.body_base;
|
||||
int body=og.body;
|
||||
int bodysize=og.body_len;
|
||||
int segptr=0;
|
||||
|
||||
int version=og.version();
|
||||
int continued=og.continued();
|
||||
int bos=og.bos();
|
||||
int eos=og.eos();
|
||||
long granulepos=og.granulepos();
|
||||
int _serialno=og.serialno();
|
||||
int _pageno=og.pageno();
|
||||
int segments=header_base[header+26]&0xff;
|
||||
|
||||
// clean up 'returned data'
|
||||
{
|
||||
int lr=lacing_returned;
|
||||
int br=body_returned;
|
||||
|
||||
// body data
|
||||
|
||||
//System.out.println("br="+br+", body_fill="+body_fill);
|
||||
|
||||
if(br!=0)
|
||||
{
|
||||
body_fill-=br;
|
||||
if(body_fill!=0)
|
||||
{
|
||||
Array.Copy(body_data, br, body_data, 0, body_fill);
|
||||
}
|
||||
body_returned=0;
|
||||
}
|
||||
|
||||
//System.out.println("?? br="+br+", body_fill="+body_fill+" body_returned="+body_returned);
|
||||
|
||||
if(lr!=0)
|
||||
{
|
||||
// segment table
|
||||
if((lacing_fill-lr)!=0)
|
||||
{
|
||||
Array.Copy(lacing_vals, lr, lacing_vals, 0, lacing_fill-lr);
|
||||
Array.Copy(granule_vals, lr, granule_vals, 0, lacing_fill-lr);
|
||||
}
|
||||
lacing_fill-=lr;
|
||||
lacing_packet-=lr;
|
||||
lacing_returned=0;
|
||||
}
|
||||
}
|
||||
|
||||
// check the serial number
|
||||
if(_serialno!=serialno)return(-1);
|
||||
if(version>0)return(-1);
|
||||
|
||||
lacing_expand(segments+1);
|
||||
|
||||
// are we in sequence?
|
||||
if(_pageno!=pageno)
|
||||
{
|
||||
int i;
|
||||
|
||||
// unroll previous partial packet (if any)
|
||||
for(i=lacing_packet;i<lacing_fill;i++)
|
||||
{
|
||||
body_fill-=lacing_vals[i]&0xff;
|
||||
//System.out.println("??");
|
||||
}
|
||||
lacing_fill=lacing_packet;
|
||||
|
||||
// make a note of dropped data in segment table
|
||||
if(pageno!=-1)
|
||||
{
|
||||
lacing_vals[lacing_fill++]=0x400;
|
||||
lacing_packet++;
|
||||
}
|
||||
|
||||
// are we a 'continued packet' page? If so, we'll need to skip
|
||||
// some segments
|
||||
if(continued!=0)
|
||||
{
|
||||
bos=0;
|
||||
for(;segptr<segments;segptr++)
|
||||
{
|
||||
int val=(header_base[header+27+segptr]&0xff);
|
||||
body+=val;
|
||||
bodysize-=val;
|
||||
if(val<255)
|
||||
{
|
||||
segptr++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//System.out.println("bodysize="+bodysize);
|
||||
|
||||
if(bodysize!=0)
|
||||
{
|
||||
body_expand(bodysize);
|
||||
Array.Copy(body_base, body, body_data, body_fill, bodysize);
|
||||
body_fill+=bodysize;
|
||||
}
|
||||
|
||||
//System.out.println("bodyfill="+body_fill);
|
||||
|
||||
{
|
||||
int saved=-1;
|
||||
while(segptr<segments)
|
||||
{
|
||||
int val=(header_base[header+27+segptr]&0xff);
|
||||
lacing_vals[lacing_fill]=val;
|
||||
granule_vals[lacing_fill]=-1;
|
||||
|
||||
if(bos!=0)
|
||||
{
|
||||
lacing_vals[lacing_fill]|=0x100;
|
||||
bos=0;
|
||||
}
|
||||
|
||||
if(val<255)saved=lacing_fill;
|
||||
|
||||
lacing_fill++;
|
||||
segptr++;
|
||||
|
||||
if(val<255)lacing_packet=lacing_fill;
|
||||
}
|
||||
|
||||
/* set the granulepos on the last pcmval of the last full packet */
|
||||
if(saved!=-1)
|
||||
{
|
||||
granule_vals[saved]=granulepos;
|
||||
}
|
||||
}
|
||||
|
||||
if(eos!=0)
|
||||
{
|
||||
e_o_s=1;
|
||||
if(lacing_fill>0)
|
||||
lacing_vals[lacing_fill-1]|=0x200;
|
||||
}
|
||||
|
||||
pageno=_pageno+1;
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
}
|
273
SharpWave/csvorbis/Ogg/SyncState.cs
Normal file
273
SharpWave/csvorbis/Ogg/SyncState.cs
Normal file
@ -0,0 +1,273 @@
|
||||
/* csogg
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace csogg
|
||||
{
|
||||
public class SyncState
|
||||
{
|
||||
public byte[] data;
|
||||
int storage;
|
||||
int fill;
|
||||
int returned;
|
||||
|
||||
int unsynced;
|
||||
int headerbytes;
|
||||
int bodybytes;
|
||||
|
||||
public int clear()
|
||||
{
|
||||
data=null;
|
||||
return(0);
|
||||
}
|
||||
|
||||
// !!!!!!!!!!!!
|
||||
// byte[] buffer(int size){
|
||||
public int buffer(int size)
|
||||
{
|
||||
// first, clear out any space that has been previously returned
|
||||
if(returned!=0)
|
||||
{
|
||||
fill-=returned;
|
||||
if(fill>0)
|
||||
{
|
||||
Array.Copy(data, returned, data, 0, fill);
|
||||
}
|
||||
returned=0;
|
||||
}
|
||||
|
||||
if(size>storage-fill)
|
||||
{
|
||||
// We need to extend the internal buffer
|
||||
int newsize=size+fill+4096; // an extra page to be nice
|
||||
if(data!=null)
|
||||
{
|
||||
byte[] foo=new byte[newsize];
|
||||
Array.Copy(data, 0, foo, 0, data.Length);
|
||||
data=foo;
|
||||
}
|
||||
else
|
||||
{
|
||||
data=new byte[newsize];
|
||||
}
|
||||
storage=newsize;
|
||||
}
|
||||
|
||||
// expose a segment at least as large as requested at the fill mark
|
||||
// return((char *)oy->data+oy->fill);
|
||||
// return(data);
|
||||
return(fill);
|
||||
}
|
||||
|
||||
public int wrote(int bytes)
|
||||
{
|
||||
if(fill+bytes>storage)return(-1);
|
||||
fill+=bytes;
|
||||
return(0);
|
||||
}
|
||||
|
||||
// sync the stream. This is meant to be useful for finding page
|
||||
// boundaries.
|
||||
//
|
||||
// return values for this:
|
||||
// -n) skipped n bytes
|
||||
// 0) page not ready; more data (no bytes skipped)
|
||||
// n) page synced at current location; page length n bytes
|
||||
private Page pageseek_p=new Page();
|
||||
private byte[] chksum=new byte[4];
|
||||
public int pageseek(Page og)
|
||||
{
|
||||
int page=returned;
|
||||
int next;
|
||||
int bytes=fill-returned;
|
||||
|
||||
if(headerbytes==0)
|
||||
{
|
||||
int _headerbytes,i;
|
||||
if(bytes<27)return(0); // not enough for a header
|
||||
|
||||
/* verify capture pattern */
|
||||
//!!!!!!!!!!!
|
||||
if(data[page]!='O' || data[page+1]!='g' || data[page+2]!='g' || data[page+3]!='S')
|
||||
{
|
||||
headerbytes=0;
|
||||
bodybytes=0;
|
||||
|
||||
// search for possible capture
|
||||
next=0;
|
||||
for(int ii=0; ii<bytes-1; ii++)
|
||||
{
|
||||
if(data[page+1+ii]=='O'){next=page+1+ii; break;}
|
||||
}
|
||||
//next=memchr(page+1,'O',bytes-1);
|
||||
if(next==0) next=fill;
|
||||
|
||||
returned=next;
|
||||
return(-(next-page));
|
||||
}
|
||||
_headerbytes=(data[page+26]&0xff)+27;
|
||||
if(bytes<_headerbytes)return(0); // not enough for header + seg table
|
||||
|
||||
// count up body length in the segment table
|
||||
|
||||
for(i=0;i<(data[page+26]&0xff);i++)
|
||||
{
|
||||
bodybytes+=(data[page+27+i]&0xff);
|
||||
}
|
||||
headerbytes=_headerbytes;
|
||||
}
|
||||
|
||||
if(bodybytes+headerbytes>bytes)return(0);
|
||||
|
||||
// The whole test page is buffered. Verify the checksum
|
||||
lock(chksum)
|
||||
{
|
||||
// Grab the checksum bytes, set the header field to zero
|
||||
|
||||
Array.Copy(data, page+22, chksum, 0, 4);
|
||||
data[page+22]=0;
|
||||
data[page+23]=0;
|
||||
data[page+24]=0;
|
||||
data[page+25]=0;
|
||||
|
||||
// set up a temp page struct and recompute the checksum
|
||||
Page log=pageseek_p;
|
||||
log.header_base=data;
|
||||
log.header=page;
|
||||
log.header_len=headerbytes;
|
||||
|
||||
log.body_base=data;
|
||||
log.body=page+headerbytes;
|
||||
log.body_len=bodybytes;
|
||||
log.checksum();
|
||||
|
||||
// Compare
|
||||
if(chksum[0]!=data[page+22] ||
|
||||
chksum[1]!=data[page+23] ||
|
||||
chksum[2]!=data[page+24] ||
|
||||
chksum[3]!=data[page+25])
|
||||
{
|
||||
// D'oh. Mismatch! Corrupt page (or miscapture and not a page at all)
|
||||
// replace the computed checksum with the one actually read in
|
||||
Array.Copy(chksum, 0, data, page+22, 4);
|
||||
// Bad checksum. Lose sync */
|
||||
|
||||
headerbytes=0;
|
||||
bodybytes=0;
|
||||
// search for possible capture
|
||||
next=0;
|
||||
for(int ii=0; ii<bytes-1; ii++)
|
||||
{
|
||||
if(data[page+1+ii]=='O'){next=page+1+ii; break;}
|
||||
}
|
||||
//next=memchr(page+1,'O',bytes-1);
|
||||
if(next==0) next=fill;
|
||||
returned=next;
|
||||
return(-(next-page));
|
||||
}
|
||||
}
|
||||
|
||||
// yes, have a whole page all ready to go
|
||||
{
|
||||
page=returned;
|
||||
|
||||
if(og!=null)
|
||||
{
|
||||
og.header_base=data;
|
||||
og.header=page;
|
||||
og.header_len=headerbytes;
|
||||
og.body_base=data;
|
||||
og.body=page+headerbytes;
|
||||
og.body_len=bodybytes;
|
||||
}
|
||||
|
||||
unsynced=0;
|
||||
returned+=(bytes=headerbytes+bodybytes);
|
||||
headerbytes=0;
|
||||
bodybytes=0;
|
||||
return(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sync the stream and get a page. Keep trying until we find a page.
|
||||
// Supress 'sync errors' after reporting the first.
|
||||
//
|
||||
// return values:
|
||||
// -1) recapture (hole in data)
|
||||
// 0) need more data
|
||||
// 1) page returned
|
||||
//
|
||||
// Returns pointers into buffered data; invalidated by next call to
|
||||
// _stream, _clear, _init, or _buffer
|
||||
|
||||
public int pageout(Page og)
|
||||
{
|
||||
// all we need to do is verify a page at the head of the stream
|
||||
// buffer. If it doesn't verify, we look for the next potential
|
||||
// frame
|
||||
|
||||
while(true)
|
||||
{
|
||||
int ret=pageseek(og);
|
||||
if(ret>0)
|
||||
{
|
||||
// have a page
|
||||
return(1);
|
||||
}
|
||||
if(ret==0)
|
||||
{
|
||||
// need more data
|
||||
return(0);
|
||||
}
|
||||
|
||||
// head did not start a synced page... skipped some bytes
|
||||
if(unsynced==0)
|
||||
{
|
||||
unsynced=1;
|
||||
return(-1);
|
||||
}
|
||||
// loop. keep looking
|
||||
}
|
||||
}
|
||||
|
||||
// clear things to an initial state. Good to call, eg, before seeking
|
||||
public int reset()
|
||||
{
|
||||
fill=0;
|
||||
returned=0;
|
||||
unsynced=0;
|
||||
headerbytes=0;
|
||||
bodybytes=0;
|
||||
return(0);
|
||||
}
|
||||
public void init(){}
|
||||
|
||||
public SyncState()
|
||||
{
|
||||
// No constructor needed
|
||||
}
|
||||
}
|
||||
}
|
290
SharpWave/csvorbis/StaticCodeBook.cs
Normal file
290
SharpWave/csvorbis/StaticCodeBook.cs
Normal file
@ -0,0 +1,290 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using csogg;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
class StaticCodeBook
|
||||
{
|
||||
internal int dim; // codebook dimensions (elements per vector)
|
||||
internal int entries; // codebook entries
|
||||
internal int[] lengthlist; // codeword lengths in bits
|
||||
|
||||
// mapping
|
||||
internal int maptype; // 0 = none
|
||||
// 1 = implicitly populated values from map column
|
||||
// 2 = listed arbitrary values
|
||||
|
||||
// The below does a linear, single monotonic sequence mapping.
|
||||
internal int q_min; // packed 32 bit float; quant value 0 maps to minval
|
||||
internal int q_delta; // packed 32 bit float; val 1 - val 0 = = delta
|
||||
internal int q_quant; // bits: 0 < quant <= 16
|
||||
internal int q_sequencep; // bitflag
|
||||
|
||||
// additional information for log (dB) mapping; the linear mapping
|
||||
// is assumed to actually be values in dB. encodebias is used to
|
||||
// assign an error weight to 0 dB. We have two additional flags:
|
||||
// zeroflag indicates if entry zero is to represent -Inf dB; negflag
|
||||
// indicates if we're to represent negative linear values in a
|
||||
// mirror of the positive mapping.
|
||||
|
||||
internal int[] quantlist; // map = = 1: (int)(entries/dim) element column map
|
||||
// map = = 2: list of dim*entries quantized entry vals
|
||||
|
||||
internal StaticCodeBook(){}
|
||||
internal StaticCodeBook(int dim, int entries, int[] lengthlist,
|
||||
int maptype, int q_min, int q_delta,
|
||||
int q_quant, int q_sequencep, int[] quantlist,
|
||||
//EncodeAuxNearestmatch nearest_tree,
|
||||
Object nearest_tree,
|
||||
// EncodeAuxThreshmatch thresh_tree,
|
||||
Object thresh_tree
|
||||
) : this()
|
||||
{
|
||||
this.dim = dim; this.entries = entries; this.lengthlist = lengthlist;
|
||||
this.maptype = maptype; this.q_min = q_min; this.q_delta = q_delta;
|
||||
this.q_quant = q_quant; this.q_sequencep = q_sequencep;
|
||||
this.quantlist = quantlist;
|
||||
}
|
||||
|
||||
// unpacks a codebook from the packet buffer into the codebook struct,
|
||||
// readies the codebook auxiliary structures for decode
|
||||
internal int unpack(csBuffer opb)
|
||||
{
|
||||
int i;
|
||||
opb.read(24);
|
||||
// first the basic parameters
|
||||
dim = opb.read(16);
|
||||
entries = opb.read(24);
|
||||
|
||||
// codeword ordering.... length ordered or unordered?
|
||||
switch(opb.read(1))
|
||||
{
|
||||
case 0:
|
||||
// unordered
|
||||
lengthlist = new int[entries];
|
||||
|
||||
// allocated but unused entries?
|
||||
if(opb.read(1) != 0)
|
||||
{
|
||||
// yes, unused entries
|
||||
|
||||
for(i = 0;i<entries;i++)
|
||||
{
|
||||
if(opb.read(1) != 0)
|
||||
{
|
||||
int num = opb.read(5);
|
||||
lengthlist[i] = num+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
lengthlist[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// all entries used; no tagging
|
||||
for(i = 0;i<entries;i++)
|
||||
{
|
||||
int num = opb.read(5);
|
||||
lengthlist[i] = num+1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// ordered
|
||||
{
|
||||
int length = opb.read(5)+1;
|
||||
lengthlist = new int[entries];
|
||||
|
||||
for(i = 0;i<entries;)
|
||||
{
|
||||
int num = opb.read(VUtils.ilog(entries-i));
|
||||
for(int j = 0;j<num;j++,i++)
|
||||
{
|
||||
lengthlist[i] = length;
|
||||
}
|
||||
length++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// EOF
|
||||
return(-1);
|
||||
}
|
||||
|
||||
// Do we have a mapping to unpack?
|
||||
switch((maptype = opb.read(4)))
|
||||
{
|
||||
case 0:
|
||||
// no mapping
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
// implicitly populated value mapping
|
||||
// explicitly populated value mapping
|
||||
q_min = opb.read(32);
|
||||
q_delta = opb.read(32);
|
||||
q_quant = opb.read(4)+1;
|
||||
q_sequencep = opb.read(1);
|
||||
|
||||
{
|
||||
int quantvals = 0;
|
||||
switch(maptype)
|
||||
{
|
||||
case 1:
|
||||
quantvals = maptype1_quantvals();
|
||||
break;
|
||||
case 2:
|
||||
quantvals = entries*dim;
|
||||
break;
|
||||
}
|
||||
|
||||
// quantized values
|
||||
quantlist = new int[quantvals];
|
||||
for(i = 0;i<quantvals;i++)
|
||||
quantlist[i] = opb.read(q_quant);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return(-1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
// there might be a straightforward one-line way to do the below
|
||||
// that's portable and totally safe against roundoff, but I haven't
|
||||
// thought of it. Therefore, we opt on the side of caution
|
||||
internal int maptype1_quantvals()
|
||||
{
|
||||
int vals = (int)(Math.Floor(Math.Pow(entries,1.0/dim)));
|
||||
|
||||
// the above *should* be reliable, but we'll not assume that FP is
|
||||
// ever reliable when bitstream sync is at stake; verify via integer
|
||||
// means that vals really is the greatest value of dim for which
|
||||
// vals^b->bim <= b->entries
|
||||
// treat the above as an initial guess
|
||||
while(true)
|
||||
{
|
||||
int acc = 1;
|
||||
int acc1 = 1;
|
||||
for(int i = 0;i<dim;i++)
|
||||
{
|
||||
acc *= vals;
|
||||
acc1 *= vals + 1;
|
||||
}
|
||||
if(acc <= entries && acc1>entries){ return(vals); }
|
||||
else
|
||||
{
|
||||
if(acc>entries){ vals--; }
|
||||
else{ vals++; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unpack the quantized list of values for encode/decode
|
||||
// we need to deal with two map types: in map type 1, the values are
|
||||
// generated algorithmically (each column of the vector counts through
|
||||
// the values in the quant vector). in map type 2, all the values came
|
||||
// in in an explicit list. Both value lists must be unpacked
|
||||
internal float[] unquantize()
|
||||
{
|
||||
|
||||
if(maptype == 1 || maptype == 2)
|
||||
{
|
||||
int quantvals;
|
||||
float mindel = float32_unpack(q_min);
|
||||
float delta = float32_unpack(q_delta);
|
||||
float[] r = new float[entries*dim];
|
||||
|
||||
//System.err.println("q_min = "+q_min+", mindel = "+mindel);
|
||||
|
||||
// maptype 1 and 2 both use a quantized value vector, but
|
||||
// different sizes
|
||||
switch(maptype)
|
||||
{
|
||||
case 1:
|
||||
// most of the time, entries%dimensions = = 0, but we need to be
|
||||
// well defined. We define that the possible vales at each
|
||||
// scalar is values = = entries/dim. If entries%dim != 0, we'll
|
||||
// have 'too few' values (values*dim<entries), which means that
|
||||
// we'll have 'left over' entries; left over entries use zeroed
|
||||
// values (and are wasted). So don't generate codebooks like that
|
||||
quantvals = maptype1_quantvals();
|
||||
for(int j = 0;j<entries;j++)
|
||||
{
|
||||
float last = 0.0f;
|
||||
int indexdiv = 1;
|
||||
for(int k = 0;k<dim;k++)
|
||||
{
|
||||
int index = (j/indexdiv)%quantvals;
|
||||
float val = quantlist[index];
|
||||
val = Math.Abs(val)*delta+mindel+last;
|
||||
if(q_sequencep != 0)last = val;
|
||||
r[j*dim+k] = val;
|
||||
indexdiv *= quantvals;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for(int j = 0;j<entries;j++)
|
||||
{
|
||||
float last = 0.0f;
|
||||
for(int k = 0;k<dim;k++)
|
||||
{
|
||||
float val = quantlist[j*dim+k];
|
||||
val = Math.Abs(val)*delta+mindel+last;
|
||||
if(q_sequencep != 0)last = val;
|
||||
r[j*dim+k] = val;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return(r);
|
||||
}
|
||||
return(null);
|
||||
}
|
||||
|
||||
internal static float float32_unpack(int val)
|
||||
{
|
||||
float mant = val&0x1fffff;
|
||||
float sign = val&0x80000000;
|
||||
float exp = (uint)(val&0x7fe00000) >> 21;
|
||||
if((val&0x80000000) != 0)
|
||||
mant = -mant;
|
||||
return ldexp(mant, (int)exp - 788);
|
||||
}
|
||||
|
||||
internal static float ldexp(float foo, int e)
|
||||
{
|
||||
return (float)(foo*Math.Pow(2, e));
|
||||
}
|
||||
}
|
||||
}
|
58
SharpWave/csvorbis/VUtils.cs
Normal file
58
SharpWave/csvorbis/VUtils.cs
Normal file
@ -0,0 +1,58 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace csvorbis {
|
||||
|
||||
public static class VUtils {
|
||||
|
||||
public static int ilog(int v) {
|
||||
int ret = 0;
|
||||
while(v != 0) {
|
||||
ret++;
|
||||
v = (int)((uint)v >> 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int ilog2(int v) {
|
||||
int ret = 0;
|
||||
while(v > 1) {
|
||||
ret++;
|
||||
v = (int)((uint)v >> 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int icount(int v) {
|
||||
int ret = 0;
|
||||
while(v != 0) {
|
||||
ret += (v&1);
|
||||
v = (int)((uint)v >> 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
269
SharpWave/csvorbis/VorbisCodec.cs
Normal file
269
SharpWave/csvorbis/VorbisCodec.cs
Normal file
@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using csogg;
|
||||
using csvorbis;
|
||||
using SharpWave.Containers;
|
||||
|
||||
namespace SharpWave.Codecs.Vorbis {
|
||||
|
||||
public sealed class OggContainer : IMediaContainer {
|
||||
|
||||
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() {
|
||||
chunk = new AudioChunk();
|
||||
}
|
||||
|
||||
AudioChunk chunk, rawChunk;
|
||||
Stream input;
|
||||
byte[] rawPcm;
|
||||
int rawIndex;
|
||||
|
||||
SyncState oy = new SyncState(); // sync and verify incoming physical bitstream
|
||||
StreamState os = new StreamState(); // take physical pages, weld into a logical stream of packets
|
||||
Page og = new Page(); // one Ogg bitstream page. Vorbis packets are inside
|
||||
Packet op = new Packet(); // one raw packet of data for decode
|
||||
|
||||
Info vi = new Info(); // struct that stores all the static vorbis bitstream settings
|
||||
Comment vc = new Comment(); // struct that stores all the bitstream user comments
|
||||
DspState vd = new DspState(); // central working state for the packet->PCM decoder
|
||||
Block vb = new Block(); // local working space for packet->PCM decode
|
||||
|
||||
byte[] buffer;
|
||||
int bytes = 0;
|
||||
|
||||
public AudioFormat ReadHeader(Stream source) {
|
||||
input = source;
|
||||
oy.init(); // Now we can read pages
|
||||
|
||||
// grab some data at the head of the stream. We want the first page
|
||||
// (which is guaranteed to be small and only contain the Vorbis
|
||||
// stream initial header) We need the first page to get the stream serialno.
|
||||
|
||||
// submit a 4k block to libvorbis' Ogg layer
|
||||
int index = oy.buffer(4096);
|
||||
buffer = oy.data;
|
||||
bytes = input.Read(buffer, index, 4096);
|
||||
oy.wrote(bytes);
|
||||
|
||||
// Get the first page.
|
||||
if (oy.pageout(og) != 1) {
|
||||
// have we simply run out of data? If so, we're done.
|
||||
if (bytes < 4096) return default(AudioFormat);
|
||||
}
|
||||
|
||||
// Get the serial number and set up the rest of decode.
|
||||
// serialno first; use it to set up a logical stream
|
||||
os.init(og.serialno());
|
||||
|
||||
// extract the initial header from the first page and verify that the
|
||||
// Ogg bitstream is in fact Vorbis data
|
||||
|
||||
// I handle the initial header first instead of just having the code
|
||||
// read all three Vorbis headers at once because reading the initial
|
||||
// header is an easy way to identify a Vorbis bitstream and it's
|
||||
// useful to see that functionality seperated out.
|
||||
|
||||
vi.init();
|
||||
vc.init();
|
||||
os.pagein(og);
|
||||
os.packetout(op);
|
||||
vi.synthesis_headerin(vc, op);
|
||||
|
||||
// At this point, we're sure we're Vorbis. We've set up the logical
|
||||
// (Ogg) bitstream decoder. Get the comment and codebook headers and
|
||||
// set up the Vorbis decoder
|
||||
|
||||
// The next two packets in order are the comment and codebook headers.
|
||||
// They're likely large and may span multiple pages. Thus we reead
|
||||
// and submit data until we get our two packets, watching that no
|
||||
// pages are missing. If a page is missing, error out; losing a
|
||||
// header page is the only place where missing data is fatal. */
|
||||
|
||||
int i = 0;
|
||||
while (i < 2) {
|
||||
while (i < 2) {
|
||||
int result = oy.pageout(og);
|
||||
if (result == 0) break; // Need more data
|
||||
// Don't complain about missing or corrupt data yet. We'll
|
||||
// catch it at the packet output phase
|
||||
|
||||
if (result == 1) {
|
||||
os.pagein(og); // we can ignore any errors here
|
||||
// as they'll also become apparent
|
||||
// at packetout
|
||||
while (i < 2) {
|
||||
result = os.packetout(op);
|
||||
if (result == 0) break;
|
||||
vi.synthesis_headerin(vc, op);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no harm in not checking before adding more
|
||||
index = oy.buffer(4096);
|
||||
buffer = oy.data;
|
||||
bytes = input.Read(buffer, index, 4096);
|
||||
oy.wrote(bytes);
|
||||
}
|
||||
|
||||
// OK, got and parsed all three headers. Initialize the Vorbis
|
||||
// packet->PCM decoder.
|
||||
vd.synthesis_init(vi); // central decode state
|
||||
vb.init(vd); // local state for most of the decode
|
||||
|
||||
AudioFormat format;
|
||||
format.Channels = vi.channels;
|
||||
format.BitsPerSample = 16;
|
||||
format.SampleRate = vi.rate;
|
||||
return format;
|
||||
}
|
||||
|
||||
public IEnumerable<AudioChunk> StreamData( Stream source ) {
|
||||
// the original iterator may not always return enough samples,
|
||||
// so we will do our own buffering here.
|
||||
|
||||
rawChunk = new AudioChunk();
|
||||
foreach( AudioChunk chunk in StreamDataCore( source ) ) {
|
||||
if( rawPcm == null )
|
||||
InitRaw( chunk );
|
||||
if( rawIndex + chunk.Length > rawPcm.Length )
|
||||
ResizeRaw( rawIndex + chunk.Length );
|
||||
|
||||
Buffer.BlockCopy( chunk.Data, 0, rawPcm, rawIndex, chunk.Length );
|
||||
rawIndex += chunk.Length;
|
||||
if( rawIndex >= (vi.rate / 4) ) {
|
||||
rawChunk.Length = rawIndex;
|
||||
rawIndex = 0;
|
||||
yield return rawChunk;
|
||||
}
|
||||
}
|
||||
|
||||
rawChunk.Length = rawIndex;
|
||||
yield return rawChunk;
|
||||
yield break;
|
||||
}
|
||||
|
||||
void InitRaw(AudioChunk chunk) {
|
||||
rawPcm = new byte[vi.rate / 4];
|
||||
rawChunk.Data = rawPcm;
|
||||
rawChunk.Length = rawPcm.Length;
|
||||
}
|
||||
|
||||
void ResizeRaw(int newLen) {
|
||||
byte[] oldPcm = rawPcm;
|
||||
rawPcm = new byte[rawIndex + chunk.Length];
|
||||
Buffer.BlockCopy( oldPcm, 0, rawPcm, 0, rawIndex );
|
||||
rawChunk.Data = rawPcm;
|
||||
rawChunk.Length = rawPcm.Length;
|
||||
}
|
||||
|
||||
IEnumerable<AudioChunk> StreamDataCore( Stream source ) {
|
||||
input = source;
|
||||
int convsize = 4096 * 2;
|
||||
byte[] convbuffer = new byte[convsize]; // take 8k out of the data segment, not the stack
|
||||
convsize = 4096 / vi.channels;
|
||||
oy.init(); // Now we can read pages
|
||||
|
||||
int eos = 0;
|
||||
// so multiple block decodes can
|
||||
// proceed in parallel. We could init
|
||||
// multiple vorbis_block structures for vd here
|
||||
|
||||
float[][] pcm = null;
|
||||
int[] _index = new int[vi.channels];
|
||||
// The rest is just a straight decode loop until end of stream
|
||||
|
||||
while (eos == 0) {
|
||||
while (eos == 0) {
|
||||
int result = oy.pageout(og);
|
||||
if (result == 0) break; // need more data
|
||||
|
||||
os.pagein(og); // can safely ignore errors at this point
|
||||
while (true) {
|
||||
result = os.packetout(op);
|
||||
|
||||
if (result == 0) break; // need more data
|
||||
if (result == -1) continue;
|
||||
// missing or corrupt data at this page position
|
||||
// no reason to complain; already complained above
|
||||
|
||||
// we have a packet. Decode it
|
||||
int samples;
|
||||
if (vb.synthesis(op) == 0) { // test for success!
|
||||
vd.synthesis_blockin(vb);
|
||||
}
|
||||
|
||||
// **pcm is a multichannel float vector. In stereo, for
|
||||
// example, pcm[0] is left, and pcm[1] is right. samples is
|
||||
// the size of each channel. Convert the float values
|
||||
// (-1.<=range<=1.) to whatever PCM format and write it out
|
||||
|
||||
while ((samples = vd.synthesis_pcmout(ref pcm, _index)) > 0) {
|
||||
int bout = (samples < convsize ? samples : convsize);
|
||||
|
||||
// convert floats to 16 bit signed ints (host order) and interleave
|
||||
for (int ch = 0; ch < vi.channels; ch++) {
|
||||
int ptr = ch * 2;
|
||||
int offset = _index[ch];
|
||||
float[] chPcm = pcm[ch];
|
||||
|
||||
for (int j = 0; j < bout; j++) {
|
||||
int val = (int)(chPcm[offset + j] * 32767);
|
||||
if (val > 32767) val = 32767;
|
||||
if (val < -32768) val = -32768;
|
||||
if (val < 0) val = val | 0x8000;
|
||||
|
||||
convbuffer[ptr] = (byte)(val);
|
||||
convbuffer[ptr + 1] = (byte)((uint)val >> 8);
|
||||
ptr += 2 * (vi.channels);
|
||||
}
|
||||
}
|
||||
|
||||
chunk.Data = convbuffer;
|
||||
chunk.Length = 2 * vi.channels * bout;
|
||||
vd.synthesis_read(bout);
|
||||
yield return chunk;
|
||||
}
|
||||
}
|
||||
if (og.eos() != 0) eos = 1;
|
||||
}
|
||||
|
||||
if (eos == 0) {
|
||||
int index = oy.buffer(4096);
|
||||
buffer = oy.data;
|
||||
bytes = input.Read(buffer, index, 4096);
|
||||
oy.wrote(bytes);
|
||||
if (bytes == 0) eos = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// clean up this logical bitstream; before exit we see if we're
|
||||
// followed by another [chained]
|
||||
os.clear();
|
||||
|
||||
// ogg_page and ogg_packet structs always point to storage in
|
||||
// libvorbis. They're never freed or manipulated directly
|
||||
vi.clear(); // must be called last
|
||||
|
||||
// OK, clean up the framer
|
||||
oy.clear();
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
36
SharpWave/csvorbis/csorbisException.cs
Normal file
36
SharpWave/csvorbis/csorbisException.cs
Normal file
@ -0,0 +1,36 @@
|
||||
/* csvorbis
|
||||
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
||||
*
|
||||
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
||||
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
||||
*
|
||||
* Thanks go to the JOrbis team, for licencing the code under the
|
||||
* LGPL, making my job a lot easier.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace csvorbis
|
||||
{
|
||||
public class csorbisException : Exception
|
||||
{
|
||||
public csorbisException ()
|
||||
:base(){}
|
||||
public csorbisException (String s)
|
||||
:base("csorbis: "+s){}
|
||||
}
|
||||
}
|
@ -117,10 +117,11 @@ ReturnCode Platform_HttpFree(void);
|
||||
struct AudioFormat { UInt8 Channels, BitsPerSample; UInt16 Frequency };
|
||||
#define AudioFormat_Eq(a, b) (a->Channels == b->Channels && a->BitsPerSample == b->BitsPerSample && a->Frequency == b->Frequency)
|
||||
|
||||
void Platform_AudioInit(Int32* handle, UInt8 buffers);
|
||||
void Platform_AudioInit(Int32* handle, Int32 buffers);
|
||||
void Platform_AudioFree(Int32 handle);
|
||||
struct AudioFormat* Platform_AudioGetFormat(Int32 handle);
|
||||
void Platform_AudioSetFormat(Int32 handle, struct AudioFormat* format);
|
||||
void Platform_AudioPlayAsync(Int32 handle, void* data, UInt32 dataSize);
|
||||
Int32 Platform_AudioNextFinishedAsync(Int32 handle);
|
||||
bool Platform_AudioFinishedAsync(Int32 handle);
|
||||
void Platform_AudioPlayData(Int32 handle, Int32 idx, void* data, UInt32 dataSize);
|
||||
bool Platform_AudioIsCompleted(Int32 handle, Int32 idx);
|
||||
bool Platform_AudioIsFinished(Int32 handle);
|
||||
#endif
|
||||
|
@ -604,18 +604,22 @@ struct AudioContext {
|
||||
HWAVEOUT Handle;
|
||||
WAVEHDR Headers[AUDIO_MAX_CHUNKS];
|
||||
struct AudioFormat Format;
|
||||
UInt8 NumBuffers, PlayingAsync;
|
||||
Int32 NumBuffers;
|
||||
};
|
||||
struct AudioContext Audio_Contexts[20];
|
||||
|
||||
void Platform_AudioInit(Int32* handle, UInt8 buffers) {
|
||||
Int32 i;
|
||||
void Platform_AudioInit(Int32* handle, Int32 buffers) {
|
||||
Int32 i, j;
|
||||
for (i = 0; i < Array_Elems(Audio_Contexts); i++) {
|
||||
struct AudioContext* ctx = &Audio_Contexts[i];
|
||||
if (ctx->NumBuffers) continue;
|
||||
|
||||
ctx->NumBuffers = buffers;
|
||||
*handle = i; return;
|
||||
|
||||
*handle = i;
|
||||
for (j = 0; j < buffers; j++) {
|
||||
ctx->Headers[j].dwFlags = WHDR_DONE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
ErrorHandler_Fail("No free audio contexts");
|
||||
}
|
||||
@ -629,6 +633,11 @@ void Platform_AudioFree(Int32 handle) {
|
||||
ErrorHandler_CheckOrFail(result, "Audio - closing device");
|
||||
}
|
||||
|
||||
struct AudioFormat* Platform_AudioGetFormat(Int32 handle) {
|
||||
struct AudioContext* ctx = &Audio_Contexts[handle];
|
||||
return &ctx->Format;
|
||||
}
|
||||
|
||||
void Platform_AudioSetFormat(Int32 handle, struct AudioFormat* format) {
|
||||
struct AudioContext* ctx = &Audio_Contexts[handle];
|
||||
struct AudioFormat* cur = &ctx->Format;
|
||||
@ -646,19 +655,18 @@ void Platform_AudioSetFormat(Int32 handle, struct AudioFormat* format) {
|
||||
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
|
||||
|
||||
if (waveOutGetNumDevs() == 0u) ErrorHandler_Fail("No audio devices found");
|
||||
ReturnCode result = waveOutOpen(&ctx->Handle, UInt32_MaxValue, &fmt, NULL, NULL, CALLBACK_NULL);
|
||||
ReturnCode result = waveOutOpen(&ctx->Handle, WAVE_MAPPER, &fmt, NULL, NULL, CALLBACK_NULL);
|
||||
ErrorHandler_CheckOrFail(result, "Audio - opening device");
|
||||
}
|
||||
|
||||
void Platform_AudioPlayAsync(Int32 handle, void* data, UInt32 dataSize) {
|
||||
void Platform_AudioPlayData(Int32 handle, Int32 idx, void* data, UInt32 dataSize) {
|
||||
struct AudioContext* ctx = &Audio_Contexts[handle];
|
||||
WAVEHDR* hdr = &ctx->Headers[0];
|
||||
WAVEHDR* hdr = &ctx->Headers[idx];
|
||||
Platform_MemSet(hdr, 0, sizeof(WAVEHDR));
|
||||
|
||||
hdr->lpData = data;
|
||||
hdr->dwBufferLength = dataSize;
|
||||
hdr->dwLoops = 1;
|
||||
ctx->PlayingAsync = true;
|
||||
|
||||
ReturnCode result = waveOutPrepareHeader(ctx->Handle, hdr, sizeof(WAVEHDR));
|
||||
ErrorHandler_CheckOrFail(result, "Audio - prepare header");
|
||||
@ -666,29 +674,24 @@ void Platform_AudioPlayAsync(Int32 handle, void* data, UInt32 dataSize) {
|
||||
ErrorHandler_CheckOrFail(result, "Audio - write header");
|
||||
}
|
||||
|
||||
Int32 Platform_AudioNextFinishedAsync(Int32 handle) {
|
||||
bool Platform_AudioIsCompleted(Int32 handle, Int32 idx) {
|
||||
struct AudioContext* ctx = &Audio_Contexts[handle];
|
||||
WAVEHDR* hdr = &ctx->Headers[idx];
|
||||
if (!(hdr->dwFlags & WHDR_DONE)) return false;
|
||||
|
||||
if (hdr->dwFlags & WHDR_PREPARED) {
|
||||
ReturnCode result = waveOutUnprepareHeader(ctx->Handle, hdr, sizeof(WAVEHDR));
|
||||
ErrorHandler_CheckOrFail(result, "Audio - unprepare header");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Platform_AudioIsFinished(Int32 handle) {
|
||||
struct AudioContext* ctx = &Audio_Contexts[handle];
|
||||
Int32 i;
|
||||
for (i = 0; i < ctx->NumBuffers; i++) {
|
||||
WAVEHDR* hdr = &ctx->Headers[i];
|
||||
if (!(hdr->dwFlags & WHDR_DONE)) continue;
|
||||
|
||||
if (hdr->dwFlags & WHDR_PREPARED) {
|
||||
ReturnCode result = waveOutUnprepareHeader(ctx->Handle, hdr, sizeof(WAVEHDR));
|
||||
ErrorHandler_CheckOrFail(result, "Audio - unprepare header");
|
||||
}
|
||||
return i;
|
||||
if (!Platform_AudioIsCompleted(handle, i)) return false;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Platform_AudioFinishedAsync(Int32 handle) {
|
||||
struct AudioContext* ctx = &Audio_Contexts[handle];
|
||||
if (!ctx->PlayingAsync) return true;
|
||||
Int32 index = Platform_AudioNextFinishedAsync(handle);
|
||||
|
||||
if (index >= 0) return false;
|
||||
ctx->PlayingAsync = false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user