rewrite sharpwave somewhat

This commit is contained in:
UnknownShadow200 2018-08-06 01:04:05 +10:00
parent daaa68cea2
commit 36c425906f
35 changed files with 4693 additions and 206 deletions

View File

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

View File

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

View File

@ -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();

View File

@ -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) {

View File

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

View File

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

View File

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

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

View 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.*")]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View 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){}
}
}

View File

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

View File

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