(caf7e6a2e) Replaced Concentus NuGet package with csproj (ensures correct System.Runtime references)

This commit is contained in:
Joonas Rikkonen
2019-04-29 21:11:59 +03:00
parent f10adc2612
commit 0b1b39d70a
149 changed files with 47171 additions and 70 deletions

View File

@@ -60,9 +60,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Concentus, Version=1.1.6.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\Concentus.1.1.7\lib\portable-net45+win+wpa81+wp80\Concentus.dll</HintPath>
</Reference>
<Reference Include="GameAnalytics.Mono, Version=1.0.6710.29255, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\GameAnalytics.Mono.dll</HintPath>
</Reference>
@@ -132,6 +129,10 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.csproj">
<Project>{0e7fee6a-15e5-4a4e-943c-80276003478c}</Project>
<Name>Concentus</Name>
</ProjectReference>
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.csproj">
<Project>{3af0347c-5a9b-4421-868c-8ee3dbfaebc6}</Project>
<Name>Facepunch.Steamworks</Name>

View File

@@ -59,9 +59,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Concentus, Version=1.1.6.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\Concentus.1.1.7\lib\portable-net45+win+wpa81+wp80\Concentus.dll</HintPath>
</Reference>
<Reference Include="GameAnalytics.Mono, Version=1.0.6710.29255, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\GameAnalytics.Mono.dll</HintPath>
</Reference>
@@ -131,6 +128,10 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.csproj">
<Project>{0e7fee6a-15e5-4a4e-943c-80276003478c}</Project>
<Name>Concentus</Name>
</ProjectReference>
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.csproj">
<Project>{3af0347c-5a9b-4421-868c-8ee3dbfaebc6}</Project>
<Name>Facepunch.Steamworks</Name>

View File

@@ -108,6 +108,61 @@ namespace Barotrauma
}
}
//unconscious/dead characters can't correct their position using AnimController movement
// -> we need to correct it manually
if (!character.AllowInput)
{
float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition);
float mainLimbErrorTolerance = 0.1f;
//if the main limb is roughly at the correct position and the collider isn't moving (much at least),
//don't attempt to correct the position.
if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f)
{
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
MainLimb.PullJointEnabled = true;
}
character.SelectedConstruction = character.MemState[0].SelectedItem;
}
if (character.MemState[0].Animation == AnimController.Animation.CPR)
{
character.AnimController.Anim = AnimController.Animation.CPR;
}
else if (character.AnimController.Anim == AnimController.Animation.CPR)
{
character.AnimController.Anim = AnimController.Animation.None;
}
Vector2 newVelocity = Collider.LinearVelocity;
Vector2 newPosition = Collider.SimPosition;
float newRotation = Collider.Rotation;
float newAngularVelocity = Collider.AngularVelocity;
Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity);
newVelocity = newVelocity.ClampLength(100.0f);
if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; }
overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero;
Collider.LinearVelocity = newVelocity;
Collider.AngularVelocity = newAngularVelocity;
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
float errorTolerance = character.AllowInput ? 0.01f : 0.2f;
if (distSqrd > errorTolerance)
{
if (distSqrd > 10.0f || !character.AllowInput)
{
Collider.TargetRotation = newRotation;
SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false);
}
else
{
Collider.TargetRotation = newRotation;
Collider.TargetPosition = newPosition;
Collider.MoveToTargetPosition(true);
}
}
//unconscious/dead characters can't correct their position using AnimController movement
// -> we need to correct it manually
if (!character.AllowInput)
@@ -151,32 +206,34 @@ namespace Barotrauma
}
}
if (character.MemLocalState.Count > 120) character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120);
character.MemState.Clear();
character.MemLocalState.Clear();
}
}
partial void ImpactProjSpecific(float impact, Body body)
{
float volume = MathHelper.Clamp(impact - 3.0f, 0.5f, 1.0f);
if (body.UserData is Limb limb && character.Stun <= 0f)
else
{
if (impact > 3.0f) { PlayImpactSound(limb); }
}
else if (body.UserData is Limb || body == Collider.FarseerBody)
{
if (!character.IsRemotePlayer && impact > ImpactTolerance)
//remove states with a timestamp (there may still timestamp-based states
//in the list if the controlled character switches from timestamp-based interpolation to ID-based)
character.MemState.RemoveAll(m => m.Timestamp > 0.0f);
for (int i = 0; i < character.MemLocalState.Count; i++)
{
SoundPlayer.PlayDamageSound("LimbBlunt", strongestImpact, Collider);
if (character.Submarine == null)
{
//transform in-sub coordinates to outside coordinates
if (character.MemLocalState[i].Position.Y > lowestSubPos)
{
character.MemLocalState[i].TransformInToOutside();
}
}
else if (currentHull?.Submarine != null)
{
//transform outside coordinates to in-sub coordinates
if (character.MemLocalState[i].Position.Y < lowestSubPos)
{
character.MemLocalState[i].TransformOutToInside(currentHull.Submarine);
}
}
}
}
if (Character.Controlled == character)
{
GameMain.GameScreen.Cam.Shake = Math.Min(Math.Max(strongestImpact, GameMain.GameScreen.Cam.Shake), 3.0f);
}
}
if (character.MemState.Count < 1) return;

View File

@@ -58,9 +58,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Concentus, Version=1.1.6.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\Concentus.1.1.7\lib\portable-net45+win+wpa81+wp80\Concentus.dll</HintPath>
</Reference>
<Reference Include="GameAnalytics.Mono, Version=1.0.6710.29255, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\GameAnalytics.Mono.dll</HintPath>
</Reference>
@@ -215,6 +212,10 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Libraries\Concentus\CSharp\Concentus\Concentus.csproj">
<Project>{0e7fee6a-15e5-4a4e-943c-80276003478c}</Project>
<Name>Concentus</Name>
</ProjectReference>
<ProjectReference Include="..\..\Libraries\Facepunch.Steamworks\Facepunch.Steamworks.csproj">
<Project>{3af0347c-5a9b-4421-868c-8ee3dbfaebc6}</Project>
<Name>Facepunch.Steamworks</Name>

View File

@@ -1,35 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.MediaFoundation" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.XAudio2" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.Direct3D11" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.Direct2D1" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.XInput" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.MediaFoundation" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.XAudio2" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.Direct3D11" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.Direct2D1" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.XInput" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Concentus" version="1.1.7" targetFramework="net45" />
<package id="GameAnalytics.Mono.SDK" version="1.1.12" targetFramework="net45" />
<package id="Microsoft.NETCore.Platforms" version="3.0.0-preview.19073.11" targetFramework="net45" />
<package id="MonoGame.Framework.DesktopGL" version="3.7.1.189" targetFramework="net45" />

View File

@@ -1149,10 +1149,6 @@ namespace Barotrauma
{
body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation);
}
else if (Submarine != null && prevSub != null && Submarine != prevSub)
{
body.SetTransform(body.SimPosition + prevSub.SimPosition - Submarine.SimPosition, body.Rotation);
}
Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition);
rect.X = (int)(displayPos.X - rect.Width / 2.0f);

View File

@@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MacClient", "Barotrauma\Bar
{85232B20-074D-4723-B0C6-91495391E448} = {85232B20-074D-4723-B0C6-91495391E448}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Concentus", "Libraries\Concentus\CSharp\Concentus\Concentus.csproj", "{777A5414-CAE5-4011-96DF-C9661985917E}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
Barotrauma\BarotraumaClient\ClientCode.projitems*{008c0f83-e914-4966-9135-ea885059edd8}*SharedItemsImports = 4
@@ -81,12 +83,16 @@ Global
Barotrauma\BarotraumaShared\SharedContent.projitems*{d7f9fdd3-af03-46ad-a2c2-f590899712b7}*SharedItemsImports = 4
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
DebugLinux|Any CPU = DebugLinux|Any CPU
DebugLinux|x64 = DebugLinux|x64
DebugMac|Any CPU = DebugMac|Any CPU
DebugMac|x64 = DebugMac|x64
DebugWindows|Any CPU = DebugWindows|Any CPU
DebugWindows|x64 = DebugWindows|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
ReleaseLinux|Any CPU = ReleaseLinux|Any CPU
ReleaseLinux|x64 = ReleaseLinux|x64
ReleaseMac|Any CPU = ReleaseMac|Any CPU
@@ -95,6 +101,10 @@ Global
ReleaseWindows|x64 = ReleaseWindows|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{008C0F83-E914-4966-9135-EA885059EDD8}.Debug|Any CPU.ActiveCfg = ReleaseWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.Debug|Any CPU.Build.0 = ReleaseWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.Debug|x64.ActiveCfg = DebugWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.Debug|x64.Build.0 = DebugWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.DebugLinux|Any CPU.ActiveCfg = DebugWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.DebugLinux|Any CPU.Build.0 = DebugWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.DebugLinux|x64.ActiveCfg = DebugWindows|x64
@@ -104,6 +114,10 @@ Global
{008C0F83-E914-4966-9135-EA885059EDD8}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.DebugWindows|x64.ActiveCfg = DebugWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.DebugWindows|x64.Build.0 = DebugWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.Release|Any CPU.ActiveCfg = ReleaseWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.Release|Any CPU.Build.0 = ReleaseWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.Release|x64.ActiveCfg = ReleaseWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.Release|x64.Build.0 = ReleaseWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseLinux|Any CPU.ActiveCfg = DebugWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseLinux|Any CPU.Build.0 = DebugWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseLinux|x64.ActiveCfg = ReleaseWindows|x64
@@ -113,6 +127,10 @@ Global
{008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|x64
{008C0F83-E914-4966-9135-EA885059EDD8}.ReleaseWindows|x64.Build.0 = ReleaseWindows|x64
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|x64.ActiveCfg = Debug|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|x64.Build.0 = Debug|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugLinux|x64.ActiveCfg = Debug|Any CPU
@@ -125,6 +143,10 @@ Global
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugWindows|x64.ActiveCfg = Debug|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.DebugWindows|x64.Build.0 = Debug|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|Any CPU.Build.0 = Release|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|x64.ActiveCfg = Release|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|x64.Build.0 = Release|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU
@@ -137,6 +159,10 @@ Global
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU
{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.ReleaseWindows|x64.Build.0 = Release|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Debug|x64.ActiveCfg = Debug|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Debug|x64.Build.0 = Debug|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugLinux|x64.ActiveCfg = Debug|Any CPU
@@ -149,6 +175,10 @@ Global
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugWindows|x64.ActiveCfg = Debug|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.DebugWindows|x64.Build.0 = Debug|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Release|Any CPU.Build.0 = Release|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Release|x64.ActiveCfg = Release|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.Release|x64.Build.0 = Release|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU
@@ -161,6 +191,10 @@ Global
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU
{3B8F9EDB-6E5E-450C-ABC2-EC49075D0B50}.ReleaseWindows|x64.Build.0 = Release|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.Debug|x64.ActiveCfg = Debug|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.Debug|x64.Build.0 = Debug|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugLinux|x64.ActiveCfg = Debug|Any CPU
@@ -173,6 +207,10 @@ Global
{C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugWindows|x64.ActiveCfg = Debug|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.DebugWindows|x64.Build.0 = Debug|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.Release|Any CPU.Build.0 = Release|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.Release|x64.ActiveCfg = Release|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.Release|x64.Build.0 = Release|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU
@@ -185,6 +223,10 @@ Global
{C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU
{C293DB32-FA42-486D-B128-5A12522FAE4E}.ReleaseWindows|x64.Build.0 = Release|Any CPU
{85232B20-074D-4723-B0C6-91495391E448}.Debug|Any CPU.ActiveCfg = ReleaseWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.Debug|Any CPU.Build.0 = ReleaseWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.Debug|x64.ActiveCfg = DebugWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.Debug|x64.Build.0 = DebugWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.DebugLinux|Any CPU.ActiveCfg = DebugLinux|x64
{85232B20-074D-4723-B0C6-91495391E448}.DebugLinux|Any CPU.Build.0 = DebugLinux|x64
{85232B20-074D-4723-B0C6-91495391E448}.DebugLinux|x64.ActiveCfg = DebugLinux|x64
@@ -195,6 +237,10 @@ Global
{85232B20-074D-4723-B0C6-91495391E448}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.DebugWindows|x64.ActiveCfg = DebugWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.DebugWindows|x64.Build.0 = DebugWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.Release|Any CPU.ActiveCfg = ReleaseWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.Release|Any CPU.Build.0 = ReleaseWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.Release|x64.ActiveCfg = ReleaseWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.Release|x64.Build.0 = ReleaseWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseLinux|x64
{85232B20-074D-4723-B0C6-91495391E448}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|x64
{85232B20-074D-4723-B0C6-91495391E448}.ReleaseLinux|x64.Build.0 = ReleaseLinux|x64
@@ -204,6 +250,10 @@ Global
{85232B20-074D-4723-B0C6-91495391E448}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|x64
{85232B20-074D-4723-B0C6-91495391E448}.ReleaseWindows|x64.Build.0 = ReleaseWindows|x64
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.Debug|x64.ActiveCfg = Debug|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.Debug|x64.Build.0 = Debug|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugLinux|x64.ActiveCfg = Debug|Any CPU
@@ -216,6 +266,10 @@ Global
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugWindows|x64.ActiveCfg = Debug|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.DebugWindows|x64.Build.0 = Debug|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.Release|Any CPU.Build.0 = Release|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.Release|x64.ActiveCfg = Release|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.Release|x64.Build.0 = Release|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU
@@ -228,6 +282,10 @@ Global
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU
{A4610E4C-DD34-428B-BABB-779CA0B5993A}.ReleaseWindows|x64.Build.0 = Release|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Debug|x64.ActiveCfg = Debug|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Debug|x64.Build.0 = Debug|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugLinux|x64.ActiveCfg = Debug|Any CPU
@@ -240,6 +298,10 @@ Global
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugWindows|x64.ActiveCfg = Debug|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.DebugWindows|x64.Build.0 = Debug|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Release|Any CPU.Build.0 = Release|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Release|x64.ActiveCfg = Release|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.Release|x64.Build.0 = Release|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU
@@ -252,6 +314,10 @@ Global
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU
{3AF0347C-5A9B-4421-868C-8EE3DBFAEBC6}.ReleaseWindows|x64.Build.0 = Release|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Debug|Any CPU.ActiveCfg = DebugWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Debug|Any CPU.Build.0 = DebugWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Debug|x64.ActiveCfg = DebugWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Debug|x64.Build.0 = DebugWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugLinux|Any CPU.ActiveCfg = DebugLinux|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugLinux|Any CPU.Build.0 = DebugLinux|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugLinux|x64.ActiveCfg = DebugLinux|Any CPU
@@ -263,6 +329,10 @@ Global
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugWindows|Any CPU.ActiveCfg = DebugWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.DebugWindows|x64.ActiveCfg = DebugWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Release|Any CPU.ActiveCfg = ReleaseWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Release|Any CPU.Build.0 = ReleaseWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Release|x64.ActiveCfg = ReleaseWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.Release|x64.Build.0 = ReleaseWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseLinux|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseLinux|Any CPU.Build.0 = ReleaseLinux|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|Any CPU
@@ -274,6 +344,10 @@ Global
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseWindows|Any CPU.ActiveCfg = ReleaseWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU
{0AAD36E3-51A5-4A07-AB60-5C8A66BD38B7}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.Debug|Any CPU.Build.0 = Debug|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.Debug|x64.ActiveCfg = Debug|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.Debug|x64.Build.0 = Debug|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugLinux|Any CPU.ActiveCfg = DebugLinux|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugLinux|Any CPU.Build.0 = DebugLinux|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugLinux|x64.ActiveCfg = DebugLinux|Any CPU
@@ -284,6 +358,10 @@ Global
{830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugWindows|Any CPU.Build.0 = DebugWindows|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugWindows|x64.ActiveCfg = DebugWindows|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.DebugWindows|x64.Build.0 = DebugWindows|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.Release|Any CPU.ActiveCfg = Release|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.Release|Any CPU.Build.0 = Release|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.Release|x64.ActiveCfg = Release|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.Release|x64.Build.0 = Release|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseLinux|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseLinux|Any CPU.Build.0 = ReleaseLinux|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|Any CPU
@@ -294,6 +372,10 @@ Global
{830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseWindows|Any CPU.Build.0 = ReleaseWindows|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseWindows|x64.ActiveCfg = ReleaseWindows|Any CPU
{830461AA-3E2E-4BDE-9B27-1B3280836521}.ReleaseWindows|x64.Build.0 = ReleaseWindows|Any CPU
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Debug|Any CPU.ActiveCfg = ReleaseLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Debug|Any CPU.Build.0 = ReleaseLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Debug|x64.ActiveCfg = DebugLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Debug|x64.Build.0 = DebugLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugLinux|Any CPU.ActiveCfg = DebugLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugLinux|x64.ActiveCfg = DebugLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugLinux|x64.Build.0 = DebugLinux|x64
@@ -303,6 +385,10 @@ Global
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugWindows|Any CPU.ActiveCfg = DebugLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugWindows|Any CPU.Build.0 = DebugLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.DebugWindows|x64.ActiveCfg = DebugLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Release|Any CPU.ActiveCfg = ReleaseLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Release|Any CPU.Build.0 = ReleaseLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Release|x64.ActiveCfg = ReleaseLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.Release|x64.Build.0 = ReleaseLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseLinux|x64.Build.0 = ReleaseLinux|x64
@@ -312,6 +398,10 @@ Global
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseWindows|Any CPU.ActiveCfg = DebugLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseWindows|Any CPU.Build.0 = DebugLinux|x64
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7}.ReleaseWindows|x64.ActiveCfg = ReleaseLinux|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.Debug|Any CPU.ActiveCfg = ReleaseMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.Debug|Any CPU.Build.0 = ReleaseMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.Debug|x64.ActiveCfg = DebugMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.Debug|x64.Build.0 = DebugMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugLinux|Any CPU.ActiveCfg = DebugMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugLinux|x64.ActiveCfg = DebugMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugMac|Any CPU.ActiveCfg = DebugMac|x64
@@ -321,6 +411,10 @@ Global
{CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugWindows|Any CPU.ActiveCfg = DebugMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugWindows|Any CPU.Build.0 = DebugMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.DebugWindows|x64.ActiveCfg = DebugMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.Release|Any CPU.ActiveCfg = ReleaseMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.Release|Any CPU.Build.0 = ReleaseMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.Release|x64.ActiveCfg = ReleaseMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.Release|x64.Build.0 = ReleaseMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseLinux|Any CPU.ActiveCfg = ReleaseMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseLinux|x64.ActiveCfg = ReleaseMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseMac|Any CPU.ActiveCfg = DebugMac|x64
@@ -330,6 +424,38 @@ Global
{CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseWindows|Any CPU.ActiveCfg = DebugMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseWindows|Any CPU.Build.0 = DebugMac|x64
{CC996BB6-3781-4868-B996-07F9CDC936ED}.ReleaseWindows|x64.ActiveCfg = ReleaseMac|x64
{777A5414-CAE5-4011-96DF-C9661985917E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.Debug|x64.ActiveCfg = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.Debug|x64.Build.0 = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugLinux|Any CPU.ActiveCfg = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugLinux|Any CPU.Build.0 = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugLinux|x64.ActiveCfg = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugLinux|x64.Build.0 = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugMac|Any CPU.ActiveCfg = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugMac|Any CPU.Build.0 = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugMac|x64.ActiveCfg = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugMac|x64.Build.0 = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugWindows|Any CPU.ActiveCfg = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugWindows|x64.ActiveCfg = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.DebugWindows|x64.Build.0 = Debug|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.Release|Any CPU.Build.0 = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.Release|x64.ActiveCfg = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.Release|x64.Build.0 = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseLinux|x64.Build.0 = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseMac|Any CPU.ActiveCfg = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseMac|x64.ActiveCfg = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseMac|x64.Build.0 = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseWindows|Any CPU.ActiveCfg = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseWindows|x64.ActiveCfg = Release|Any CPU
{777A5414-CAE5-4011-96DF-C9661985917E}.ReleaseWindows|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -355,6 +481,7 @@ Global
{D7F9FDD3-AF03-46AD-A2C2-F590899712B7} = {B2C129F2-8E5C-419A-98EB-161AA5B5FC71}
{DBCF6FF0-3DE9-11E9-B3EF-63280FDBDA4A} = {F35DF9BF-0BED-4FEF-A51C-DD83C531882F}
{CC996BB6-3781-4868-B996-07F9CDC936ED} = {DBCF6FF0-3DE9-11E9-B3EF-63280FDBDA4A}
{777A5414-CAE5-4011-96DF-C9661985917E} = {DE36F45F-F09E-4719-B953-00D148F7722A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {17032EAB-554B-4B44-A4F6-EFB177ACAB7A}

240
Libraries/Concentus/.gitignore vendored Normal file
View File

@@ -0,0 +1,240 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
/Java/Concentus/target/
/Java/ContentusTestConsole/ContentusTestConsole/target/
/Java/ContentusTestConsole/ConcentusTestConsole/target/
/Java/ConcentusTestConsole/target/

View File

@@ -0,0 +1,36 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2009
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Concentus", "Concentus\Concentus.csproj", "{0E7FEE6A-15E5-4A4E-943C-80276003478C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|x64.ActiveCfg = Debug|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|x64.Build.0 = Debug|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|x86.ActiveCfg = Debug|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Debug|x86.Build.0 = Debug|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|Any CPU.Build.0 = Release|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|x64.ActiveCfg = Release|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|x64.Build.0 = Release|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|x86.ActiveCfg = Release|Any CPU
{0E7FEE6A-15E5-4A4E-943C-80276003478C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4EE53631-CA03-4D29-A3A0-D17B4E57A175}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

View File

@@ -0,0 +1,300 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if UNSAFE
namespace Concentus.Common
{
using Concentus.Celt;
using Concentus.Common.CPlusPlus;
internal static class Autocorrelation
{
/* Compute autocorrelation */
internal static void silk_autocorr(
int[] results, /* O Result (length correlationCount) */
BoxedValueInt scale, /* O Scaling of the correlation vector */
short[] inputData, /* I Input data to correlate */
int inputDataSize, /* I Length of input */
int correlationCount /* I Number of correlation taps to compute */
)
{
int corrCount = Inlines.silk_min_int(inputDataSize, correlationCount);
scale.Val = Autocorrelation._celt_autocorr(inputData, results, corrCount - 1, inputDataSize);
}
internal static unsafe int _celt_autocorr(
short[] x, /* in: [0...n-1] samples x */
int[] ac, /* out: [0...lag-1] ac values */
int lag,
int n
)
{
int d;
int i, k;
int fastN = n - lag;
int shift;
short[] xx = new short[n];
Inlines.OpusAssert(n > 0);
fixed (short* xptr_base = x, pxx = xx)
{
short* xptr = xptr_base;
shift = 0;
{
int ac0;
ac0 = 1 + (n << 7);
if ((n & 1) != 0)
{
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9);
}
for (i = (n & 1); i < n; i += 2)
{
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9);
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9);
}
shift = Inlines.celt_ilog2(ac0) - 30 + 10;
shift = (shift) / 2;
if (shift > 0)
{
for (i = 0; i < n; i++)
{
xx[i] = (short)(Inlines.PSHR32(xptr[i], shift));
}
xptr = pxx;
}
else
shift = 0;
}
CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1);
for (k = 0; k <= lag; k++)
{
for (i = k + fastN, d = 0; i < n; i++)
d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]);
ac[k] += d;
}
shift = 2 * shift;
if (shift <= 0)
ac[0] += Inlines.SHL32((int)1, -shift);
if (ac[0] < 268435456)
{
int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]);
for (i = 0; i <= lag; i++)
{
ac[i] = Inlines.SHL32(ac[i], shift2);
}
shift -= shift2;
}
else if (ac[0] >= 536870912)
{
int shift2 = 1;
if (ac[0] >= 1073741824)
shift2++;
for (i = 0; i <= lag; i++)
{
ac[i] = Inlines.SHR32(ac[i], shift2);
}
shift += shift2;
}
}
return shift;
}
internal static unsafe int _celt_autocorr(
int[] x, /* in: [0...n-1] samples x */
int[] ac, /* out: [0...lag-1] ac values */
int[] window,
int overlap,
int lag,
int n)
{
int d;
int i, k;
int fastN = n - lag;
int shift;
int[] xx = new int[n];
Inlines.OpusAssert(n > 0);
Inlines.OpusAssert(overlap >= 0);
fixed (int* xptr_base = x, pxx = xx)
{
int* xptr = xptr_base;
if (overlap == 0)
{
xptr = xptr_base;
}
else
{
for (i = 0; i < n; i++)
xx[i] = x[i];
for (i = 0; i < overlap; i++)
{
xx[i] = Inlines.MULT16_16_Q15(x[i], window[i]);
xx[n - i - 1] = Inlines.MULT16_16_Q15(x[n - i - 1], window[i]);
}
xptr = pxx;
}
shift = 0;
int ac0;
ac0 = 1 + (n << 7);
if ((n & 1) != 0)
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9);
for (i = (n & 1); i < n; i += 2)
{
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9);
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9);
}
shift = Inlines.celt_ilog2(ac0) - 30 + 10;
shift = (shift) / 2;
if (shift > 0)
{
for (i = 0; i < n; i++)
xx[i] = (Inlines.PSHR32(xptr[i], shift));
xptr = pxx;
}
else
shift = 0;
CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1);
for (k = 0; k <= lag; k++)
{
for (i = k + fastN, d = 0; i < n; i++)
d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]);
ac[k] += d;
}
shift = 2 * shift;
if (shift <= 0)
ac[0] += Inlines.SHL32((int)1, -shift);
if (ac[0] < 268435456)
{
int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]);
for (i = 0; i <= lag; i++)
ac[i] = Inlines.SHL32(ac[i], shift2);
shift -= shift2;
}
else if (ac[0] >= 536870912)
{
int shift2 = 1;
if (ac[0] >= 1073741824)
shift2++;
for (i = 0; i <= lag; i++)
ac[i] = Inlines.SHR32(ac[i], shift2);
shift += shift2;
}
}
return shift;
}
private const int QC = 10;
private const int QS = 14;
/* Autocorrelations for a warped frequency axis */
internal static unsafe void silk_warped_autocorrelation(
int[] corr, /* O Result [order + 1] */
BoxedValueInt scale, /* O Scaling of the correlation vector */
short[] input, /* I Input data to correlate */
int warping_Q16, /* I Warping coefficient */
int length, /* I Length of input */
int order /* I Correlation order (even) */
)
{
int n, i, lsh;
int tmp1_QS, tmp2_QS;
int[] state_QS = new int[order + 1];// = { 0 };
long[] corr_QC = new long[order + 1];// = { 0 };
fixed (long* pcorr_QC = corr_QC)
{
fixed (int* pstate_QS = state_QS)
{
fixed (short* pinput = input)
{
/* Order must be even */
Inlines.OpusAssert((order & 1) == 0);
Inlines.OpusAssert(2 * QS - QC >= 0);
/* Loop over samples */
for (n = 0; n < length; n++)
{
tmp1_QS = Inlines.silk_LSHIFT32((int)pinput[n], QS);
/* Loop over allpass sections */
for (i = 0; i < order; i += 2)
{
/* Output of allpass section */
int* pstate_QSi = pstate_QS + i;
tmp2_QS = Inlines.silk_SMLAWB(pstate_QSi[0], pstate_QSi[1] - tmp1_QS, warping_Q16);
pstate_QSi[0] = tmp1_QS;
pcorr_QC[i] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, *pstate_QS), 2 * QS - QC);
/* Output of allpass section */
tmp1_QS = Inlines.silk_SMLAWB(pstate_QSi[1], pstate_QSi[2] - tmp2_QS, warping_Q16);
pstate_QSi[1] = tmp2_QS;
pcorr_QC[i + 1] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp2_QS, *pstate_QS), 2 * QS - QC);
}
pstate_QS[order] = tmp1_QS;
pcorr_QC[order] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, *pstate_QS), 2 * QS - QC);
}
}
}
lsh = Inlines.silk_CLZ64(*pcorr_QC) - 35;
lsh = Inlines.silk_LIMIT(lsh, -12 - QC, 30 - QC);
scale.Val = -(QC + lsh);
Inlines.OpusAssert(scale.Val >= -30 && scale.Val <= 12);
if (lsh >= 0)
{
for (i = 0; i < order + 1; i++)
{
corr[i] = (int)(Inlines.silk_LSHIFT64(pcorr_QC[i], lsh));
}
}
else
{
for (i = 0; i < order + 1; i++)
{
corr[i] = (int)(Inlines.silk_RSHIFT64(corr_QC[i], -lsh));
}
}
Inlines.OpusAssert(*pcorr_QC >= 0); /* If breaking, decrease QC*/
}
}
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,319 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
internal static class CWRS
{
/*Although derived separately, the pulse vector coding scheme is equivalent to
a Pyramid Vector Quantizer \cite{Fis86}.
Some additional notes about an early version appear at
http://people.xiph.org/~tterribe/notes/cwrs.html, but the codebook ordering
and the definitions of some terms have evolved since that was written.
The conversion from a pulse vector to an integer index (encoding) and back
(decoding) is governed by two related functions, V(N,K) and U(N,K).
V(N,K) = the number of combinations, with replacement, of N items, taken K
at a time, when a sign bit is added to each item taken at least once (i.e.,
the number of N-dimensional unit pulse vectors with K pulses).
One way to compute this is via
V(N,K) = K>0 ? sum(k=1...K,2**k*choose(N,k)*choose(K-1,k-1)) : 1,
where choose() is the binomial function.
A table of values for N<10 and K<10 looks like:
V[10][10] = {
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{1, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{1, 4, 8, 12, 16, 20, 24, 28, 32, 36},
{1, 6, 18, 38, 66, 102, 146, 198, 258, 326},
{1, 8, 32, 88, 192, 360, 608, 952, 1408, 1992},
{1, 10, 50, 170, 450, 1002, 1970, 3530, 5890, 9290},
{1, 12, 72, 292, 912, 2364, 5336, 10836, 20256, 35436},
{1, 14, 98, 462, 1666, 4942, 12642, 28814, 59906, 115598},
{1, 16, 128, 688, 2816, 9424, 27008, 68464, 157184, 332688},
{1, 18, 162, 978, 4482, 16722, 53154, 148626, 374274, 864146}
};
U(N,K) = the number of such combinations wherein N-1 objects are taken at
most K-1 at a time.
This is given by
U(N,K) = sum(k=0...K-1,V(N-1,k))
= K>0 ? (V(N-1,K-1) + V(N,K-1))/2 : 0.
The latter expression also makes clear that U(N,K) is half the number of such
combinations wherein the first object is taken at least once.
Although it may not be clear from either of these definitions, U(N,K) is the
natural function to work with when enumerating the pulse vector codebooks,
not V(N,K).
U(N,K) is not well-defined for N=0, but with the extension
U(0,K) = K>0 ? 0 : 1,
the function becomes symmetric: U(N,K) = U(K,N), with a similar table:
U[10][10] = {
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{0, 1, 3, 5, 7, 9, 11, 13, 15, 17},
{0, 1, 5, 13, 25, 41, 61, 85, 113, 145},
{0, 1, 7, 25, 63, 129, 231, 377, 575, 833},
{0, 1, 9, 41, 129, 321, 681, 1289, 2241, 3649},
{0, 1, 11, 61, 231, 681, 1683, 3653, 7183, 13073},
{0, 1, 13, 85, 377, 1289, 3653, 8989, 19825, 40081},
{0, 1, 15, 113, 575, 2241, 7183, 19825, 48639, 108545},
{0, 1, 17, 145, 833, 3649, 13073, 40081, 108545, 265729}
};
With this extension, V(N,K) may be written in terms of U(N,K):
V(N,K) = U(N,K) + U(N,K+1)
for all N>=0, K>=0.
Thus U(N,K+1) represents the number of combinations where the first element
is positive or zero, and U(N,K) represents the number of combinations where
it is negative.
With a large enough table of U(N,K) values, we could write O(N) encoding
and O(min(N*log(K),N+K)) decoding routines, but such a table would be
prohibitively large for small embedded devices (K may be as large as 32767
for small N, and N may be as large as 200).
Both functions obey the same recurrence relation:
V(N,K) = V(N-1,K) + V(N,K-1) + V(N-1,K-1),
U(N,K) = U(N-1,K) + U(N,K-1) + U(N-1,K-1),
for all N>0, K>0, with different initial conditions at N=0 or K=0.
This allows us to construct a row of one of the tables above given the
previous row or the next row.
Thus we can derive O(NK) encoding and decoding routines with O(K) memory
using only addition and subtraction.
When encoding, we build up from the U(2,K) row and work our way forwards.
When decoding, we need to start at the U(N,K) row and work our way backwards,
which requires a means of computing U(N,K).
U(N,K) may be computed from two previous values with the same N:
U(N,K) = ((2*N-1)*U(N,K-1) - U(N,K-2))/(K-1) + U(N,K-2)
for all N>1, and since U(N,K) is symmetric, a similar relation holds for two
previous values with the same K:
U(N,K>1) = ((2*K-1)*U(N-1,K) - U(N-2,K))/(N-1) + U(N-2,K)
for all K>1.
This allows us to construct an arbitrary row of the U(N,K) table by starting
with the first two values, which are constants.
This saves roughly 2/3 the work in our O(NK) decoding routine, but costs O(K)
multiplications.
Similar relations can be derived for V(N,K), but are not used here.
For N>0 and K>0, U(N,K) and V(N,K) take on the form of an (N-1)-degree
polynomial for fixed N.
The first few are
U(1,K) = 1,
U(2,K) = 2*K-1,
U(3,K) = (2*K-2)*K+1,
U(4,K) = (((4*K-6)*K+8)*K-3)/3,
U(5,K) = ((((2*K-4)*K+10)*K-8)*K+3)/3,
and
V(1,K) = 2,
V(2,K) = 4*K,
V(3,K) = 4*K*K+2,
V(4,K) = 8*(K*K+2)*K/3,
V(5,K) = ((4*K*K+20)*K*K+6)/3,
for all K>0.
This allows us to derive O(N) encoding and O(N*log(K)) decoding routines for
small N (and indeed decoding is also O(N) for N<3).
@ARTICLE{Fis86,
author="Thomas R. Fischer",
title="A Pyramid Vector Quantizer",
journal="IEEE Transactions on Information Theory",
volume="IT-32",
number=4,
pages="568--583",
month=Jul,
year=1986
}*/
internal static readonly uint[] CELT_PVQ_U_ROW =
{
0,176,351,525,698,870,1041,1131,1178,1207,1226,1240,1248,1254,1257
};
/*U(N,K) = U(K,N) := N>0?K>0?U(N-1,K)+U(N,K-1)+U(N-1,K-1):0:K>0?1:0*/
private static uint CELT_PVQ_U(int _n, int _k)
{
return Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[Inlines.IMIN(_n, _k)] + Inlines.IMAX(_n, _k)];
}
/*V(N,K) := U(N,K)+U(N,K+1) = the number of PVQ codewords for a band of size N
with K pulses allocated to it.*/
private static uint CELT_PVQ_V(int _n, int _k)
{
return (CELT_PVQ_U(_n, _k) + CELT_PVQ_U(_n, (_k) + 1));
}
internal static uint icwrs(int _n, int[] _y)
{
uint i;
int j;
int k;
Inlines.OpusAssert(_n >= 2);
j = _n - 1;
i = (_y[j] < 0) ? 1U : 0;
k = Inlines.abs(_y[j]);
do
{
j--;
i += CELT_PVQ_U(_n - j, k);
k += Inlines.abs(_y[j]);
if (_y[j] < 0) i += CELT_PVQ_U(_n - j, k + 1);
}
while (j > 0);
return i;
}
internal static void encode_pulses(int[] _y, int _n, int _k, EntropyCoder _enc)
{
Inlines.OpusAssert(_k > 0);
_enc.enc_uint(icwrs(_n, _y), CELT_PVQ_V(_n, _k));
}
internal static int cwrsi(int _n, int _k, uint _i, int[] _y)
{
uint p;
int s;
int k0;
short val;
int yy = 0;
int y_ptr = 0;
Inlines.OpusAssert(_k > 0);
Inlines.OpusAssert(_n > 1);
while (_n > 2)
{
uint q;
/*Lots of pulses case:*/
if (_k >= _n)
{
uint row;
row = CELT_PVQ_U_ROW[_n];
/*Are the pulses in this dimension negative?*/
p = Tables.CELT_PVQ_U_DATA[row + _k + 1];
s = 0 - (_i >= p ? 1 : 0);
_i -= (p & unchecked((uint)s));
/*Count how many pulses were placed in this dimension.*/
k0 = _k;
q = Tables.CELT_PVQ_U_DATA[row + _n];
if (q > _i)
{
Inlines.OpusAssert(p > q);
_k = _n;
do
{
p = Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[--_k] + _n];
} while (p > _i);
}
else
{
for (p = Tables.CELT_PVQ_U_DATA[row + _k]; p > _i; p = Tables.CELT_PVQ_U_DATA[row + _k])
{
_k--;
}
}
_i -= p;
val = (short)((k0 - _k + s) ^ s);
_y[y_ptr++] = val;
yy = Inlines.MAC16_16(yy, val, val);
}
/*Lots of dimensions case:*/
else
{
/*Are there any pulses in this dimension at all?*/
p = Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[_k] + _n];
q = Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[_k + 1] + _n];
if (p <= _i && _i < q)
{
_i -= p;
_y[y_ptr++] = 0;
}
else
{
/*Are the pulses in this dimension negative?*/
s = 0 - (_i >= q ? 1 : 0);
_i -= (q & unchecked((uint)s));
/*Count how many pulses were placed in this dimension.*/
k0 = _k;
do
{
p = Tables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[--_k] + _n];
} while (p > _i);
_i -= p;
val = (short)((k0 - _k + s) ^ s);
_y[y_ptr++] = val;
yy = Inlines.MAC16_16(yy, val, val);
}
}
_n--;
}
/*_n==2*/
p = (uint)(2 * _k + 1);
s = 0 - (_i >= p ? 1 : 0);
_i -= (p & unchecked((uint)s));
k0 = _k;
_k = (int)((_i + 1) >> 1);
if (_k != 0)
{
_i -= (2 * (uint)_k - 1);
}
val = (short)((k0 - _k + s) ^ s);
_y[y_ptr++] = val;
yy = Inlines.MAC16_16(yy, val, val);
/*_n==1*/
s = -(int)_i;
val = (short)((_k + s) ^ s);
_y[y_ptr] = val;
yy = Inlines.MAC16_16(yy, val, val);
return yy;
}
internal static int decode_pulses(int[] _y, int _n, int _k, EntropyCoder _dec)
{
return cwrsi(_n, _k, _dec.dec_uint(CELT_PVQ_V(_n, _k)), _y);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt
{
internal static class CeltConstants
{
public const int Q15ONE = 32767;
public const float CELT_SIG_SCALE = 32768.0f;
public const int SIG_SHIFT = 12;
public const int NORM_SCALING = 16384;
public const int DB_SHIFT = 10;
public const int EPSILON = 1;
public const int VERY_SMALL = 0;
public const short VERY_LARGE16 = ((short)32767);
public const short Q15_ONE = ((short)32767);
public const int COMBFILTER_MAXPERIOD = 1024;
public const int COMBFILTER_MINPERIOD = 15;
// from opus_decode.c
public const int DECODE_BUFFER_SIZE = 2048;
// from modes.c
/* Alternate tuning (partially derived from Vorbis) */
public const int BITALLOC_SIZE = 11;
public const int MAX_PERIOD = 1024;
// from static_modes_float.h
public const int TOTAL_MODES = 1;
// from rate.h
public const int MAX_PSEUDO = 40;
public const int LOG_MAX_PSEUDO = 6;
public const int CELT_MAX_PULSES = 128;
public const int MAX_FINE_BITS = 8;
public const int FINE_OFFSET = 21;
public const int QTHETA_OFFSET = 4;
public const int QTHETA_OFFSET_TWOPHASE = 16;
/* The maximum pitch lag to allow in the pitch-based PLC. It's possible to save
CPU time in the PLC pitch search by making this smaller than MAX_PERIOD. The
current value corresponds to a pitch of 66.67 Hz. */
public const int PLC_PITCH_LAG_MAX = 720;
/* The minimum pitch lag to allow in the pitch-based PLC. This corresponds to a
pitch of 480 Hz. */
public const int PLC_PITCH_LAG_MIN = 100;
public const int LPC_ORDER = 24;
}
}

View File

@@ -0,0 +1,159 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if !UNSAFE
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
internal static class CeltLPC
{
internal static void celt_lpc(
int[] _lpc, /* out: [0...p-1] LPC coefficients */
int[] ac, /* in: [0...p] autocorrelation values */
int p)
{
int i, j;
int r;
int error = ac[0];
int[] lpc = new int[p];
//Arrays.MemSetInt(lpc, 0, p); strictly, this is not necessary since the runtime zeroes memory for us
if (ac[0] != 0)
{
for (i = 0; i < p; i++)
{
/* Sum up this iteration's reflection coefficient */
int rr = 0;
for (j = 0; j < i; j++)
rr += Inlines.MULT32_32_Q31(lpc[j], ac[i - j]);
rr += Inlines.SHR32(ac[i + 1], 3);
r = 0 - Inlines.frac_div32(Inlines.SHL32(rr, 3), error);
/* Update LPC coefficients and total error */
lpc[i] = Inlines.SHR32(r, 3);
for (j = 0; j < (i + 1) >> 1; j++)
{
int tmp1, tmp2;
tmp1 = lpc[j];
tmp2 = lpc[i - 1 - j];
lpc[j] = tmp1 + Inlines.MULT32_32_Q31(r, tmp2);
lpc[i - 1 - j] = tmp2 + Inlines.MULT32_32_Q31(r, tmp1);
}
error = error - Inlines.MULT32_32_Q31(Inlines.MULT32_32_Q31(r, r), error);
/* Bail out once we get 30 dB gain */
if (error < Inlines.SHR32(ac[0], 10))
{
break;
}
}
}
for (i = 0; i < p; i++)
{
_lpc[i] = Inlines.ROUND16((lpc[i]), 16);
}
}
internal static void celt_iir(
int[] _x,
int _x_ptr,
int[] den,
int[] _y,
int _y_ptr,
int N,
int ord,
int[] mem)
{
int i, j;
int[] rden = new int[ord];
int[] y = new int[N + ord];
Inlines.OpusAssert((ord & 3) == 0);
for (i = 0; i < ord; i++)
rden[i] = den[ord - i - 1];
for (i = 0; i < ord; i++)
y[i] = (0 - mem[ord - i - 1]);
for (; i < N + ord; i++)
y[i] = 0;
for (i = 0; i < N - 3; i += 4)
{
/* Unroll by 4 as if it were an FIR filter */
int sum0 = _x[_x_ptr + i];
int sum1 = _x[_x_ptr + i + 1];
int sum2 = _x[_x_ptr + i + 2];
int sum3 = _x[_x_ptr + i + 3];
Kernels.xcorr_kernel(rden, y, i, ref sum0, ref sum1, ref sum2, ref sum3, ord);
/* Patch up the result to compensate for the fact that this is an IIR */
y[i + ord] = (0 - Inlines.ROUND16((sum0), CeltConstants.SIG_SHIFT));
_y[_y_ptr + i] = sum0;
sum1 = Inlines.MAC16_16(sum1, y[i + ord], den[0]);
y[i + ord + 1] = (0 - Inlines.ROUND16((sum1), CeltConstants.SIG_SHIFT));
_y[_y_ptr + i + 1] = sum1;
sum2 = Inlines.MAC16_16(sum2, y[i + ord + 1], den[0]);
sum2 = Inlines.MAC16_16(sum2, y[i + ord], den[1]);
y[i + ord + 2] = (0 - Inlines.ROUND16((sum2), CeltConstants.SIG_SHIFT));
_y[_y_ptr + i + 2] = sum2;
sum3 = Inlines.MAC16_16(sum3, y[i + ord + 2], den[0]);
sum3 = Inlines.MAC16_16(sum3, y[i + ord + 1], den[1]);
sum3 = Inlines.MAC16_16(sum3, y[i + ord], den[2]);
y[i + ord + 3] = (0 - Inlines.ROUND16((sum3), CeltConstants.SIG_SHIFT));
_y[_y_ptr + i + 3] = sum3;
}
for (; i < N; i++)
{
int sum = _x[_x_ptr + i];
for (j = 0; j < ord; j++)
sum -= Inlines.MULT16_16(rden[j], y[i + j]);
y[i + ord] = Inlines.ROUND16((sum), CeltConstants.SIG_SHIFT);
_y[_y_ptr + i] = sum;
}
for (i = 0; i < ord; i++)
mem[i] = (_y[_y_ptr + N - i - 1]);
}
}
}
#endif

View File

@@ -0,0 +1,163 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if UNSAFE
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
internal static class CeltLPC
{
internal static void celt_lpc(
int[] _lpc, /* out: [0...p-1] LPC coefficients */
int[] ac, /* in: [0...p] autocorrelation values */
int p)
{
int i, j;
int r;
int error = ac[0];
int[] lpc = new int[p];
//Arrays.MemSetInt(lpc, 0, p); strictly, this is not necessary since the runtime zeroes memory for us
if (ac[0] != 0)
{
for (i = 0; i < p; i++)
{
/* Sum up this iteration's reflection coefficient */
int rr = 0;
for (j = 0; j < i; j++)
rr += Inlines.MULT32_32_Q31(lpc[j], ac[i - j]);
rr += Inlines.SHR32(ac[i + 1], 3);
r = 0 - Inlines.frac_div32(Inlines.SHL32(rr, 3), error);
/* Update LPC coefficients and total error */
lpc[i] = Inlines.SHR32(r, 3);
for (j = 0; j < (i + 1) >> 1; j++)
{
int tmp1, tmp2;
tmp1 = lpc[j];
tmp2 = lpc[i - 1 - j];
lpc[j] = tmp1 + Inlines.MULT32_32_Q31(r, tmp2);
lpc[i - 1 - j] = tmp2 + Inlines.MULT32_32_Q31(r, tmp1);
}
error = error - Inlines.MULT32_32_Q31(Inlines.MULT32_32_Q31(r, r), error);
/* Bail out once we get 30 dB gain */
if (error < Inlines.SHR32(ac[0], 10))
{
break;
}
}
}
for (i = 0; i < p; i++)
{
_lpc[i] = Inlines.ROUND16((lpc[i]), 16);
}
}
internal static unsafe void celt_iir(
int[] _x,
int _x_ptr,
int[] den,
int[] _y,
int _y_ptr,
int N,
int ord,
int[] mem)
{
int i, j;
int[] rden = new int[ord];
int[] y = new int[N + ord];
Inlines.OpusAssert((ord & 3) == 0);
fixed (int* prden = rden, py_base = y)
{
for (i = 0; i < ord; i++)
rden[i] = den[ord - i - 1];
for (i = 0; i < ord; i++)
y[i] = (0 - mem[ord - i - 1]);
for (; i < N + ord; i++)
y[i] = 0;
for (i = 0; i < N - 3; i += 4)
{
int* py = py_base + i;
/* Unroll by 4 as if it were an FIR filter */
int sum0 = _x[_x_ptr + i];
int sum1 = _x[_x_ptr + i + 1];
int sum2 = _x[_x_ptr + i + 2];
int sum3 = _x[_x_ptr + i + 3];
Kernels.xcorr_kernel(prden, py, ref sum0, ref sum1, ref sum2, ref sum3, ord);
/* Patch up the result to compensate for the fact that this is an IIR */
y[i + ord] = (0 - Inlines.ROUND16((sum0), CeltConstants.SIG_SHIFT));
_y[_y_ptr + i] = sum0;
sum1 = Inlines.MAC16_16(sum1, y[i + ord], den[0]);
y[i + ord + 1] = (0 - Inlines.ROUND16((sum1), CeltConstants.SIG_SHIFT));
_y[_y_ptr + i + 1] = sum1;
sum2 = Inlines.MAC16_16(sum2, y[i + ord + 1], den[0]);
sum2 = Inlines.MAC16_16(sum2, y[i + ord], den[1]);
y[i + ord + 2] = (0 - Inlines.ROUND16((sum2), CeltConstants.SIG_SHIFT));
_y[_y_ptr + i + 2] = sum2;
sum3 = Inlines.MAC16_16(sum3, y[i + ord + 2], den[0]);
sum3 = Inlines.MAC16_16(sum3, y[i + ord + 1], den[1]);
sum3 = Inlines.MAC16_16(sum3, y[i + ord], den[2]);
y[i + ord + 3] = (0 - Inlines.ROUND16((sum3), CeltConstants.SIG_SHIFT));
_y[_y_ptr + i + 3] = sum3;
}
for (; i < N; i++)
{
int sum = _x[_x_ptr + i];
for (j = 0; j < ord; j++)
sum -= Inlines.MULT16_16(rden[j], y[i + j]);
y[i + ord] = Inlines.ROUND16((sum), CeltConstants.SIG_SHIFT);
_y[_y_ptr + i] = sum;
}
for (i = 0; i < ord; i++)
mem[i] = (_y[_y_ptr + N - i - 1]);
}
}
}
}
#endif

View File

@@ -0,0 +1,154 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if !UNSAFE
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
using System.Threading;
internal static class CeltPitchXCorr
{
internal static int pitch_xcorr(
int[] _x,
int[] _y,
int[] xcorr,
int len,
int max_pitch)
{
int i;
int maxcorr = 1;
Inlines.OpusAssert(max_pitch > 0);
for (i = 0; i < max_pitch - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
Kernels.xcorr_kernel(_x, _y, i, ref sum0, ref sum1, ref sum2, ref sum3, len);
xcorr[i] = sum0;
xcorr[i + 1] = sum1;
xcorr[i + 2] = sum2;
xcorr[i + 3] = sum3;
sum0 = Inlines.MAX32(sum0, sum1);
sum2 = Inlines.MAX32(sum2, sum3);
sum0 = Inlines.MAX32(sum0, sum2);
maxcorr = Inlines.MAX32(maxcorr, sum0);
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (; i < max_pitch; i++)
{
int inner_sum = Kernels.celt_inner_prod(_x, 0, _y, i, len);
xcorr[i] = inner_sum;
maxcorr = Inlines.MAX32(maxcorr, inner_sum);
}
return maxcorr;
}
internal static int pitch_xcorr(
short[] _x,
int _x_ptr,
short[] _y,
int _y_ptr,
int[] xcorr,
int len,
int max_pitch)
{
int i;
int maxcorr = 1;
Inlines.OpusAssert(max_pitch > 0);
for (i = 0; i < max_pitch - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
Kernels.xcorr_kernel(_x, _x_ptr, _y, _y_ptr + i, ref sum0, ref sum1, ref sum2, ref sum3, len);
xcorr[i] = sum0;
xcorr[i + 1] = sum1;
xcorr[i + 2] = sum2;
xcorr[i + 3] = sum3;
sum0 = Inlines.MAX32(sum0, sum1);
sum2 = Inlines.MAX32(sum2, sum3);
sum0 = Inlines.MAX32(sum0, sum2);
maxcorr = Inlines.MAX32(maxcorr, sum0);
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (; i < max_pitch; i++)
{
int inner_sum = Kernels.celt_inner_prod(_x, _x_ptr, _y, _y_ptr + i, len);
xcorr[i] = inner_sum;
maxcorr = Inlines.MAX32(maxcorr, inner_sum);
}
return maxcorr;
}
internal static int pitch_xcorr(
short[] _x,
short[] _y,
int[] xcorr,
int len,
int max_pitch)
{
int i;
int maxcorr = 1;
Inlines.OpusAssert(max_pitch > 0);
for (i = 0; i < max_pitch - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
Kernels.xcorr_kernel(_x, 0, _y, i, ref sum0, ref sum1, ref sum2, ref sum3, len);
xcorr[i] = sum0;
xcorr[i + 1] = sum1;
xcorr[i + 2] = sum2;
xcorr[i + 3] = sum3;
sum0 = Inlines.MAX32(sum0, sum1);
sum2 = Inlines.MAX32(sum2, sum3);
sum0 = Inlines.MAX32(sum0, sum2);
maxcorr = Inlines.MAX32(maxcorr, sum0);
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (; i < max_pitch; i++)
{
int inner_sum = Kernels.celt_inner_prod(_x, _y, i, len);
xcorr[i] = inner_sum;
maxcorr = Inlines.MAX32(maxcorr, inner_sum);
}
return maxcorr;
}
}
}
#endif

View File

@@ -0,0 +1,255 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if UNSAFE
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
using System.Threading;
internal static class CeltPitchXCorr
{
internal static unsafe int pitch_xcorr(
int[] _x,
int[] _y,
int[] xcorr,
int len,
int max_pitch)
{
int i;
int maxcorr = 1;
Inlines.OpusAssert(max_pitch > 0);
fixed (int* py_base = _y, px = _x, px_base = _x)
{
for (i = 0; i < max_pitch - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
int* py = py_base + i;
Kernels.xcorr_kernel(px, py, ref sum0, ref sum1, ref sum2, ref sum3, len);
xcorr[i] = sum0;
xcorr[i + 1] = sum1;
xcorr[i + 2] = sum2;
xcorr[i + 3] = sum3;
sum0 = Inlines.MAX32(sum0, sum1);
sum2 = Inlines.MAX32(sum2, sum3);
sum0 = Inlines.MAX32(sum0, sum2);
maxcorr = Inlines.MAX32(maxcorr, sum0);
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (; i < max_pitch; i++)
{
int* py = py_base + i;
int inner_sum = Kernels.celt_inner_prod(px_base, py, len);
xcorr[i] = inner_sum;
maxcorr = Inlines.MAX32(maxcorr, inner_sum);
}
}
return maxcorr;
}
internal static unsafe int pitch_xcorr(
int* _x,
int* _y,
int[] xcorr,
int len,
int max_pitch)
{
int i;
int maxcorr = 1;
Inlines.OpusAssert(max_pitch > 0);
for (i = 0; i < max_pitch - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
int* py = _y + i;
Kernels.xcorr_kernel(_x, py, ref sum0, ref sum1, ref sum2, ref sum3, len);
xcorr[i] = sum0;
xcorr[i + 1] = sum1;
xcorr[i + 2] = sum2;
xcorr[i + 3] = sum3;
sum0 = Inlines.MAX32(sum0, sum1);
sum2 = Inlines.MAX32(sum2, sum3);
sum0 = Inlines.MAX32(sum0, sum2);
maxcorr = Inlines.MAX32(maxcorr, sum0);
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (; i < max_pitch; i++)
{
int* py = _y + i;
int inner_sum = Kernels.celt_inner_prod(_x, py, len);
xcorr[i] = inner_sum;
maxcorr = Inlines.MAX32(maxcorr, inner_sum);
}
return maxcorr;
}
internal static unsafe int pitch_xcorr(
short[] _x,
int _x_ptr,
short[] _y,
int _y_ptr,
int[] xcorr,
int len,
int max_pitch)
{
int i;
int maxcorr = 1;
Inlines.OpusAssert(max_pitch > 0);
fixed (int* pxcorr = xcorr)
{
fixed (short* px_base = _x, py_base = _y)
{
short* px = px_base + _x_ptr;
for (i = 0; i < max_pitch - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
short* py = py_base + _y_ptr + i;
Kernels.xcorr_kernel(px, py, ref sum0, ref sum1, ref sum2, ref sum3, len);
int* pxcorr2 = pxcorr + i;
pxcorr2[0] = sum0;
pxcorr2[1] = sum1;
pxcorr2[2] = sum2;
pxcorr2[3] = sum3;
sum0 = Inlines.MAX32(sum0, sum1);
sum2 = Inlines.MAX32(sum2, sum3);
sum0 = Inlines.MAX32(sum0, sum2);
maxcorr = Inlines.MAX32(maxcorr, sum0);
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (; i < max_pitch; i++)
{
short* py = py_base + _y_ptr + i;
int inner_sum = Kernels.celt_inner_prod(px, py, len);
xcorr[i] = inner_sum;
maxcorr = Inlines.MAX32(maxcorr, inner_sum);
}
}
}
return maxcorr;
}
internal static unsafe int pitch_xcorr(
short[] _x,
short[] _y,
int[] xcorr,
int len,
int max_pitch)
{
int i;
int maxcorr = 1;
Inlines.OpusAssert(max_pitch > 0);
fixed (int* pxcorr_base = xcorr)
{
fixed (short* px = _x, py_base = _y)
{
for (i = 0; i < max_pitch - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
short* py = py_base + i;
Kernels.xcorr_kernel(px, py, ref sum0, ref sum1, ref sum2, ref sum3, len);
int* pxcorr = pxcorr_base + i;
pxcorr[0] = sum0;
pxcorr[1] = sum1;
pxcorr[2] = sum2;
pxcorr[3] = sum3;
sum0 = Inlines.MAX32(sum0, sum1);
sum2 = Inlines.MAX32(sum2, sum3);
sum0 = Inlines.MAX32(sum0, sum2);
maxcorr = Inlines.MAX32(maxcorr, sum0);
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (; i < max_pitch; i++)
{
short* py = py_base + i;
int inner_sum = Kernels.celt_inner_prod(px, py, len);
xcorr[i] = inner_sum;
maxcorr = Inlines.MAX32(maxcorr, inner_sum);
}
return maxcorr;
}
}
}
internal static unsafe int pitch_xcorr(
short* px,
short* py,
int[] xcorr,
int len,
int max_pitch)
{
int i;
int maxcorr = 1;
Inlines.OpusAssert(max_pitch > 0);
fixed (int* pxcorr_base = xcorr)
{
for (i = 0; i < max_pitch - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
short* py2 = py + i;
Kernels.xcorr_kernel(px, py2, ref sum0, ref sum1, ref sum2, ref sum3, len);
int* pxcorr = pxcorr_base + i;
pxcorr[0] = sum0;
pxcorr[1] = sum1;
pxcorr[2] = sum2;
pxcorr[3] = sum3;
sum0 = Inlines.MAX32(sum0, sum1);
sum2 = Inlines.MAX32(sum2, sum3);
sum0 = Inlines.MAX32(sum0, sum2);
maxcorr = Inlines.MAX32(maxcorr, sum0);
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (; i < max_pitch; i++)
{
short* py2 = py + i;
int inner_sum = Kernels.celt_inner_prod(px, py2, len);
xcorr[i] = inner_sum;
maxcorr = Inlines.MAX32(maxcorr, inner_sum);
}
return maxcorr;
}
}
}
}
#endif

View File

@@ -0,0 +1,45 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt.Enums
{
internal static class Spread
{
public const int SPREAD_NONE = 0;
public const int SPREAD_LIGHT = 1;
public const int SPREAD_NORMAL = 2;
public const int SPREAD_AGGRESSIVE = 3;
}
}

View File

@@ -0,0 +1,354 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if !UNSAFE
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
internal static class Kernels
{
internal static void celt_fir(
short[] x,
int x_ptr,
short[] num,
short[] y,
int y_ptr,
int N,
int ord,
short[] mem
)
{
int i, j;
short[] rnum = new short[ord];
short[] local_x = new short[N + ord];
for (i = 0; i < ord; i++)
{
rnum[i] = num[ord - i - 1];
}
for (i = 0; i < ord; i++)
{
local_x[i] = mem[ord - i - 1];
}
for (i = 0; i < N; i++)
{
local_x[i + ord] = x[x_ptr + i];
}
for (i = 0; i < ord; i++)
{
mem[i] = x[x_ptr + N - i - 1];
}
for (i = 0; i < N - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
xcorr_kernel(rnum, 0, local_x, i, ref sum0, ref sum1, ref sum2, ref sum3, ord);
y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT))));
y[y_ptr + i + 1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT))));
y[y_ptr + i + 2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT))));
y[y_ptr + i + 3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT))));
}
for (; i < N; i++)
{
int sum = 0;
for (j = 0; j < ord; j++)
{
sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]);
}
y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT))));
}
}
internal static void celt_fir(
int[] x,
int x_ptr,
int[] num,
int num_ptr,
int[] y,
int y_ptr,
int N,
int ord,
int[] mem
)
{
int i, j;
int[] rnum = new int[ord];
int[] local_x = new int[N + ord];
for (i = 0; i < ord; i++)
{
rnum[i] = num[num_ptr + ord - i - 1];
}
for (i = 0; i < ord; i++)
{
local_x[i] = mem[ord - i - 1];
}
for (i = 0; i < N; i++)
{
local_x[i + ord] = x[x_ptr + i];
}
for (i = 0; i < ord; i++)
{
mem[i] = x[x_ptr + N - i - 1];
}
for (i = 0; i < N - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
xcorr_kernel(rnum, local_x, i, ref sum0, ref sum1, ref sum2, ref sum3, ord);
y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT))));
y[y_ptr + i + 1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT))));
y[y_ptr + i + 2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT))));
y[y_ptr + i + 3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT))));
}
for (; i < N; i++)
{
int sum = 0;
for (j = 0; j < ord; j++)
{
sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]);
}
y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT))));
}
}
/// <summary>
/// OPT: This is the kernel you really want to optimize. It gets used a lot by the prefilter and by the PLC.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="sum0"></param>
/// <param name="len"></param>
internal static void xcorr_kernel(short[] x, int x_ptr, short[] y, int y_ptr, ref int sum0, ref int sum1, ref int sum2, ref int sum3, int len)
{
int j;
short y_0, y_1, y_2, y_3;
Inlines.OpusAssert(len >= 3);
y_3 = 0; /* gcc doesn't realize that y_3 can't be used uninitialized */
y_0 = y[y_ptr++];
y_1 = y[y_ptr++];
y_2 = y[y_ptr++];
for (j = 0; j < len - 3; j += 4)
{
short tmp;
tmp = x[x_ptr++];
y_3 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_0);
sum1 = Inlines.MAC16_16(sum1, tmp, y_1);
sum2 = Inlines.MAC16_16(sum2, tmp, y_2);
sum3 = Inlines.MAC16_16(sum3, tmp, y_3);
tmp = x[x_ptr++];
y_0 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_1);
sum1 = Inlines.MAC16_16(sum1, tmp, y_2);
sum2 = Inlines.MAC16_16(sum2, tmp, y_3);
sum3 = Inlines.MAC16_16(sum3, tmp, y_0);
tmp = x[x_ptr++];
y_1 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_2);
sum1 = Inlines.MAC16_16(sum1, tmp, y_3);
sum2 = Inlines.MAC16_16(sum2, tmp, y_0);
sum3 = Inlines.MAC16_16(sum3, tmp, y_1);
tmp = x[x_ptr++];
y_2 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_3);
sum1 = Inlines.MAC16_16(sum1, tmp, y_0);
sum2 = Inlines.MAC16_16(sum2, tmp, y_1);
sum3 = Inlines.MAC16_16(sum3, tmp, y_2);
}
if (j++ < len)
{
short tmp;
tmp = x[x_ptr++];
y_3 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_0);
sum1 = Inlines.MAC16_16(sum1, tmp, y_1);
sum2 = Inlines.MAC16_16(sum2, tmp, y_2);
sum3 = Inlines.MAC16_16(sum3, tmp, y_3);
}
if (j++ < len)
{
short tmp;
tmp = x[x_ptr++];
y_0 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_1);
sum1 = Inlines.MAC16_16(sum1, tmp, y_2);
sum2 = Inlines.MAC16_16(sum2, tmp, y_3);
sum3 = Inlines.MAC16_16(sum3, tmp, y_0);
}
if (j < len)
{
short tmp;
tmp = x[x_ptr++];
y_1 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_2);
sum1 = Inlines.MAC16_16(sum1, tmp, y_3);
sum2 = Inlines.MAC16_16(sum2, tmp, y_0);
sum3 = Inlines.MAC16_16(sum3, tmp, y_1);
}
}
internal static void xcorr_kernel(int[] x, int[] y, int y_ptr, ref int sum0, ref int sum1, ref int sum2, ref int sum3, int len)
{
int j;
int y_0, y_1, y_2, y_3;
int x_ptr = 0;
Inlines.OpusAssert(len >= 3);
y_3 = 0; /* gcc doesn't realize that y_3 can't be used uninitialized */
y_0 = y[y_ptr++];
y_1 = y[y_ptr++];
y_2 = y[y_ptr++];
for (j = 0; j < len - 3; j += 4)
{
int tmp;
tmp = x[x_ptr++];
y_3 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_0);
sum1 = Inlines.MAC16_16(sum1, tmp, y_1);
sum2 = Inlines.MAC16_16(sum2, tmp, y_2);
sum3 = Inlines.MAC16_16(sum3, tmp, y_3);
tmp = x[x_ptr++];
y_0 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_1);
sum1 = Inlines.MAC16_16(sum1, tmp, y_2);
sum2 = Inlines.MAC16_16(sum2, tmp, y_3);
sum3 = Inlines.MAC16_16(sum3, tmp, y_0);
tmp = x[x_ptr++];
y_1 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_2);
sum1 = Inlines.MAC16_16(sum1, tmp, y_3);
sum2 = Inlines.MAC16_16(sum2, tmp, y_0);
sum3 = Inlines.MAC16_16(sum3, tmp, y_1);
tmp = x[x_ptr++];
y_2 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_3);
sum1 = Inlines.MAC16_16(sum1, tmp, y_0);
sum2 = Inlines.MAC16_16(sum2, tmp, y_1);
sum3 = Inlines.MAC16_16(sum3, tmp, y_2);
}
if (j++ < len)
{
int tmp;
tmp = x[x_ptr++];
y_3 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_0);
sum1 = Inlines.MAC16_16(sum1, tmp, y_1);
sum2 = Inlines.MAC16_16(sum2, tmp, y_2);
sum3 = Inlines.MAC16_16(sum3, tmp, y_3);
}
if (j++ < len)
{
int tmp;
tmp = x[x_ptr++];
y_0 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_1);
sum1 = Inlines.MAC16_16(sum1, tmp, y_2);
sum2 = Inlines.MAC16_16(sum2, tmp, y_3);
sum3 = Inlines.MAC16_16(sum3, tmp, y_0);
}
if (j < len)
{
int tmp;
tmp = x[x_ptr++];
y_1 = y[y_ptr++];
sum0 = Inlines.MAC16_16(sum0, tmp, y_2);
sum1 = Inlines.MAC16_16(sum1, tmp, y_3);
sum2 = Inlines.MAC16_16(sum2, tmp, y_0);
sum3 = Inlines.MAC16_16(sum3, tmp, y_1);
}
}
internal static int celt_inner_prod(short[] x, int x_ptr, short[] y, int y_ptr, int N)
{
int i;
int xy = 0;
for (i = 0; i < N; i++)
xy = Inlines.MAC16_16(xy, x[x_ptr + i], y[y_ptr + i]);
return xy;
}
internal static int celt_inner_prod(short[] x, short[] y, int y_ptr, int N)
{
int i;
int xy = 0;
for (i = 0; i < N; i++)
xy = Inlines.MAC16_16(xy, x[i], y[y_ptr + i]);
return xy;
}
internal static int celt_inner_prod(int[] x, int x_ptr, int[] y, int y_ptr, int N)
{
int i;
int xy = 0;
for (i = 0; i < N; i++)
xy = Inlines.MAC16_16(xy, x[x_ptr + i], y[y_ptr + i]);
return xy;
}
internal static void dual_inner_prod(int[] x, int x_ptr, int[] y01,int y01_ptr, int[] y02, int y02_ptr, int N, out int xy1, out int xy2)
{
int i;
int xy01 = 0;
int xy02 = 0;
for (i = 0; i < N; i++)
{
xy01 = Inlines.MAC16_16(xy01, x[x_ptr + i], y01[y01_ptr + i]);
xy02 = Inlines.MAC16_16(xy02, x[x_ptr + i], y02[y02_ptr + i]);
}
xy1 = xy01;
xy2 = xy02;
}
}
}
#endif

View File

@@ -0,0 +1,352 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if UNSAFE
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
internal static class Kernels
{
internal static unsafe void celt_fir(
short* x,
short[] num,
short* y,
int N,
int ord,
short[] mem
)
{
int i, j;
short[] rnum = new short[ord];
short[] local_x = new short[N + ord];
fixed (short* prnum = rnum, plocal_x = local_x)
{
for (i = 0; i < ord; i++)
{
prnum[i] = num[ord - i - 1];
}
for (i = 0; i < ord; i++)
{
plocal_x[i] = mem[ord - i - 1];
}
for (i = 0; i < N; i++)
{
plocal_x[i + ord] = x[i];
}
for (i = 0; i < ord; i++)
{
mem[i] = x[N - i - 1];
}
short* py = y;
for (i = 0; i < N - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
short* local_x2 = plocal_x + i;
xcorr_kernel(prnum, local_x2, ref sum0, ref sum1, ref sum2, ref sum3, ord);
py[0] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT))));
py[1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i + 1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT))));
py[2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i + 2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT))));
py[3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i + 3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT))));
py += 4;
}
for (; i < N; i++)
{
int sum = 0;
for (j = 0; j < ord; j++)
{
sum = Inlines.MAC16_16(sum, prnum[j], plocal_x[i + j]);
}
*py = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT))));
py++;
}
}
}
internal static unsafe void celt_fir(
int* px,
int* pnum,
int* py,
int N,
int ord,
int[] mem
)
{
int i, j;
int[] rnum = new int[ord];
int[] local_x = new int[N + ord];
fixed (int* prnum = rnum, plocal_x_base = local_x)
{
for (i = 0; i < ord; i++)
{
rnum[i] = pnum[ord - i - 1];
}
for (i = 0; i < ord; i++)
{
local_x[i] = mem[ord - i - 1];
}
for (i = 0; i < N; i++)
{
local_x[i + ord] = px[i];
}
for (i = 0; i < ord; i++)
{
mem[i] = px[N - i - 1];
}
int* px2 = px;
for (i = 0; i < N - 3; i += 4)
{
int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
int* plocal_x = plocal_x_base + i;
xcorr_kernel(prnum, plocal_x, ref sum0, ref sum1, ref sum2, ref sum3, ord);
py[0] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[0]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT))));
py[1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT))));
py[2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT))));
py[3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT))));
py += 4;
px2 += 4;
}
for (; i < N; i++)
{
int sum = 0;
for (j = 0; j < ord; j++)
{
sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]);
}
*py = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px[i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT))));
py++;
}
}
}
/// <summary>
/// OPT: This is the kernel you really want to optimize. It gets used a lot by the prefilter and by the PLC.
/// </summary>
/// <param name="x"></param>
/// <param name="py"></param>
/// <param name="sum0"></param>
/// <param name="len"></param>
internal unsafe static void xcorr_kernel(short* x, short* py, ref int sum0, ref int sum1, ref int sum2, ref int sum3, int len)
{
int j;
short y_0, y_1, y_2, y_3;
Inlines.OpusAssert(len >= 3);
y_3 = 0; /* gcc doesn't realize that y_3 can't be used uninitialized */
y_0 = *py++;
y_1 = *py++;
y_2 = *py++;
for (j = 0; j < len - 3; j += 4)
{
short tmp;
tmp = *x++;
y_3 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_0);
sum1 = Inlines.MAC16_16(sum1, tmp, y_1);
sum2 = Inlines.MAC16_16(sum2, tmp, y_2);
sum3 = Inlines.MAC16_16(sum3, tmp, y_3);
tmp = *x++;
y_0 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_1);
sum1 = Inlines.MAC16_16(sum1, tmp, y_2);
sum2 = Inlines.MAC16_16(sum2, tmp, y_3);
sum3 = Inlines.MAC16_16(sum3, tmp, y_0);
tmp = *x++;
y_1 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_2);
sum1 = Inlines.MAC16_16(sum1, tmp, y_3);
sum2 = Inlines.MAC16_16(sum2, tmp, y_0);
sum3 = Inlines.MAC16_16(sum3, tmp, y_1);
tmp = *x++;
y_2 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_3);
sum1 = Inlines.MAC16_16(sum1, tmp, y_0);
sum2 = Inlines.MAC16_16(sum2, tmp, y_1);
sum3 = Inlines.MAC16_16(sum3, tmp, y_2);
}
if (j++ < len)
{
short tmp;
tmp = *x++;
y_3 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_0);
sum1 = Inlines.MAC16_16(sum1, tmp, y_1);
sum2 = Inlines.MAC16_16(sum2, tmp, y_2);
sum3 = Inlines.MAC16_16(sum3, tmp, y_3);
}
if (j++ < len)
{
short tmp;
tmp = *x++;
y_0 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_1);
sum1 = Inlines.MAC16_16(sum1, tmp, y_2);
sum2 = Inlines.MAC16_16(sum2, tmp, y_3);
sum3 = Inlines.MAC16_16(sum3, tmp, y_0);
}
if (j < len)
{
short tmp;
tmp = *x++;
y_1 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_2);
sum1 = Inlines.MAC16_16(sum1, tmp, y_3);
sum2 = Inlines.MAC16_16(sum2, tmp, y_0);
sum3 = Inlines.MAC16_16(sum3, tmp, y_1);
}
}
internal unsafe static void xcorr_kernel(int* px, int* py, ref int sum0, ref int sum1, ref int sum2, ref int sum3, int len)
{
int j;
int y_0, y_1, y_2, y_3;
Inlines.OpusAssert(len >= 3);
y_3 = 0; /* gcc doesn't realize that y_3 can't be used uninitialized */
y_0 = *py++;
y_1 = *py++;
y_2 = *py++;
for (j = 0; j < len - 3; j += 4)
{
int tmp;
tmp = *px++;
y_3 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_0);
sum1 = Inlines.MAC16_16(sum1, tmp, y_1);
sum2 = Inlines.MAC16_16(sum2, tmp, y_2);
sum3 = Inlines.MAC16_16(sum3, tmp, y_3);
tmp = *px++;
y_0 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_1);
sum1 = Inlines.MAC16_16(sum1, tmp, y_2);
sum2 = Inlines.MAC16_16(sum2, tmp, y_3);
sum3 = Inlines.MAC16_16(sum3, tmp, y_0);
tmp = *px++;
y_1 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_2);
sum1 = Inlines.MAC16_16(sum1, tmp, y_3);
sum2 = Inlines.MAC16_16(sum2, tmp, y_0);
sum3 = Inlines.MAC16_16(sum3, tmp, y_1);
tmp = *px++;
y_2 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_3);
sum1 = Inlines.MAC16_16(sum1, tmp, y_0);
sum2 = Inlines.MAC16_16(sum2, tmp, y_1);
sum3 = Inlines.MAC16_16(sum3, tmp, y_2);
}
if (j++ < len)
{
int tmp;
tmp = *px++;
y_3 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_0);
sum1 = Inlines.MAC16_16(sum1, tmp, y_1);
sum2 = Inlines.MAC16_16(sum2, tmp, y_2);
sum3 = Inlines.MAC16_16(sum3, tmp, y_3);
}
if (j++ < len)
{
int tmp;
tmp = *px++;
y_0 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_1);
sum1 = Inlines.MAC16_16(sum1, tmp, y_2);
sum2 = Inlines.MAC16_16(sum2, tmp, y_3);
sum3 = Inlines.MAC16_16(sum3, tmp, y_0);
}
if (j < len)
{
int tmp;
tmp = *px++;
y_1 = *py++;
sum0 = Inlines.MAC16_16(sum0, tmp, y_2);
sum1 = Inlines.MAC16_16(sum1, tmp, y_3);
sum2 = Inlines.MAC16_16(sum2, tmp, y_0);
sum3 = Inlines.MAC16_16(sum3, tmp, y_1);
}
}
internal static unsafe int celt_inner_prod(short* x, short* y, int N)
{
int i;
int xy = 0;
for (i = 0; i < N; i++)
xy = Inlines.MAC16_16(xy, x[i], y[i]);
return xy;
}
internal static unsafe int celt_inner_prod(int* x, int* y, int N)
{
int i;
int xy = 0;
for (i = 0; i < N; i++)
xy = Inlines.MAC16_16(xy, x[i], y[i]);
return xy;
}
internal static unsafe void dual_inner_prod(int* x, int* y01, int* y02, int N, out int xy1, out int xy2)
{
int i;
int xy01 = 0;
int xy02 = 0;
for (i = 0; i < N; i++)
{
xy01 = Inlines.MAC16_16(xy01, x[i], y01[i]);
xy02 = Inlines.MAC16_16(xy02, x[i], y02[i]);
}
xy1 = xy01;
xy2 = xy02;
}
}
}
#endif

View File

@@ -0,0 +1,455 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Copyright (c) 2003-2004, Mark Borgerding
Modified from KISS-FFT by Jean-Marc Valin
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* This code is originally from Mark Borgerding's KISS-FFT but has been
heavily modified to better suit Opus */
#if !UNSAFE
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
using System.Threading;
internal static class KissFFT
{
//public const int SAMP_MAX = 2147483647;
//public const int SAMP_MIN = 0 - SAMP_MAX;
//public const int TWID_MAX = 32767;
//public const int TRIG_UPSCALE = 1;
internal const int MAXFACTORS = 8;
internal static int S_MUL(int a, int b)
{
return Inlines.MULT16_32_Q15(b, a);
}
internal static int S_MUL(int a, short b)
{
return Inlines.MULT16_32_Q15(b, a);
}
internal static int HALF_OF(int x)
{
return x >> 1;
}
internal static void kf_bfly2(int[] Fout, int fout_ptr, int m, int N)
{
int Fout2;
int i;
{
short tw;
tw = ((short)(0.5 + (0.7071067812f) * (((int)1) << (15))))/*Inlines.QCONST16(0.7071067812f, 15)*/;
/* We know that m==4 here because the radix-2 is just after a radix-4 */
Inlines.OpusAssert(m == 4);
for (i = 0; i < N; i++)
{
int t_r, t_i;
Fout2 = fout_ptr + 8;
t_r = Fout[Fout2 + 0];
t_i = Fout[Fout2 + 1];
Fout[Fout2 + 0] = Fout[fout_ptr + 0] - t_r;
Fout[Fout2 + 1] = Fout[fout_ptr + 1] - t_i;
Fout[fout_ptr + 0] += t_r;
Fout[fout_ptr + 1] += t_i;
t_r = S_MUL(Fout[Fout2 + 2] + Fout[Fout2 + 3], tw);
t_i = S_MUL(Fout[Fout2 + 3] - Fout[Fout2 + 2], tw);
Fout[Fout2 + 2] = Fout[fout_ptr + 2] - t_r;
Fout[Fout2 + 3] = Fout[fout_ptr + 3] - t_i;
Fout[fout_ptr + 2] += t_r;
Fout[fout_ptr + 3] += t_i;
t_r = Fout[Fout2 + 5];
t_i = 0 - Fout[Fout2 + 4];
Fout[Fout2 + 4] = Fout[fout_ptr + 4] - t_r;
Fout[Fout2 + 5] = Fout[fout_ptr + 5] - t_i;
Fout[fout_ptr + 4] += t_r;
Fout[fout_ptr + 5] += t_i;
t_r = S_MUL(Fout[Fout2 + 7] - Fout[Fout2 + 6], tw);
t_i = S_MUL(0 - Fout[Fout2 + 7] - Fout[Fout2 + 6], tw);
Fout[Fout2 + 6] = Fout[fout_ptr + 6] - t_r;
Fout[Fout2 + 7] = Fout[fout_ptr + 7] - t_i;
Fout[fout_ptr + 6] += t_r;
Fout[fout_ptr + 7] += t_i;
fout_ptr += 16;
}
}
}
internal static void kf_bfly4(
int[] Fout,
int fout_ptr,
int fstride,
FFTState st,
int m,
int N,
int mm)
{
int i;
if (m == 1)
{
/* Degenerate case where all the twiddles are 1. */
int scratch0, scratch1, scratch2, scratch3;
for (i = 0; i < N; i++)
{
scratch0 = Fout[fout_ptr + 0] - Fout[fout_ptr + 4];
scratch1 = Fout[fout_ptr + 1] - Fout[fout_ptr + 5];
Fout[fout_ptr + 0] += Fout[fout_ptr + 4];
Fout[fout_ptr + 1] += Fout[fout_ptr + 5];
scratch2 = Fout[fout_ptr + 2] + Fout[fout_ptr + 6];
scratch3 = Fout[fout_ptr + 3] + Fout[fout_ptr + 7];
Fout[fout_ptr + 4] = Fout[fout_ptr + 0] - scratch2;
Fout[fout_ptr + 5] = Fout[fout_ptr + 1] - scratch3;
Fout[fout_ptr + 0] += scratch2;
Fout[fout_ptr + 1] += scratch3;
scratch2 = Fout[fout_ptr + 2] - Fout[fout_ptr + 6];
scratch3 = Fout[fout_ptr + 3] - Fout[fout_ptr + 7];
Fout[fout_ptr + 2] = scratch0 + scratch3;
Fout[fout_ptr + 3] = scratch1 - scratch2;
Fout[fout_ptr + 6] = scratch0 - scratch3;
Fout[fout_ptr + 7] = scratch1 + scratch2;
fout_ptr += 8;
}
}
else
{
int j;
int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7, scratch8, scratch9, scratch10, scratch11;
int tw1, tw2, tw3;
int Fout_beg = fout_ptr;
for (i = 0; i < N; i++)
{
fout_ptr = Fout_beg + 2 * i * mm;
int m1 = fout_ptr + (2 * m);
int m2 = fout_ptr + (4 * m);
int m3 = fout_ptr + (6 * m);
tw3 = tw2 = tw1 = 0;
/* m is guaranteed to be a multiple of 4. */
for (j = 0; j < m; j++)
{
scratch0 = (S_MUL(Fout[m1], st.twiddles[tw1 ]) - S_MUL(Fout[m1 + 1], st.twiddles[tw1 + 1]));
scratch1 = (S_MUL(Fout[m1], st.twiddles[tw1 + 1]) + S_MUL(Fout[m1 + 1], st.twiddles[tw1]));
scratch2 = (S_MUL(Fout[m2], st.twiddles[tw2 ]) - S_MUL(Fout[m2 + 1], st.twiddles[tw2 + 1]));
scratch3 = (S_MUL(Fout[m2], st.twiddles[tw2 + 1]) + S_MUL(Fout[m2 + 1], st.twiddles[tw2]));
scratch4 = (S_MUL(Fout[m3], st.twiddles[tw3 ]) - S_MUL(Fout[m3 + 1], st.twiddles[tw3 + 1]));
scratch5 = (S_MUL(Fout[m3], st.twiddles[tw3 + 1]) + S_MUL(Fout[m3 + 1], st.twiddles[tw3]));
scratch10 = Fout[fout_ptr] - scratch2;
scratch11 = Fout[fout_ptr + 1] - scratch3;
Fout[fout_ptr] += scratch2;
Fout[fout_ptr + 1] += scratch3;
scratch6 = scratch0 + scratch4;
scratch7 = scratch1 + scratch5;
scratch8 = scratch0 - scratch4;
scratch9 = scratch1 - scratch5;
Fout[m2] = Fout[fout_ptr] - scratch6;
Fout[m2 + 1] = Fout[fout_ptr + 1] - scratch7;
tw1 += fstride * 2;
tw2 += fstride * 4;
tw3 += fstride * 6;
Fout[fout_ptr] += scratch6;
Fout[fout_ptr + 1] += scratch7;
Fout[m1] = scratch10 + scratch9;
Fout[m1 + 1] = scratch11 - scratch8;
Fout[m3] = scratch10 - scratch9;
Fout[m3 + 1] = scratch11 + scratch8;
fout_ptr += 2;
m1 += 2;
m2 += 2;
m3 += 2;
}
}
}
}
internal static void kf_bfly3(
int[] Fout,
int fout_ptr,
int fstride,
FFTState st,
int m,
int N,
int mm
)
{
int i;
int k;
int m1 = 2 * m;
int m2 = 4 * m;
int tw1, tw2;
int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7;
int Fout_beg = fout_ptr;
for (i = 0; i < N; i++)
{
fout_ptr = Fout_beg + 2 * i * mm;
tw1 = tw2 = 0;
/* For non-custom modes, m is guaranteed to be a multiple of 4. */
k = m;
do
{
scratch2 = (S_MUL(Fout[fout_ptr + m1], st.twiddles[tw1]) - S_MUL(Fout[fout_ptr + m1 + 1], st.twiddles[tw1 + 1]));
scratch3 = (S_MUL(Fout[fout_ptr + m1], st.twiddles[tw1 + 1]) + S_MUL(Fout[fout_ptr + m1 + 1], st.twiddles[tw1]));
scratch4 = (S_MUL(Fout[fout_ptr + m2], st.twiddles[tw2]) - S_MUL(Fout[fout_ptr + m2 + 1], st.twiddles[tw2 + 1]));
scratch5 = (S_MUL(Fout[fout_ptr + m2], st.twiddles[tw2 + 1]) + S_MUL(Fout[fout_ptr + m2 + 1], st.twiddles[tw2]));
scratch6 = scratch2 + scratch4;
scratch7 = scratch3 + scratch5;
scratch0 = scratch2 - scratch4;
scratch1 = scratch3 - scratch5;
tw1 += fstride * 2;
tw2 += fstride * 4;
Fout[fout_ptr + m1] = Fout[fout_ptr + 0] - HALF_OF(scratch6);
Fout[fout_ptr + m1 + 1] = Fout[fout_ptr + 1] - HALF_OF(scratch7);
scratch0 = S_MUL(scratch0, -28378);
scratch1 = S_MUL(scratch1, -28378);
Fout[fout_ptr + 0] += scratch6;
Fout[fout_ptr + 1] += scratch7;
Fout[fout_ptr + m2] = Fout[fout_ptr + m1] + scratch1;
Fout[fout_ptr + m2 + 1] = Fout[fout_ptr + m1 + 1] - scratch0;
Fout[fout_ptr + m1] -= scratch1;
Fout[fout_ptr + m1 + 1] += scratch0;
fout_ptr += 2;
} while ((--k) != 0);
}
}
internal static void kf_bfly5(
int[] Fout,
int fout_ptr,
int fstride,
FFTState st,
int m,
int N,
int mm
)
{
int Fout0, Fout1, Fout2, Fout3, Fout4;
int i, u;
int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5,
scratch6, scratch7, scratch8, scratch9, scratch10, scratch11,
scratch12,scratch13, scratch14, scratch15, scratch16, scratch17,
scratch18, scratch19, scratch20, scratch21, scratch22, scratch23,
scratch24, scratch25;
int Fout_beg = fout_ptr;
short ya_r = 10126;
short ya_i = -31164;
short yb_r = -26510;
short yb_i = -19261;
int tw1, tw2, tw3, tw4;
for (i = 0; i < N; i++)
{
tw1 = tw2 = tw3 = tw4 = 0;
fout_ptr = Fout_beg + 2 * i * mm;
Fout0 = fout_ptr;
Fout1 = fout_ptr + (2 * m);
Fout2 = fout_ptr + (4 * m);
Fout3 = fout_ptr + (6 * m);
Fout4 = fout_ptr + (8 * m);
/* For non-custom modes, m is guaranteed to be a multiple of 4. */
for (u = 0; u < m; ++u)
{
scratch0 = Fout[Fout0 + 0];
scratch1 = Fout[Fout0 + 1];
scratch2 = (S_MUL(Fout[Fout1 + 0], st.twiddles[tw1]) - S_MUL(Fout[Fout1 + 1], st.twiddles[tw1 + 1]));
scratch3 = (S_MUL(Fout[Fout1 + 0], st.twiddles[tw1 + 1]) + S_MUL(Fout[Fout1 + 1], st.twiddles[tw1]));
scratch4 = (S_MUL(Fout[Fout2 + 0], st.twiddles[tw2]) - S_MUL(Fout[Fout2 + 1], st.twiddles[tw2 + 1]));
scratch5 = (S_MUL(Fout[Fout2 + 0], st.twiddles[tw2 + 1]) + S_MUL(Fout[Fout2 + 1], st.twiddles[tw2]));
scratch6 = (S_MUL(Fout[Fout3 + 0], st.twiddles[tw3]) - S_MUL(Fout[Fout3 + 1], st.twiddles[tw3 + 1]));
scratch7 = (S_MUL(Fout[Fout3 + 0], st.twiddles[tw3 + 1]) + S_MUL(Fout[Fout3 + 1], st.twiddles[tw3]));
scratch8 = (S_MUL(Fout[Fout4 + 0], st.twiddles[tw4]) - S_MUL(Fout[Fout4 + 1], st.twiddles[tw4 + 1]));
scratch9 = (S_MUL(Fout[Fout4 + 0], st.twiddles[tw4 + 1]) + S_MUL(Fout[Fout4 + 1], st.twiddles[tw4]));
tw1 += (2 * fstride);
tw2 += (4 * fstride);
tw3 += (6 * fstride);
tw4 += (8 * fstride);
scratch14 = scratch2 + scratch8;
scratch15 = scratch3 + scratch9;
scratch20 = scratch2 - scratch8;
scratch21 = scratch3 - scratch9;
scratch16 = scratch4 + scratch6;
scratch17 = scratch5 + scratch7;
scratch18 = scratch4 - scratch6;
scratch19 = scratch5 - scratch7;
Fout[Fout0 + 0] += scratch14 + scratch16;
Fout[Fout0 + 1] += scratch15 + scratch17;
scratch10 = scratch0 + S_MUL(scratch14, ya_r) + S_MUL(scratch16, yb_r);
scratch11 = scratch1 + S_MUL(scratch15, ya_r) + S_MUL(scratch17, yb_r);
scratch12 = S_MUL(scratch21, ya_i) + S_MUL(scratch19, yb_i);
scratch13 = 0 - S_MUL(scratch20, ya_i) - S_MUL(scratch18, yb_i);
Fout[Fout1 + 0] = scratch10 - scratch12;
Fout[Fout1 + 1] = scratch11 - scratch13;
Fout[Fout4 + 0] = scratch10 + scratch12;
Fout[Fout4 + 1] = scratch11 + scratch13;
scratch22 = scratch0 + S_MUL(scratch14, yb_r) + S_MUL(scratch16, ya_r);
scratch23 = scratch1 + S_MUL(scratch15, yb_r) + S_MUL(scratch17, ya_r);
scratch24 = 0 - S_MUL(scratch21, yb_i) + S_MUL(scratch19, ya_i);
scratch25 = S_MUL(scratch20, yb_i) - S_MUL(scratch18, ya_i);
Fout[Fout2 + 0] = scratch22 + scratch24;
Fout[Fout2 + 1] = scratch23 + scratch25;
Fout[Fout3 + 0] = scratch22 - scratch24;
Fout[Fout3 + 1] = scratch23 - scratch25;
Fout0 += 2;
Fout1 += 2;
Fout2 += 2;
Fout3 += 2;
Fout4 += 2;
}
}
}
internal static void opus_fft_impl(FFTState st, int[] fout, int fout_ptr)
{
int m2, m;
int p;
int L;
int[] fstride = new int[MAXFACTORS];
int i;
int shift;
/* st.shift can be -1 */
shift = st.shift > 0 ? st.shift : 0;
fstride[0] = 1;
L = 0;
do
{
p = st.factors[2 * L];
m = st.factors[2 * L + 1];
fstride[L + 1] = fstride[L] * p;
L++;
} while (m != 1);
m = st.factors[2 * L - 1];
for (i = L - 1; i >= 0; i--)
{
if (i != 0)
m2 = st.factors[2 * i - 1];
else
m2 = 1;
switch (st.factors[2 * i])
{
case 2:
kf_bfly2(fout, fout_ptr, m, fstride[i]);
break;
case 4:
kf_bfly4(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2);
break;
case 3:
kf_bfly3(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2);
break;
case 5:
kf_bfly5(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2);
break;
}
m = m2;
}
}
internal static void opus_fft(FFTState st, int[] fin, int[] fout)
{
int i;
/* Allows us to scale with MULT16_32_Q16() */
int scale_shift = st.scale_shift - 1;
short scale = st.scale;
Inlines.OpusAssert(fin != fout, "In-place FFT not supported");
/* Bit-reverse the input */
for (i = 0; i < st.nfft; i++)
{
fout[(2 * st.bitrev[i])] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i)]), scale_shift);
fout[(2 * st.bitrev[i] + 1)] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i) + 1]), scale_shift);
}
opus_fft_impl(st, fout, 0);
}
//internal static void opus_ifft(FFTState st, Pointer<int> fin, Pointer<int> fout)
//{
// int i;
// Inlines.OpusAssert(fin != fout, "In-place iFFT not supported");
// /* Bit-reverse the input */
// for (i = 0; i < st.nfft * 2; i++)
// {
// fout[st.bitrev[i]] = fin[i];
// }
// for (i = 1; i < st.nfft * 2; i += 2)
// {
// fout[i] = -fout[i];
// }
// opus_fft_impl(st, fout.Data, fout.Offset);
// for (i = 1; i < st.nfft * 2; i += 2)
// fout[i] = -fout[i];
//}
}
}
#endif

View File

@@ -0,0 +1,456 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Copyright (c) 2003-2004, Mark Borgerding
Modified from KISS-FFT by Jean-Marc Valin
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* This code is originally from Mark Borgerding's KISS-FFT but has been
heavily modified to better suit Opus */
#if UNSAFE
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
using System.Threading;
internal static class KissFFT
{
//public const int SAMP_MAX = 2147483647;
//public const int SAMP_MIN = 0 - SAMP_MAX;
//public const int TWID_MAX = 32767;
//public const int TRIG_UPSCALE = 1;
internal const int MAXFACTORS = 8;
internal static int S_MUL(int a, int b)
{
return Inlines.MULT16_32_Q15(b, a);
}
internal static int S_MUL(int a, short b)
{
return Inlines.MULT16_32_Q15(b, a);
}
internal static int HALF_OF(int x)
{
return x >> 1;
}
internal static unsafe void kf_bfly2(int* Fout, int m, int N)
{
int* Fout2;
int i;
{
short tw;
tw = ((short)(0.5 + (0.7071067812f) * (((int)1) << (15))))/*Inlines.QCONST16(0.7071067812f, 15)*/;
/* We know that m==4 here because the radix-2 is just after a radix-4 */
Inlines.OpusAssert(m == 4);
for (i = 0; i < N; i++)
{
int t_r, t_i;
Fout2 = Fout + 8;
t_r = *(Fout2);
t_i = *(Fout2 + 1);
*Fout2 = (*Fout) - t_r;
*(Fout2 + 1) = *(Fout + 1) - t_i;
*Fout += t_r;
*(Fout + 1) += t_i;
t_r = S_MUL(*(Fout2 + 2) + *(Fout2 + 3), tw);
t_i = S_MUL(*(Fout2 + 3) - *(Fout2 + 2), tw);
*(Fout2 + 2) = *(Fout + 2) - t_r;
*(Fout2 + 3) = *(Fout + 3) - t_i;
*(Fout + 2) += t_r;
*(Fout + 3) += t_i;
t_r = *(Fout2 + 5);
t_i = 0 - *(Fout2 + 4);
*(Fout2 + 4) = *(Fout + 4) - t_r;
*(Fout2 + 5) = *(Fout + 5) - t_i;
*(Fout + 4) += t_r;
*(Fout + 5) += t_i;
t_r = S_MUL(*(Fout2 + 7) - *(Fout2 + 6), tw);
t_i = S_MUL(0 - *(Fout2 + 7) - *(Fout2 + 6), tw);
*(Fout2 + 6) = *(Fout + 6) - t_r;
*(Fout2 + 7) = *(Fout + 7) - t_i;
*(Fout + 6) += t_r;
*(Fout + 7) += t_i;
Fout += 16;
}
}
}
internal static unsafe void kf_bfly4(
int* Fout,
int fstride,
FFTState st,
int m,
int N,
int mm)
{
int i;
if (m == 1)
{
/* Degenerate case where all the twiddles are 1. */
int scratch0, scratch1, scratch2, scratch3;
for (i = 0; i < N; i++)
{
scratch0 = *(Fout) - *(Fout + 4);
scratch1 = *(Fout + 1) - *(Fout + 5);
*(Fout + 0) += *(Fout + 4);
*(Fout + 1) += *(Fout + 5);
scratch2 = *(Fout + 2) + *(Fout + 6);
scratch3 = *(Fout + 3) + *(Fout + 7);
*(Fout + 4) = *(Fout + 0) - scratch2;
*(Fout + 5) = *(Fout + 1) - scratch3;
*(Fout + 0) += scratch2;
*(Fout + 1) += scratch3;
scratch2 = *(Fout + 2) - *(Fout + 6);
scratch3 = *(Fout + 3) - *(Fout + 7);
*(Fout + 2) = scratch0 + scratch3;
*(Fout + 3) = scratch1 - scratch2;
*(Fout + 6) = scratch0 - scratch3;
*(Fout + 7) = scratch1 + scratch2;
Fout += 8;
}
}
else
{
int j;
int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7, scratch8, scratch9, scratch10, scratch11;
int tw1, tw2, tw3;
int* Fout_beg = Fout;
for (i = 0; i < N; i++)
{
Fout = Fout_beg + 2 * i * mm;
int* m1 = Fout + (2 * m);
int* m2 = Fout + (4 * m);
int* m3 = Fout + (6 * m);
tw3 = tw2 = tw1 = 0;
/* m is guaranteed to be a multiple of 4. */
for (j = 0; j < m; j++)
{
scratch0 = (S_MUL(*m1, st.twiddles[tw1]) - S_MUL(*(m1 + 1), st.twiddles[tw1 + 1]));
scratch1 = (S_MUL(*m1, st.twiddles[tw1 + 1]) + S_MUL(*(m1 + 1), st.twiddles[tw1]));
scratch2 = (S_MUL(*m2, st.twiddles[tw2]) - S_MUL(*(m2 + 1), st.twiddles[tw2 + 1]));
scratch3 = (S_MUL(*m2, st.twiddles[tw2 + 1]) + S_MUL(*(m2 + 1), st.twiddles[tw2]));
scratch4 = (S_MUL(*m3, st.twiddles[tw3]) - S_MUL(*(m3 + 1), st.twiddles[tw3 + 1]));
scratch5 = (S_MUL(*m3, st.twiddles[tw3 + 1]) + S_MUL(*(m3 + 1), st.twiddles[tw3]));
scratch10 = *(Fout) - scratch2;
scratch11 = *(Fout + 1) - scratch3;
*(Fout) += scratch2;
*(Fout + 1) += scratch3;
scratch6 = scratch0 + scratch4;
scratch7 = scratch1 + scratch5;
scratch8 = scratch0 - scratch4;
scratch9 = scratch1 - scratch5;
*m2 = *(Fout) - scratch6;
*(m2 + 1) = *(Fout + 1) - scratch7;
tw1 += fstride * 2;
tw2 += fstride * 4;
tw3 += fstride * 6;
*(Fout ) += scratch6;
*(Fout + 1) += scratch7;
*m1 = scratch10 + scratch9;
*(m1 + 1) = scratch11 - scratch8;
*m3 = scratch10 - scratch9;
*(m3 + 1) = scratch11 + scratch8;
Fout += 2;
m1 += 2;
m2 += 2;
m3 += 2;
}
}
}
}
internal static unsafe void kf_bfly3(
int* Fout,
int fstride,
FFTState st,
int m,
int N,
int mm
)
{
int i;
int k;
int m1 = 2 * m;
int m2 = 4 * m;
int tw1, tw2;
int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7;
int* Fout_beg = Fout;
for (i = 0; i < N; i++)
{
Fout = Fout_beg + 2 * i * mm;
tw1 = tw2 = 0;
/* For non-custom modes, m is guaranteed to be a multiple of 4. */
k = m;
do
{
scratch2 = (S_MUL(*(Fout + m1), st.twiddles[tw1]) - S_MUL(*(Fout + m1 + 1), st.twiddles[tw1 + 1]));
scratch3 = (S_MUL(*(Fout + m1), st.twiddles[tw1 + 1]) + S_MUL(*(Fout + m1 + 1), st.twiddles[tw1]));
scratch4 = (S_MUL(*(Fout + m2), st.twiddles[tw2]) - S_MUL(*(Fout + m2 + 1), st.twiddles[tw2 + 1]));
scratch5 = (S_MUL(*(Fout + m2), st.twiddles[tw2 + 1]) + S_MUL(*(Fout + m2 + 1), st.twiddles[tw2]));
scratch6 = scratch2 + scratch4;
scratch7 = scratch3 + scratch5;
scratch0 = scratch2 - scratch4;
scratch1 = scratch3 - scratch5;
tw1 += fstride * 2;
tw2 += fstride * 4;
*(Fout + m1) = *(Fout) - HALF_OF(scratch6);
*(Fout + m1 + 1) = *(Fout + 1) - HALF_OF(scratch7);
scratch0 = S_MUL(scratch0, -28378);
scratch1 = S_MUL(scratch1, -28378);
*(Fout) += scratch6;
*(Fout + 1) += scratch7;
*(Fout + m2) = *(Fout + m1) + scratch1;
*(Fout + m2 + 1) = *(Fout + m1 + 1) - scratch0;
*(Fout + m1) -= scratch1;
*(Fout + m1 + 1) += scratch0;
Fout += 2;
} while ((--k) != 0);
}
}
internal static unsafe void kf_bfly5(
int* Fout,
int fstride,
FFTState st,
int m,
int N,
int mm
)
{
int* Fout0, Fout1, Fout2, Fout3, Fout4;
int i, u;
int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5,
scratch6, scratch7, scratch8, scratch9, scratch10, scratch11,
scratch12, scratch13, scratch14, scratch15, scratch16, scratch17,
scratch18, scratch19, scratch20, scratch21, scratch22, scratch23,
scratch24, scratch25;
int* Fout_beg = Fout;
short ya_r = 10126;
short ya_i = -31164;
short yb_r = -26510;
short yb_i = -19261;
int tw1, tw2, tw3, tw4;
for (i = 0; i < N; i++)
{
tw1 = tw2 = tw3 = tw4 = 0;
Fout = Fout_beg + 2 * i * mm;
Fout0 = Fout;
Fout1 = Fout + (2 * m);
Fout2 = Fout + (4 * m);
Fout3 = Fout + (6 * m);
Fout4 = Fout + (8 * m);
/* For non-custom modes, m is guaranteed to be a multiple of 4. */
for (u = 0; u < m; ++u)
{
scratch0 = *(Fout0 + 0);
scratch1 = *(Fout0 + 1);
scratch2 = (S_MUL(*(Fout1 + 0), st.twiddles[tw1]) - S_MUL(*(Fout1 + 1), st.twiddles[tw1 + 1]));
scratch3 = (S_MUL(*(Fout1 + 0), st.twiddles[tw1 + 1]) + S_MUL(*(Fout1 + 1), st.twiddles[tw1]));
scratch4 = (S_MUL(*(Fout2 + 0), st.twiddles[tw2]) - S_MUL(*(Fout2 + 1), st.twiddles[tw2 + 1]));
scratch5 = (S_MUL(*(Fout2 + 0), st.twiddles[tw2 + 1]) + S_MUL(*(Fout2 + 1), st.twiddles[tw2]));
scratch6 = (S_MUL(*(Fout3 + 0), st.twiddles[tw3]) - S_MUL(*(Fout3 + 1), st.twiddles[tw3 + 1]));
scratch7 = (S_MUL(*(Fout3 + 0), st.twiddles[tw3 + 1]) + S_MUL(*(Fout3 + 1), st.twiddles[tw3]));
scratch8 = (S_MUL(*(Fout4 + 0), st.twiddles[tw4]) - S_MUL(*(Fout4 + 1), st.twiddles[tw4 + 1]));
scratch9 = (S_MUL(*(Fout4 + 0), st.twiddles[tw4 + 1]) + S_MUL(*(Fout4 + 1), st.twiddles[tw4]));
tw1 += (2 * fstride);
tw2 += (4 * fstride);
tw3 += (6 * fstride);
tw4 += (8 * fstride);
scratch14 = scratch2 + scratch8;
scratch15 = scratch3 + scratch9;
scratch20 = scratch2 - scratch8;
scratch21 = scratch3 - scratch9;
scratch16 = scratch4 + scratch6;
scratch17 = scratch5 + scratch7;
scratch18 = scratch4 - scratch6;
scratch19 = scratch5 - scratch7;
*(Fout0 + 0) += scratch14 + scratch16;
*(Fout0 + 1) += scratch15 + scratch17;
scratch10 = scratch0 + S_MUL(scratch14, ya_r) + S_MUL(scratch16, yb_r);
scratch11 = scratch1 + S_MUL(scratch15, ya_r) + S_MUL(scratch17, yb_r);
scratch12 = S_MUL(scratch21, ya_i) + S_MUL(scratch19, yb_i);
scratch13 = 0 - S_MUL(scratch20, ya_i) - S_MUL(scratch18, yb_i);
*(Fout1 + 0) = scratch10 - scratch12;
*(Fout1 + 1) = scratch11 - scratch13;
*(Fout4 + 0) = scratch10 + scratch12;
*(Fout4 + 1) = scratch11 + scratch13;
scratch22 = scratch0 + S_MUL(scratch14, yb_r) + S_MUL(scratch16, ya_r);
scratch23 = scratch1 + S_MUL(scratch15, yb_r) + S_MUL(scratch17, ya_r);
scratch24 = 0 - S_MUL(scratch21, yb_i) + S_MUL(scratch19, ya_i);
scratch25 = S_MUL(scratch20, yb_i) - S_MUL(scratch18, ya_i);
*(Fout2 + 0) = scratch22 + scratch24;
*(Fout2 + 1) = scratch23 + scratch25;
*(Fout3 + 0) = scratch22 - scratch24;
*(Fout3 + 1) = scratch23 - scratch25;
Fout0 += 2;
Fout1 += 2;
Fout2 += 2;
Fout3 += 2;
Fout4 += 2;
}
}
}
internal static unsafe void opus_fft_impl(FFTState st, int[] fout, int fout_ptr)
{
int m2, m;
int p;
int L;
int[] fstride = new int[MAXFACTORS];
int i;
int shift;
/* st.shift can be -1 */
shift = st.shift > 0 ? st.shift : 0;
fstride[0] = 1;
L = 0;
do
{
p = st.factors[2 * L];
m = st.factors[2 * L + 1];
fstride[L + 1] = fstride[L] * p;
L++;
} while (m != 1);
fixed (int* _fixed_fout = fout)
{
int* pfout = _fixed_fout + fout_ptr;
m = st.factors[2 * L - 1];
for (i = L - 1; i >= 0; i--)
{
if (i != 0)
m2 = st.factors[2 * i - 1];
else
m2 = 1;
switch (st.factors[2 * i])
{
case 2:
kf_bfly2(pfout, m, fstride[i]);
break;
case 4:
kf_bfly4(pfout, fstride[i] << shift, st, m, fstride[i], m2);
break;
case 3:
kf_bfly3(pfout, fstride[i] << shift, st, m, fstride[i], m2);
break;
case 5:
kf_bfly5(pfout, fstride[i] << shift, st, m, fstride[i], m2);
break;
}
m = m2;
}
}
}
internal static void opus_fft(FFTState st, int[] fin, int[] fout)
{
int i;
/* Allows us to scale with MULT16_32_Q16() */
int scale_shift = st.scale_shift - 1;
short scale = st.scale;
Inlines.OpusAssert(fin != fout, "In-place FFT not supported");
/* Bit-reverse the input */
for (i = 0; i < st.nfft; i++)
{
fout[(2 * st.bitrev[i])] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i)]), scale_shift);
fout[(2 * st.bitrev[i] + 1)] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i) + 1]), scale_shift);
}
opus_fft_impl(st, fout, 0);
}
//internal static void opus_ifft(FFTState st, Pointer<int> fin, Pointer<int> fout)
//{
// int i;
// Inlines.OpusAssert(fin != fout, "In-place iFFT not supported");
// /* Bit-reverse the input */
// for (i = 0; i < st.nfft * 2; i++)
// {
// fout[st.bitrev[i]] = fin[i];
// }
// for (i = 1; i < st.nfft * 2; i += 2)
// {
// fout[i] = -fout[i];
// }
// opus_fft_impl(st, fout.Data, fout.Offset);
// for (i = 1; i < st.nfft * 2; i += 2)
// fout[i] = -fout[i];
//}
}
}
#endif

View File

@@ -0,0 +1,157 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Celt.Enums;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace Concentus.Celt
{
internal static class Laplace
{
/* The minimum probability of an energy delta (out of 32768). */
private const int LAPLACE_LOG_MINP = 0;
private const uint LAPLACE_MINP = (1 << LAPLACE_LOG_MINP);
/* The minimum number of guaranteed representable energy deltas (in one
direction). */
private const int LAPLACE_NMIN = 16;
/* When called, decay is positive and at most 11456. */
internal static uint ec_laplace_get_freq1(uint fs0, int decay)
{
uint ft;
ft = 32768 - LAPLACE_MINP * (2 * LAPLACE_NMIN) - fs0;
return (uint)((ft * (int)(16384 - decay)) >> 15);
}
internal static void ec_laplace_encode(EntropyCoder enc, ref int value, uint fs, int decay)
{
uint fl;
int val = value;
fl = 0;
if (val != 0)
{
int s;
int i;
s = 0 - (val < 0 ? 1 : 0);
val = (val + s) ^ s;
fl = fs;
fs = ec_laplace_get_freq1(fs, decay);
/* Search the decaying part of the PDF.*/
for (i = 1; fs > 0 && i < val; i++)
{
fs *= 2;
fl += fs + 2 * LAPLACE_MINP;
fs = (uint)((fs * (int)decay) >> 15);
}
/* Everything beyond that has probability LAPLACE_MINP. */
if (fs == 0)
{
int di;
int ndi_max;
ndi_max = (int)(32768 - fl + LAPLACE_MINP - 1) >> LAPLACE_LOG_MINP;
ndi_max = (ndi_max - s) >> 1;
di = Inlines.IMIN(val - i, ndi_max - 1);
fl += (uint)(2 * di + 1 + s) * LAPLACE_MINP;
fs = Inlines.IMIN(LAPLACE_MINP, 32768 - fl);
value = (i + di + s) ^ s;
}
else
{
fs += LAPLACE_MINP;
fl += (uint)(fs & ~s);
}
Inlines.OpusAssert(fl + fs <= 32768);
Inlines.OpusAssert(fs > 0);
}
enc.encode_bin(fl, fl + fs, 15);
}
internal static int ec_laplace_decode(EntropyCoder dec, uint fs, int decay)
{
int val = 0;
uint fl;
uint fm;
fm = dec.decode_bin(15);
fl = 0;
if (fm >= fs)
{
val++;
fl = fs;
fs = ec_laplace_get_freq1(fs, decay) + LAPLACE_MINP;
/* Search the decaying part of the PDF.*/
while (fs > LAPLACE_MINP && fm >= fl + 2 * fs)
{
fs *= 2;
fl += fs;
fs = (uint)(((fs - 2 * LAPLACE_MINP) * (int)decay) >> 15);
fs += LAPLACE_MINP;
val++;
}
/* Everything beyond that has probability LAPLACE_MINP. */
if (fs <= LAPLACE_MINP)
{
int di;
di = (int)(fm - fl) >> (LAPLACE_LOG_MINP + 1);
val += di;
fl += (uint)(2 * di * LAPLACE_MINP);
}
if (fm < fl + fs)
val = -val;
else
fl += fs;
}
Inlines.OpusAssert(fl < 32768);
Inlines.OpusAssert(fs > 0);
Inlines.OpusAssert(fl <= fm);
Inlines.OpusAssert(fm < Inlines.IMIN(fl + fs, 32768));
dec.dec_update(fl, Inlines.IMIN(fl + fs, 32768), 32768);
return val;
}
}
}

View File

@@ -0,0 +1,370 @@
/* Copyright (c) 2003-2004, Mark Borgerding
Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Modified from KISS-FFT by Jean-Marc Valin
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
internal static class MDCT
{
#if !UNSAFE
/* Forward MDCT trashes the input array */
internal static void clt_mdct_forward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr,
int[] window, int overlap, int shift, int stride)
{
int i;
int N, N2, N4;
int[] f;
int[] f2;
FFTState st = l.kfft[shift];
short[] trig;
int trig_ptr = 0;
int scale;
int scale_shift = st.scale_shift - 1;
scale = st.scale;
N = l.n;
trig = l.trig;
for (i = 0; i < shift; i++)
{
N = N >> 1;
trig_ptr += N;
}
N2 = N >> 1;
N4 = N >> 2;
f = new int[N2];
f2 = new int[N4 * 2];
/* Consider the input to be composed of four blocks: [a, b, c, d] */
/* Window, shuffle, fold */
{
/* Temp pointers to make it really clear to the compiler what we're doing */
int xp1 = input_ptr + (overlap >> 1);
int xp2 = input_ptr + N2 - 1 + (overlap >> 1);
int yp = 0;
int wp1 = (overlap >> 1);
int wp2 = ((overlap >> 1) - 1);
for (i = 0; i < ((overlap + 3) >> 2); i++)
{
/* Real part arranged as -d-cR, Imag part arranged as -b+aR*/
f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp1 + N2]) + Inlines.MULT16_32_Q15(window[wp1], input[xp2]);
f[yp++] = Inlines.MULT16_32_Q15(window[wp1], input[xp1]) - Inlines.MULT16_32_Q15(window[wp2], input[xp2 - N2]);
xp1 += 2;
xp2 -= 2;
wp1 += 2;
wp2 -= 2;
}
wp1 = 0;
wp2 = (overlap - 1);
for (; i < N4 - ((overlap + 3) >> 2); i++)
{
/* Real part arranged as a-bR, Imag part arranged as -c-dR */
f[yp++] = input[xp2];
f[yp++] = input[xp1];
xp1 += 2;
xp2 -= 2;
}
for (; i < N4; i++)
{
/* Real part arranged as a-bR, Imag part arranged as -c-dR */
f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp2]) - Inlines.MULT16_32_Q15(window[wp1], input[xp1 - N2]);
f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp1]) + Inlines.MULT16_32_Q15(window[wp1], input[xp2 + N2]);
xp1 += 2;
xp2 -= 2;
wp1 += 2;
wp2 -= 2;
}
}
/* Pre-rotation */
{
int yp = 0;
int t = trig_ptr;
for (i = 0; i < N4; i++)
{
short t0, t1;
int re, im, yr, yi;
t0 = trig[t + i];
t1 = trig[t + N4 + i];
re = f[yp++];
im = f[yp++];
yr = KissFFT.S_MUL(re, t0) - KissFFT.S_MUL(im, t1);
yi = KissFFT.S_MUL(im, t0) + KissFFT.S_MUL(re, t1);
f2[2 * st.bitrev[i]] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yr), scale_shift);
f2[2 * st.bitrev[i] + 1] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yi), scale_shift);
}
}
/* N/4 complex FFT, does not downscale anymore */
KissFFT.opus_fft_impl(st, f2, 0);
/* Post-rotate */
{
/* Temp pointers to make it really clear to the compiler what we're doing */
int fp = 0;
int yp1 = output_ptr;
int yp2 = output_ptr + (stride * (N2 - 1));
int t = trig_ptr;
for (i = 0; i < N4; i++)
{
int yr, yi;
yr = KissFFT.S_MUL(f2[fp + 1], trig[t + N4 + i]) - KissFFT.S_MUL(f2[fp], trig[t + i]);
yi = KissFFT.S_MUL(f2[fp], trig[t + N4 + i]) + KissFFT.S_MUL(f2[fp + 1], trig[t + i]);
output[yp1] = yr;
output[yp2] = yi;
fp += 2;
yp1 += (2 * stride);
yp2 -= (2 * stride);
}
}
}
#else
/* Forward MDCT trashes the input array */
internal static unsafe void clt_mdct_forward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr,
int[] window, int overlap, int shift, int stride)
{
int i;
int N, N2, N4;
int[] f;
int[] f2;
FFTState st = l.kfft[shift];
int scale;
int scale_shift = st.scale_shift - 1;
scale = st.scale;
N = l.n;
fixed (short* ptrig_base = l.trig)
{
short* trig = ptrig_base;
for (i = 0; i < shift; i++)
{
N = N >> 1;
trig += N;
}
N2 = N >> 1;
N4 = N >> 2;
f = new int[N2];
f2 = new int[N4 * 2];
fixed (int* pinput_base = input, pwindow = window, pf = f, pf2 = f2)
{
int* pinput = pinput_base + input_ptr;
/* Consider the input to be composed of four blocks: [a, b, c, d] */
/* Window, shuffle, fold */
{
/* Temp pointers to make it really clear to the compiler what we're doing */
int* xp1 = pinput + (overlap >> 1);
int* xp2 = pinput + N2 - 1 + (overlap >> 1);
int* yp = pf;
int* wp1 = pwindow + (overlap >> 1);
int* wp2 = pwindow + ((overlap >> 1) - 1);
for (i = 0; i < ((overlap + 3) >> 2); i++)
{
/* Real part arranged as -d-cR, Imag part arranged as -b+aR*/
*yp++ = Inlines.MULT16_32_Q15(*wp2, xp1[N2]) + Inlines.MULT16_32_Q15(*wp1, *xp2);
*yp++ = Inlines.MULT16_32_Q15(*wp1, *xp1) - Inlines.MULT16_32_Q15(*wp2, xp2[0 - N2]);
xp1 += 2;
xp2 -= 2;
wp1 += 2;
wp2 -= 2;
}
wp1 = pwindow;
wp2 = pwindow + (overlap - 1);
for (; i < N4 - ((overlap + 3) >> 2); i++)
{
/* Real part arranged as a-bR, Imag part arranged as -c-dR */
*yp++ = *xp2;
*yp++ = *xp1;
xp1 += 2;
xp2 -= 2;
}
for (; i < N4; i++)
{
/* Real part arranged as a-bR, Imag part arranged as -c-dR */
*yp++ = Inlines.MULT16_32_Q15(*wp2, *xp2) - Inlines.MULT16_32_Q15(*wp1, xp1[0 - N2]);
*yp++ = Inlines.MULT16_32_Q15(*wp2, *xp1) + Inlines.MULT16_32_Q15(*wp1, xp2[N2]);
xp1 += 2;
xp2 -= 2;
wp1 += 2;
wp2 -= 2;
}
}
/* Pre-rotation */
{
int* yp = pf;
short* t = trig;
for (i = 0; i < N4; i++)
{
short t0, t1;
int re, im, yr, yi;
t0 = t[i];
t1 = t[N4 + i];
re = *yp++;
im = *yp++;
yr = KissFFT.S_MUL(re, t0) - KissFFT.S_MUL(im, t1);
yi = KissFFT.S_MUL(im, t0) + KissFFT.S_MUL(re, t1);
pf2[2 * st.bitrev[i]] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yr), scale_shift);
pf2[2 * st.bitrev[i] + 1] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yi), scale_shift);
}
}
/* N/4 complex FFT, does not downscale anymore */
KissFFT.opus_fft_impl(st, f2, 0);
/* Post-rotate */
fixed (int* poutput_base = output)
{
/* Temp pointers to make it really clear to the compiler what we're doing */
int* fp = pf2;
int* yp1 = poutput_base + output_ptr;
int* yp2 = poutput_base + output_ptr + (stride * (N2 - 1));
short* t = trig;
for (i = 0; i < N4; i++)
{
int yr, yi;
yr = KissFFT.S_MUL(fp[1], t[N4 + i]) - KissFFT.S_MUL(fp[0], t[i]);
yi = KissFFT.S_MUL(fp[0], t[N4 + i]) + KissFFT.S_MUL(fp[1], t[i]);
*yp1 = yr;
*yp2 = yi;
fp += 2;
yp1 += (2 * stride);
yp2 -= (2 * stride);
}
}
}
}
}
#endif
internal static void clt_mdct_backward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr,
int[] window, int overlap, int shift, int stride)
{
int i;
int N, N2, N4;
int trig = 0;
int xp1, xp2, yp, yp0, yp1;
N = l.n;
for (i = 0; i < shift; i++)
{
N >>= 1;
trig += N;
}
N2 = N >> 1;
N4 = N >> 2;
/* Pre-rotate */
/* Temp pointers to make it really clear to the compiler what we're doing */
xp2 = input_ptr + (stride * (N2 - 1));
yp = output_ptr + (overlap >> 1);
short[] bitrev = l.kfft[shift].bitrev;
int bitrav_ptr = 0;
for (i = 0; i < N4; i++)
{
int rev = bitrev[bitrav_ptr++];
int ypr = yp + 2 * rev;
/* We swap real and imag because we use an FFT instead of an IFFT. */
output[ypr + 1] = KissFFT.S_MUL(input[xp2], l.trig[trig + i]) + KissFFT.S_MUL(input[input_ptr], l.trig[trig + N4 + i]); //yr
output[ypr] = KissFFT.S_MUL(input[input_ptr], l.trig[trig + i]) - KissFFT.S_MUL(input[xp2], l.trig[trig + N4 + i]); //yi
/* Storing the pre-rotation directly in the bitrev order. */
input_ptr += (2 * stride);
xp2 -= (2 * stride);
}
KissFFT.opus_fft_impl(l.kfft[shift], output, output_ptr + (overlap >> 1));
/* Post-rotate and de-shuffle from both ends of the buffer at once to make
it in-place. */
yp0 = output_ptr + (overlap >> 1);
yp1 = output_ptr + (overlap >> 1) + N2 - 2;
int t = trig;
/* Loop to (N4+1)>>1 to handle odd N4. When N4 is odd, the
middle pair will be computed twice. */
int tN4m1 = t + N4 - 1;
int tN2m1 = t + N2 - 1;
for (i = 0; i < (N4 + 1) >> 1; i++)
{
int re, im, yr, yi;
short t0, t1;
/* We swap real and imag because we're using an FFT instead of an IFFT. */
re = output[yp0 + 1];
im = output[yp0];
t0 = l.trig[t + i];
t1 = l.trig[t + N4 + i];
/* We'd scale up by 2 here, but instead it's done when mixing the windows */
yr = KissFFT.S_MUL(re, t0) + KissFFT.S_MUL(im, t1);
yi = KissFFT.S_MUL(re, t1) - KissFFT.S_MUL(im, t0);
/* We swap real and imag because we're using an FFT instead of an IFFT. */
re = output[yp1 + 1];
im = output[yp1];
output[yp0] = yr;
output[yp1 + 1] = yi;
t0 = l.trig[tN4m1 - i];
t1 = l.trig[tN2m1 - i];
/* We'd scale up by 2 here, but instead it's done when mixing the windows */
yr = KissFFT.S_MUL(re, t0) + KissFFT.S_MUL(im, t1);
yi = KissFFT.S_MUL(re, t1) - KissFFT.S_MUL(im, t0);
output[yp1] = yr;
output[yp0 + 1] = yi;
yp0 += 2;
yp1 -= 2;
}
/* Mirror on both sides for TDAC */
xp1 = output_ptr + overlap - 1;
yp1 = output_ptr;
int wp1 = 0;
int wp2 = (overlap - 1);
for (i = 0; i < overlap / 2; i++)
{
int x1 = output[xp1];
int x2 = output[yp1];
output[yp1++] = Inlines.MULT16_32_Q15(window[wp2], x2) - Inlines.MULT16_32_Q15(window[wp1], x1);
output[xp1--] = Inlines.MULT16_32_Q15(window[wp1], x2) + Inlines.MULT16_32_Q15(window[wp2], x1);
wp1++;
wp2--;
}
}
}
}

View File

@@ -0,0 +1,506 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
internal static class Pitch
{
internal static void find_best_pitch(int[] xcorr, int[] y, int len,
int max_pitch, int[]best_pitch,
int yshift, int maxcorr
)
{
int i, j;
int Syy = 1;
int best_num_0;
int best_num_1;
int best_den_0;
int best_den_1;
int xshift = Inlines.celt_ilog2(maxcorr) - 14;
best_num_0 = -1;
best_num_1 = -1;
best_den_0 = 0;
best_den_1 = 0;
best_pitch[0] = 0;
best_pitch[1] = 1;
for (j = 0; j < len; j++)
Syy = Inlines.ADD32(Syy, Inlines.SHR32(Inlines.MULT16_16(y[j], y[j]), yshift));
for (i = 0; i < max_pitch; i++)
{
if (xcorr[i] > 0)
{
int num;
int xcorr16;
xcorr16 = Inlines.EXTRACT16(Inlines.VSHR32(xcorr[i], xshift));
num = Inlines.MULT16_16_Q15((xcorr16), (xcorr16));
if (Inlines.MULT16_32_Q15(num, best_den_1) > Inlines.MULT16_32_Q15(best_num_1, Syy))
{
if (Inlines.MULT16_32_Q15(num, best_den_0) > Inlines.MULT16_32_Q15(best_num_0, Syy))
{
best_num_1 = best_num_0;
best_den_1 = best_den_0;
best_pitch[1] = best_pitch[0];
best_num_0 = num;
best_den_0 = Syy;
best_pitch[0] = i;
}
else
{
best_num_1 = num;
best_den_1 = Syy;
best_pitch[1] = i;
}
}
}
Syy += Inlines.SHR32(Inlines.MULT16_16(y[i + len], y[i + len]), yshift) - Inlines.SHR32(Inlines.MULT16_16(y[i], y[i]), yshift);
Syy = Inlines.MAX32(1, Syy);
}
}
internal static void celt_fir5(int[] x,
int[] num,
int[] y,
int N,
int[] mem)
{
int i;
int num0, num1, num2, num3, num4;
int mem0, mem1, mem2, mem3, mem4;
num0 = num[0];
num1 = num[1];
num2 = num[2];
num3 = num[3];
num4 = num[4];
mem0 = mem[0];
mem1 = mem[1];
mem2 = mem[2];
mem3 = mem[3];
mem4 = mem[4];
for (i = 0; i < N; i++)
{
int sum = Inlines.SHL32(Inlines.EXTEND32(x[i]), CeltConstants.SIG_SHIFT);
sum = Inlines.MAC16_16(sum, num0, (mem0));
sum = Inlines.MAC16_16(sum, num1, (mem1));
sum = Inlines.MAC16_16(sum, num2, (mem2));
sum = Inlines.MAC16_16(sum, num3, (mem3));
sum = Inlines.MAC16_16(sum, num4, (mem4));
mem4 = mem3;
mem3 = mem2;
mem2 = mem1;
mem1 = mem0;
mem0 = x[i];
y[i] = Inlines.ROUND16(sum, CeltConstants.SIG_SHIFT);
}
mem[0] = (mem0);
mem[1] = (mem1);
mem[2] = (mem2);
mem[3] = (mem3);
mem[4] = (mem4);
}
internal static void pitch_downsample(int[][] x, int[] x_lp, int len, int C)
{
int i;
int[] ac = new int[5];
int tmp = CeltConstants.Q15ONE;
int[] lpc = new int[4];
int[] mem = new int[] { 0, 0, 0, 0, 0 };
int[] lpc2 = new int[5];
int c1 = ((short)(0.5 + (0.8f) * (((int)1) << (15))))/*Inlines.QCONST16(0.8f, 15)*/;
int shift;
int maxabs = Inlines.celt_maxabs32(x[0], 0, len);
if (C == 2)
{
int maxabs_1 = Inlines.celt_maxabs32(x[1], 0, len);
maxabs = Inlines.MAX32(maxabs, maxabs_1);
}
if (maxabs < 1)
maxabs = 1;
shift = Inlines.celt_ilog2(maxabs) - 10;
if (shift < 0)
shift = 0;
if (C == 2)
shift++;
int halflen = len >> 1; // cached for performance
for (i = 1; i < halflen; i++)
{
x_lp[i] = (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[0][(2 * i - 1)] + x[0][(2 * i + 1)]) + x[0][2 * i]), shift));
}
x_lp[0] = (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[0][1]) + x[0][0]), shift));
if (C == 2)
{
for (i = 1; i < halflen; i++)
x_lp[i] += (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[1][(2 * i - 1)] + x[1][(2 * i + 1)]) + x[1][2 * i]), shift));
x_lp[0] += (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[1][1]) + x[1][0]), shift));
}
Autocorrelation._celt_autocorr(x_lp, ac, null, 0, 4, halflen);
/* Noise floor -40 dB */
ac[0] += Inlines.SHR32(ac[0], 13);
/* Lag windowing */
for (i = 1; i <= 4; i++)
{
/*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/
ac[i] -= Inlines.MULT16_32_Q15((2 * i * i), ac[i]);
}
CeltLPC.celt_lpc(lpc, ac, 4);
for (i = 0; i < 4; i++)
{
tmp = Inlines.MULT16_16_Q15(((short)(0.5 + (.9f) * (((int)1) << (15))))/*Inlines.QCONST16(.9f, 15)*/, tmp);
lpc[i] = Inlines.MULT16_16_Q15(lpc[i], tmp);
}
/* Add a zero */
lpc2[0] = (lpc[0] + ((short)(0.5 + (0.8f) * (((int)1) << (CeltConstants.SIG_SHIFT))))/*Inlines.QCONST16(0.8f, CeltConstants.SIG_SHIFT)*/);
lpc2[1] = (lpc[1] + Inlines.MULT16_16_Q15(c1, lpc[0]));
lpc2[2] = (lpc[2] + Inlines.MULT16_16_Q15(c1, lpc[1]));
lpc2[3] = (lpc[3] + Inlines.MULT16_16_Q15(c1, lpc[2]));
lpc2[4] = Inlines.MULT16_16_Q15(c1, lpc[3]);
celt_fir5(x_lp, lpc2, x_lp, halflen, mem);
}
// Fixme: remove pointers and optimize
internal static void pitch_search(int[] x_lp, int x_lp_ptr, int[] y,
int len, int max_pitch, out int pitch)
{
int i, j;
int lag;
int[] best_pitch = new int[] { 0, 0 };
int maxcorr;
int xmax, ymax;
int shift = 0;
int offset;
Inlines.OpusAssert(len > 0);
Inlines.OpusAssert(max_pitch > 0);
lag = len + max_pitch;
int[] x_lp4 = new int[len >> 2];
int[] y_lp4 = new int[lag >> 2];
int[] xcorr = new int[max_pitch >> 1];
/* Downsample by 2 again */
for (j = 0; j < len >> 2; j++)
x_lp4[j] = x_lp[x_lp_ptr + (2 * j)];
for (j = 0; j < lag >> 2; j++)
y_lp4[j] = y[2 * j];
xmax = Inlines.celt_maxabs32(x_lp4, 0, len >> 2);
ymax = Inlines.celt_maxabs32(y_lp4, 0, lag >> 2);
shift = Inlines.celt_ilog2(Inlines.MAX32(1, Inlines.MAX32(xmax, ymax))) - 11;
if (shift > 0)
{
for (j = 0; j < len >> 2; j++)
x_lp4[j] = Inlines.SHR16(x_lp4[j], shift);
for (j = 0; j < lag >> 2; j++)
y_lp4[j] = Inlines.SHR16(y_lp4[j], shift);
/* Use double the shift for a MAC */
shift *= 2;
}
else {
shift = 0;
}
/* Coarse search with 4x decimation */
maxcorr = CeltPitchXCorr.pitch_xcorr(x_lp4, y_lp4, xcorr, len >> 2, max_pitch >> 2);
find_best_pitch(xcorr, y_lp4, len >> 2, max_pitch >> 2, best_pitch, 0, maxcorr);
/* Finer search with 2x decimation */
maxcorr = 1;
for (i = 0; i < max_pitch >> 1; i++)
{
int sum;
xcorr[i] = 0;
if (Inlines.abs(i - 2 * best_pitch[0]) > 2 && Inlines.abs(i - 2 * best_pitch[1]) > 2)
{
continue;
}
sum = 0;
for (j = 0; j < len >> 1; j++)
sum += Inlines.SHR32(Inlines.MULT16_16(x_lp[x_lp_ptr + j], y[i + j]), shift);
xcorr[i] = Inlines.MAX32(-1, sum);
maxcorr = Inlines.MAX32(maxcorr, sum);
}
find_best_pitch(xcorr, y, len >> 1, max_pitch >> 1, best_pitch, shift + 1, maxcorr);
/* Refine by pseudo-interpolation */
if (best_pitch[0] > 0 && best_pitch[0] < (max_pitch >> 1) - 1)
{
int a, b, c;
a = xcorr[best_pitch[0] - 1];
b = xcorr[best_pitch[0]];
c = xcorr[best_pitch[0] + 1];
if ((c - a) > Inlines.MULT16_32_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, b - a))
{
offset = 1;
}
else if ((a - c) > Inlines.MULT16_32_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, b - c))
{
offset = -1;
}
else
{
offset = 0;
}
}
else
{
offset = 0;
}
pitch = 2 * best_pitch[0] - offset;
}
private static readonly int[] second_check = { 0, 0, 3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2 };
internal static int remove_doubling(int[] x, int maxperiod, int minperiod,
int N, ref int T0_, int prev_period, int prev_gain)
{
int k, i, T, T0;
int g, g0;
int pg;
int yy, xx, xy, xy2;
int[] xcorr = new int[3];
int best_xy, best_yy;
int offset;
int minperiod0 = minperiod;
maxperiod /= 2;
minperiod /= 2;
T0_ /= 2;
prev_period /= 2;
N /= 2;
int x_ptr = maxperiod;
if (T0_ >= maxperiod)
T0_ = maxperiod - 1;
T = T0 = T0_;
int[] yy_lookup = new int[maxperiod + 1];
#if UNSAFE
unsafe
{
fixed (int* px_base = x)
{
int* px = px_base + x_ptr;
int* px2 = px_base + x_ptr - T0;
Kernels.dual_inner_prod(px, px, px2, N, out xx, out xy);
}
}
#else
Kernels.dual_inner_prod(x, x_ptr, x, x_ptr, x, x_ptr - T0, N, out xx, out xy);
#endif
yy_lookup[0] = xx;
yy = xx;
for (i = 1; i <= maxperiod; i++)
{
int xi = x_ptr - i;
yy = yy + Inlines.MULT16_16(x[xi], x[xi]) - Inlines.MULT16_16(x[xi + N], x[xi + N]);
yy_lookup[i] = Inlines.MAX32(0, yy);
}
yy = yy_lookup[T0];
best_xy = xy;
best_yy = yy;
{
int x2y2;
int sh, t;
x2y2 = 1 + Inlines.HALF32(Inlines.MULT32_32_Q31(xx, yy));
sh = Inlines.celt_ilog2(x2y2) >> 1;
t = Inlines.VSHR32(x2y2, 2 * (sh - 7));
g = (Inlines.VSHR32(Inlines.MULT16_32_Q15(Inlines.celt_rsqrt_norm(t), xy), sh + 1));
g0 = g;
}
/* Look for any pitch at T/k */
for (k = 2; k <= 15; k++)
{
int T1, T1b;
int g1;
int cont = 0;
int thresh;
T1 = Inlines.celt_udiv(2 * T0 + k, 2 * k);
if (T1 < minperiod)
{
break;
}
/* Look for another strong correlation at T1b */
if (k == 2)
{
if (T1 + T0 > maxperiod)
T1b = T0;
else
T1b = T0 + T1;
}
else
{
T1b = Inlines.celt_udiv(2 * second_check[k] * T0 + k, 2 * k);
}
#if UNSAFE
unsafe
{
fixed (int* px_base = x)
{
int* px = px_base + x_ptr;
int* px2 = px_base + x_ptr - T1;
int* px3 = px_base + x_ptr - T1b;
Kernels.dual_inner_prod(px, px2, px3, N, out xy, out xy2);
}
}
#else
Kernels.dual_inner_prod(x, x_ptr, x, x_ptr - T1, x, x_ptr - T1b, N, out xy, out xy2);
#endif
xy += xy2;
yy = yy_lookup[T1] + yy_lookup[T1b];
{
int x2y2;
int sh, t;
x2y2 = 1 + Inlines.MULT32_32_Q31(xx, yy);
sh = Inlines.celt_ilog2(x2y2) >> 1;
t = Inlines.VSHR32(x2y2, 2 * (sh - 7));
g1 = (Inlines.VSHR32(Inlines.MULT16_32_Q15(Inlines.celt_rsqrt_norm(t), xy), sh + 1));
}
if (Inlines.abs(T1 - prev_period) <= 1)
cont = prev_gain;
else if (Inlines.abs(T1 - prev_period) <= 2 && 5 * k * k < T0)
{
cont = Inlines.HALF16(prev_gain);
}
else
{
cont = 0;
}
thresh = Inlines.MAX16(((short)(0.5 + (.3f) * (((int)1) << (15))))/*Inlines.QCONST16(.3f, 15)*/, (Inlines.MULT16_16_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, g0) - cont));
/* Bias against very high pitch (very short period) to avoid false-positives
due to short-term correlation */
if (T1 < 3 * minperiod)
{
thresh = Inlines.MAX16(((short)(0.5 + (.4f) * (((int)1) << (15))))/*Inlines.QCONST16(.4f, 15)*/, (Inlines.MULT16_16_Q15(((short)(0.5 + (.85f) * (((int)1) << (15))))/*Inlines.QCONST16(.85f, 15)*/, g0) - cont));
}
else if (T1 < 2 * minperiod)
{
thresh = Inlines.MAX16(((short)(0.5 + (.5f) * (((int)1) << (15))))/*Inlines.QCONST16(.5f, 15)*/, (Inlines.MULT16_16_Q15(((short)(0.5 + (.9f) * (((int)1) << (15))))/*Inlines.QCONST16(.9f, 15)*/, g0) - cont));
}
if (g1 > thresh)
{
best_xy = xy;
best_yy = yy;
T = T1;
g = g1;
}
}
best_xy = Inlines.MAX32(0, best_xy);
if (best_yy <= best_xy)
{
pg = CeltConstants.Q15ONE;
}
else
{
pg = (Inlines.SHR32(Inlines.frac_div32(best_xy, best_yy + 1), 16));
}
#if UNSAFE
unsafe
{
fixed (int* px_base = x)
{
int* px = px_base + x_ptr;
for (k = 0; k < 3; k++)
{
xcorr[k] = Kernels.celt_inner_prod(px, px - (T + k - 1), N);
}
}
}
#else
for (k = 0; k < 3; k++)
{
xcorr[k] = Kernels.celt_inner_prod(x, x_ptr, x, x_ptr - (T + k - 1), N);
}
#endif
if ((xcorr[2] - xcorr[0]) > Inlines.MULT16_32_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, xcorr[1] - xcorr[0]))
{
offset = 1;
}
else if ((xcorr[0] - xcorr[2]) > Inlines.MULT16_32_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, xcorr[1] - xcorr[2]))
{
offset = -1;
}
else
{
offset = 0;
}
if (pg > g)
{
pg = g;
}
T0_ = 2 * T + offset;
if (T0_ < minperiod0)
{
T0_ = minperiod0;
}
return pg;
}
}
}

View File

@@ -0,0 +1,524 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System;
using System.Diagnostics;
internal static class QuantizeBands
{
/* prediction coefficients: 0.9, 0.8, 0.65, 0.5 */
private static readonly int[] pred_coef = new int[]{ 29440, 26112, 21248, 16384 };
private static readonly int[] beta_coef = new int[] { 30147, 22282, 12124, 6554 };
private static readonly int beta_intra = 4915;
private static byte[] small_energy_icdf = { 2, 1, 0 };
internal static int loss_distortion(int[][] eBands, int[][] oldEBands, int start, int end, int len, int C)
{
int c, i;
int dist = 0;
c = 0;
do
{
for (i = start; i < end; i++)
{
int d = Inlines.SUB16(Inlines.SHR16(eBands[c][i], 3), Inlines.SHR16(oldEBands[c][i], 3));
dist = Inlines.MAC16_16(dist, d, d);
}
} while (++c < C);
return Inlines.MIN32(200, Inlines.SHR32(dist, 2 * CeltConstants.DB_SHIFT - 6));
}
internal static int quant_coarse_energy_impl(CeltMode m, int start, int end,
int[][] eBands, int[][] oldEBands,
int budget, int tell,
byte[] prob_model, int[][] error, EntropyCoder enc,
int C, int LM, int intra, int max_decay, int lfe)
{
int i, c;
int badness = 0;
int[] prev = { 0, 0 };
int coef;
int beta;
if (tell + 3 <= budget)
{
enc.enc_bit_logp(intra, 3);
}
if (intra != 0)
{
coef = 0;
beta = beta_intra;
}
else {
beta = beta_coef[LM];
coef = pred_coef[LM];
}
/* Encode at a fixed coarse resolution */
for (i = start; i < end; i++)
{
c = 0;
do
{
int bits_left;
int qi, qi0;
int q;
int x;
int f, tmp;
int oldE;
int decay_bound;
x = eBands[c][i];
oldE = Inlines.MAX16(-((short)(0.5 + (9.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(9.0f, CeltConstants.DB_SHIFT)*/, oldEBands[c][i]);
f = Inlines.SHL32(Inlines.EXTEND32(x), 7) - Inlines.PSHR32(Inlines.MULT16_16(coef, oldE), 8) - prev[c];
/* Rounding to nearest integer here is really important! */
qi = (f + ((int)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT + 7))))/*Inlines.QCONST32(.5f, CeltConstants.DB_SHIFT + 7)*/) >> (CeltConstants.DB_SHIFT + 7);
decay_bound = Inlines.EXTRACT16(Inlines.MAX32(-((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/,
Inlines.SUB32((int)oldEBands[c][i], max_decay)));
/* Prevent the energy from going down too quickly (e.g. for bands
that have just one bin) */
if (qi < 0 && x < decay_bound)
{
qi += (int)Inlines.SHR16(Inlines.SUB16(decay_bound, x), CeltConstants.DB_SHIFT);
if (qi > 0)
qi = 0;
}
qi0 = qi;
/* If we don't have enough bits to encode all the energy, just assume
something safe. */
tell = enc.tell();
bits_left = budget - tell - 3 * C * (end - i);
if (i != start && bits_left < 30)
{
if (bits_left < 24)
qi = Inlines.IMIN(1, qi);
if (bits_left < 16)
qi = Inlines.IMAX(-1, qi);
}
if (lfe != 0 && i >= 2)
qi = Inlines.IMIN(qi, 0);
if (budget - tell >= 15)
{
int pi;
pi = 2 * Inlines.IMIN(i, 20);
Laplace.ec_laplace_encode(enc, ref qi, (((uint)prob_model[pi]) << 7), ((int)prob_model[pi + 1]) << 6);
}
else if (budget - tell >= 2)
{
qi = Inlines.IMAX(-1, Inlines.IMIN(qi, 1));
enc.enc_icdf(2 * qi ^ (0 - (qi < 0 ? 1 : 0)), small_energy_icdf, 2);
}
else if (budget - tell >= 1)
{
qi = Inlines.IMIN(0, qi);
enc.enc_bit_logp(-qi, 1);
}
else
qi = -1;
error[c][i] = (Inlines.PSHR32(f, 7) - Inlines.SHL16((qi), CeltConstants.DB_SHIFT));
badness += Inlines.abs(qi0 - qi);
q = (int)Inlines.SHL32(qi, CeltConstants.DB_SHIFT); // opus bug: useless extend32
tmp = Inlines.PSHR32(Inlines.MULT16_16(coef, oldE), 8) + prev[c] + Inlines.SHL32(q, 7);
tmp = Inlines.MAX32(-((int)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT + 7))))/*Inlines.QCONST32(28.0f, CeltConstants.DB_SHIFT + 7)*/, tmp);
oldEBands[c][i] = (Inlines.PSHR32(tmp, 7));
prev[c] = prev[c] + Inlines.SHL32(q, 7) - Inlines.MULT16_16(beta, Inlines.PSHR32(q, 8));
} while (++c < C);
}
return lfe != 0 ? 0 : badness;
}
internal static void quant_coarse_energy(CeltMode m, int start, int end, int effEnd,
int[][] eBands, int[][] oldEBands, uint budget,
int[][] error, EntropyCoder enc, int C, int LM, int nbAvailableBytes,
int force_intra, ref int delayedIntra, int two_pass, int loss_rate, int lfe)
{
int intra;
int max_decay;
int[][] oldEBands_intra;
int[][] error_intra;
EntropyCoder enc_start_state = new EntropyCoder(); // [porting note] stack variable
uint tell;
int badness1 = 0;
int intra_bias;
int new_distortion;
intra = (force_intra != 0 || (two_pass == 0 && delayedIntra > 2 * C * (end - start) && nbAvailableBytes > (end - start) * C)) ? 1 : 0;
intra_bias = (int)((budget * delayedIntra * loss_rate) / (C * 512));
new_distortion = loss_distortion(eBands, oldEBands, start, effEnd, m.nbEBands, C);
tell = (uint)enc.tell();
if (tell + 3 > budget)
two_pass = intra = 0;
max_decay = ((short)(0.5 + (16.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(16.0f, CeltConstants.DB_SHIFT)*/;
if (end - start > 10)
{
max_decay = (Inlines.MIN32(max_decay, Inlines.SHL32(nbAvailableBytes, CeltConstants.DB_SHIFT - 3))); // opus bug: useless extend32
}
if (lfe != 0)
{
max_decay = ((short)(0.5 + (3.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(3.0f, CeltConstants.DB_SHIFT)*/;
}
enc_start_state.Assign(enc);
oldEBands_intra = Arrays.InitTwoDimensionalArray<int>(C, m.nbEBands);
error_intra = Arrays.InitTwoDimensionalArray<int>(C, m.nbEBands);
Array.Copy(oldEBands[0], 0, oldEBands_intra[0], 0, m.nbEBands);
if (C == 2)
Array.Copy(oldEBands[1], 0, oldEBands_intra[1], 0, m.nbEBands);
if (two_pass != 0 || intra != 0)
{
badness1 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands_intra, (int)budget,
(int)tell, Tables.e_prob_model[LM][1], error_intra, enc, C, LM, 1, max_decay, lfe);
}
if (intra == 0)
{
int intra_buf;
EntropyCoder enc_intra_state = new EntropyCoder(); // [porting note] stack variable
int tell_intra;
uint nstart_bytes;
uint nintra_bytes;
uint save_bytes;
int badness2;
byte[] intra_bits = null;
tell_intra = (int)enc.tell_frac();
enc_intra_state.Assign(enc);
nstart_bytes = enc_start_state.range_bytes();
nintra_bytes = enc_intra_state.range_bytes();
intra_buf = enc_intra_state.buf_ptr + (int)nstart_bytes;
save_bytes = nintra_bytes - nstart_bytes;
if (save_bytes != 0)
{
intra_bits = new byte[(int)save_bytes];
/* Copy bits from intra bit-stream */
Array.Copy(enc_intra_state.buf, intra_buf, intra_bits, 0, (int)save_bytes);
}
enc.Assign(enc_start_state);
badness2 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands, (int)budget,
(int)tell, Tables.e_prob_model[LM][intra], error, enc, C, LM, 0, max_decay, lfe);
if (two_pass != 0 && (badness1 < badness2 || (badness1 == badness2 && ((int)enc.tell_frac()) + intra_bias > tell_intra)))
{
enc.Assign(enc_intra_state);
/* Copy intra bits to bit-stream */
if (intra_bits != null)
{
Array.Copy(intra_bits, 0, enc_intra_state.buf, intra_buf, (int)(nintra_bytes - nstart_bytes));
}
Array.Copy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands);
Array.Copy(error_intra[0], 0, error[0], 0, m.nbEBands);
if (C == 2)
{
Array.Copy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands);
Array.Copy(error_intra[1], 0, error[1], 0, m.nbEBands);
}
intra = 1;
}
}
else
{
Array.Copy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands);
Array.Copy(error_intra[0], 0, error[0], 0, m.nbEBands);
if (C == 2)
{
Array.Copy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands);
Array.Copy(error_intra[1], 0, error[1], 0, m.nbEBands);
}
}
if (intra != 0)
{
delayedIntra = new_distortion;
}
else
{
delayedIntra = Inlines.ADD32(Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(pred_coef[LM], pred_coef[LM]), delayedIntra),
new_distortion);
}
}
internal static void quant_fine_energy(CeltMode m, int start, int end, int[][] oldEBands, int[][] error, int[] fine_quant, EntropyCoder enc, int C)
{
int i, c;
/* Encode finer resolution */
for (i = start; i < end; i++)
{
int frac = (1 << fine_quant[i]);
if (fine_quant[i] <= 0)
continue;
c = 0;
do
{
int q2;
int offset;
/* Has to be without rounding */
q2 = (error[c][i] + ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/) >> (CeltConstants.DB_SHIFT - fine_quant[i]);
if (q2 > frac - 1)
q2 = frac - 1;
if (q2 < 0)
q2 = 0;
enc.enc_bits((uint)q2, (uint)fine_quant[i]);
offset = Inlines.SUB16(
(Inlines.SHR32(
Inlines.SHL32(q2, CeltConstants.DB_SHIFT) + ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/,
fine_quant[i])),
((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/);
oldEBands[c][i] += offset;
error[c][i] -= offset;
} while (++c < C);
}
}
internal static void quant_energy_finalise(CeltMode m, int start, int end, int[][] oldEBands, int[][] error, int[] fine_quant, int[] fine_priority, int bits_left, EntropyCoder enc, int C)
{
int i, prio, c;
/* Use up the remaining bits */
for (prio = 0; prio < 2; prio++)
{
for (i = start; i < end && bits_left >= C; i++)
{
if (fine_quant[i] >= CeltConstants.MAX_FINE_BITS || fine_priority[i] != prio)
{
continue;
}
c = 0;
do
{
int q2;
int offset;
q2 = error[c][i] < 0 ? 0 : 1;
enc.enc_bits((uint)q2, 1);
offset = Inlines.SHR16((Inlines.SHL16((q2), CeltConstants.DB_SHIFT) - ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/), fine_quant[i] + 1);
oldEBands[c][i] += offset;
bits_left--;
} while (++c < C);
}
}
}
internal static void unquant_coarse_energy(CeltMode m, int start, int end, int[] oldEBands, int intra, EntropyCoder dec, int C, int LM)
{
byte[] prob_model = Tables.e_prob_model[LM][intra];
int i, c;
int[] prev = { 0, 0 };
int coef;
int beta;
int budget;
int tell;
if (intra != 0)
{
coef = 0;
beta = beta_intra;
}
else {
beta = beta_coef[LM];
coef = pred_coef[LM];
}
budget = (int)dec.storage * 8;
/* Decode at a fixed coarse resolution */
for (i = start; i < end; i++)
{
c = 0;
do
{
int qi;
int q;
int tmp;
/* It would be better to express this invariant as a
test on C at function entry, but that isn't enough
to make the static analyzer happy. */
Inlines.OpusAssert(c < 2);
tell = dec.tell();
if (budget - tell >= 15)
{
int pi;
pi = 2 * Inlines.IMIN(i, 20);
qi = Laplace.ec_laplace_decode(dec,
(uint)prob_model[pi] << 7, prob_model[pi + 1] << 6);
}
else if (budget - tell >= 2)
{
qi = dec.dec_icdf(small_energy_icdf, 2);
qi = (qi >> 1) ^ -(qi & 1);
}
else if (budget - tell >= 1)
{
qi = 0 - dec.dec_bit_logp(1);
}
else
{
qi = -1;
}
q = (int)Inlines.SHL32(qi, CeltConstants.DB_SHIFT); // opus bug: useless extend32
oldEBands[i + c * m.nbEBands] = Inlines.MAX16((0 - ((short)(0.5 + (9.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(9.0f, CeltConstants.DB_SHIFT)*/), oldEBands[i + c * m.nbEBands]);
tmp = Inlines.PSHR32(Inlines.MULT16_16(coef, oldEBands[i + c * m.nbEBands]), 8) + prev[c] + Inlines.SHL32(q, 7);
tmp = Inlines.MAX32(-((int)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT + 7))))/*Inlines.QCONST32(28.0f, CeltConstants.DB_SHIFT + 7)*/, tmp);
oldEBands[i + c * m.nbEBands] = (Inlines.PSHR32(tmp, 7));
prev[c] = prev[c] + Inlines.SHL32(q, 7) - Inlines.MULT16_16(beta, Inlines.PSHR32(q, 8));
} while (++c < C);
}
}
internal static void unquant_fine_energy(CeltMode m, int start, int end, int[] oldEBands, int[] fine_quant, EntropyCoder dec, int C)
{
int i, c;
/* Decode finer resolution */
for (i = start; i < end; i++)
{
if (fine_quant[i] <= 0)
continue;
c = 0;
do
{
int q2;
int offset;
q2 = (int)dec.dec_bits((uint)fine_quant[i]);
offset = Inlines.SUB16((Inlines.SHR32(
Inlines.SHL32(q2, CeltConstants.DB_SHIFT) +
((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/, fine_quant[i])),
((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); // opus bug: unnecessary extend32
oldEBands[i + c * m.nbEBands] += offset;
} while (++c < C);
}
}
internal static void unquant_energy_finalise(CeltMode m, int start, int end, int[] oldEBands, int[] fine_quant, int[] fine_priority, int bits_left, EntropyCoder dec, int C)
{
int i, prio, c;
/* Use up the remaining bits */
for (prio = 0; prio < 2; prio++)
{
for (i = start; i < end && bits_left >= C; i++)
{
if (fine_quant[i] >= CeltConstants.MAX_FINE_BITS || fine_priority[i] != prio)
continue;
c = 0;
do
{
int q2;
int offset;
q2 = (int)dec.dec_bits(1);
offset = Inlines.SHR16((Inlines.SHL16((q2), CeltConstants.DB_SHIFT) - ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/), fine_quant[i] + 1);
oldEBands[i + c * m.nbEBands] += offset;
bits_left--;
} while (++c < C);
}
}
}
/// <summary>
/// non-pointer case
/// </summary>
/// <param name="m"></param>
/// <param name="effEnd"></param>
/// <param name="end"></param>
/// <param name="bandE"></param>
/// <param name="bandLogE"></param>
/// <param name="C"></param>
internal static void amp2Log2(CeltMode m, int effEnd, int end,
int[][] bandE, int[][] bandLogE, int C)
{
int c, i;
c = 0;
do
{
for (i = 0; i < effEnd; i++)
{
bandLogE[c][i] =
(Inlines.celt_log2(Inlines.SHL32(bandE[c][i], 2))
- Inlines.SHL16((int)Tables.eMeans[i], 6));
}
for (i = effEnd; i < end; i++)
{
bandLogE[c][i] = (0 - ((short)(0.5 + (14.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(14.0f, CeltConstants.DB_SHIFT)*/);
}
} while (++c < C);
}
/// <summary>
/// only needed in one place
/// </summary>
/// <param name="m"></param>
/// <param name="effEnd"></param>
/// <param name="end"></param>
/// <param name="bandE"></param>
/// <param name="bandLogE"></param>
/// <param name="C"></param>
internal static void amp2Log2(CeltMode m, int effEnd, int end,
int[] bandE, int[] bandLogE, int bandLogE_ptr, int C)
{
int c, i;
c = 0;
do
{
for (i = 0; i < effEnd; i++)
{
bandLogE[bandLogE_ptr + (c * m.nbEBands) + i] =
(Inlines.celt_log2(Inlines.SHL32(bandE[i + c * m.nbEBands], 2))
- Inlines.SHL16((int)Tables.eMeans[i], 6));
}
for (i = effEnd; i < end; i++)
{
bandLogE[bandLogE_ptr + (c * m.nbEBands) + i] = (0 - ((short)(0.5 + (14.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(14.0f, CeltConstants.DB_SHIFT)*/);
}
} while (++c < C);
}
}
}

View File

@@ -0,0 +1,519 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
internal static class Rate
{
private static readonly byte[] LOG2_FRAC_TABLE ={
0,
8,13,
16,19,21,23,
24,26,27,28,29,30,31,32,
32,33,34,34,35,36,36,37,37
};
private const int ALLOC_STEPS = 6;
internal static int get_pulses(int i)
{
return i < 8 ? i : (8 + (i & 7)) << ((i >> 3) - 1);
}
internal static int bits2pulses(CeltMode m, int band, int LM, int bits)
{
int i;
int lo, hi;
LM++;
byte[] cache = m.cache.bits;
int cache_ptr = m.cache.index[LM * m.nbEBands + band];
lo = 0;
hi = cache[cache_ptr];
bits--;
for (i = 0; i < CeltConstants.LOG_MAX_PSEUDO; i++)
{
int mid = (lo + hi + 1) >> 1;
/* OPT: Make sure this is implemented with a conditional move */
if ((int)cache[cache_ptr + mid] >= bits)
hi = mid;
else
lo = mid;
}
if (bits - (lo == 0 ? -1 : (int)cache[cache_ptr + lo]) <= (int)cache[cache_ptr + hi] - bits)
return lo;
else
return hi;
}
internal static int pulses2bits(CeltMode m, int band, int LM, int pulses)
{
LM++;
return pulses == 0 ? 0 : m.cache.bits[m.cache.index[LM * m.nbEBands + band] + pulses] + 1;
}
internal static int interp_bits2pulses(CeltMode m, int start, int end, int skip_start,
int[] bits1, int[] bits2, int[] thresh, int[] cap, int total, out int _balance,
int skip_rsv, ref int intensity, int intensity_rsv, ref int dual_stereo, int dual_stereo_rsv, int[] bits,
int[] ebits, int[] fine_priority, int C, int LM, EntropyCoder ec, int encode, int prev, int signalBandwidth)
{
int psum;
int lo, hi;
int i, j;
int logM;
int stereo;
int codedBands = -1;
int alloc_floor;
int left, percoeff;
int done;
int balance;
alloc_floor = C << EntropyCoder.BITRES;
stereo = C > 1 ? 1 : 0;
logM = LM << EntropyCoder.BITRES;
lo = 0;
hi = 1 << ALLOC_STEPS;
for (i = 0; i < ALLOC_STEPS; i++)
{
int mid = (lo + hi) >> 1;
psum = 0;
done = 0;
for (j = end; j-- > start;)
{
int tmp = bits1[j] + (mid * (int)bits2[j] >> ALLOC_STEPS);
if (tmp >= thresh[j] || done != 0)
{
done = 1;
/* Don't allocate more than we can actually use */
psum += Inlines.IMIN(tmp, cap[j]);
}
else
{
if (tmp >= alloc_floor)
psum += alloc_floor;
}
}
if (psum > total)
hi = mid;
else
lo = mid;
}
psum = 0;
/*printf ("interp bisection gave %d\n", lo);*/
done = 0;
for (j = end; j-- > start;)
{
int tmp = bits1[j] + (lo * bits2[j] >> ALLOC_STEPS);
if (tmp < thresh[j] && done == 0)
{
if (tmp >= alloc_floor)
tmp = alloc_floor;
else
tmp = 0;
}
else
{
done = 1;
}
/* Don't allocate more than we can actually use */
tmp = Inlines.IMIN(tmp, cap[j]);
bits[j] = tmp;
psum += tmp;
}
/* Decide which bands to skip, working backwards from the end. */
for (codedBands = end; ; codedBands--)
{
int band_width;
int band_bits;
int rem;
j = codedBands - 1;
/* Never skip the first band, nor a band that has been boosted by
dynalloc.
In the first case, we'd be coding a bit to signal we're going to waste
all the other bits.
In the second case, we'd be coding a bit to redistribute all the bits
we just signaled should be cocentrated in this band. */
if (j <= skip_start)
{
/* Give the bit we reserved to end skipping back. */
total += skip_rsv;
break;
}
/*Figure out how many left-over bits we would be adding to this band.
This can include bits we've stolen back from higher, skipped bands.*/
left = total - psum;
percoeff = Inlines.celt_udiv(left, m.eBands[codedBands] - m.eBands[start]);
left -= (m.eBands[codedBands] - m.eBands[start]) * percoeff;
rem = Inlines.IMAX(left - (m.eBands[j] - m.eBands[start]), 0);
band_width = m.eBands[codedBands] - m.eBands[j];
band_bits = (int)(bits[j] + percoeff * band_width + rem);
/*Only code a skip decision if we're above the threshold for this band.
Otherwise it is force-skipped.
This ensures that we have enough bits to code the skip flag.*/
if (band_bits >= Inlines.IMAX(thresh[j], alloc_floor + (1 << EntropyCoder.BITRES)))
{
if (encode != 0)
{
/*This if() block is the only part of the allocation function that
is not a mandatory part of the bitstream: any bands we choose to
skip here must be explicitly signaled.*/
/*Choose a threshold with some hysteresis to keep bands from
fluctuating in and out.*/
#if FUZZING
if ((new Random().Next() & 0x1) == 0)
#else
if (codedBands <= start + 2 || (band_bits > ((j < prev ? 7 : 9) * band_width << LM << EntropyCoder.BITRES) >> 4 && j <= signalBandwidth))
#endif
{
ec.enc_bit_logp(1, 1);
break;
}
ec.enc_bit_logp(0, 1);
}
else if (ec.dec_bit_logp(1) != 0)
{
break;
}
/*We used a bit to skip this band.*/
psum += 1 << EntropyCoder.BITRES;
band_bits -= 1 << EntropyCoder.BITRES;
}
/*Reclaim the bits originally allocated to this band.*/
psum -= bits[j] + intensity_rsv;
if (intensity_rsv > 0)
intensity_rsv = LOG2_FRAC_TABLE[j - start];
psum += intensity_rsv;
if (band_bits >= alloc_floor)
{
/*If we have enough for a fine energy bit per channel, use it.*/
psum += alloc_floor;
bits[j] = alloc_floor;
}
else {
/*Otherwise this band gets nothing at all.*/
bits[j] = 0;
}
}
Inlines.OpusAssert(codedBands > start);
/* Code the intensity and dual stereo parameters. */
if (intensity_rsv > 0)
{
if (encode != 0)
{
intensity = Inlines.IMIN(intensity, codedBands);
ec.enc_uint((uint)(intensity - start), (uint)(codedBands + 1 - start));
}
else
{
intensity = start + (int)ec.dec_uint((uint)(codedBands + 1 - start));
}
}
else
{
intensity = 0;
}
if (intensity <= start)
{
total += dual_stereo_rsv;
dual_stereo_rsv = 0;
}
if (dual_stereo_rsv > 0)
{
if (encode != 0)
{
ec.enc_bit_logp(dual_stereo, 1);
}
else
{
dual_stereo = ec.dec_bit_logp(1);
}
}
else
{
dual_stereo = 0;
}
/* Allocate the remaining bits */
left = total - psum;
percoeff = Inlines.celt_udiv(left, m.eBands[codedBands] - m.eBands[start]);
left -= (m.eBands[codedBands] - m.eBands[start]) * percoeff;
for (j = start; j < codedBands; j++)
bits[j] += ((int)percoeff * (m.eBands[j + 1] - m.eBands[j]));
for (j = start; j < codedBands; j++)
{
int tmp = (int)Inlines.IMIN(left, m.eBands[j + 1] - m.eBands[j]);
bits[j] += tmp;
left -= tmp;
}
/*for (j=0;j<end;j++)printf("%d ", bits[j]);printf("\n");*/
balance = 0;
for (j = start; j < codedBands; j++)
{
int N0, N, den;
int offset;
int NClogN;
int excess, bit;
Inlines.OpusAssert(bits[j] >= 0);
N0 = m.eBands[j + 1] - m.eBands[j];
N = N0 << LM;
bit = (int)bits[j] + balance;
if (N > 1)
{
excess = Inlines.MAX32(bit - cap[j], 0);
bits[j] = bit - excess;
/* Compensate for the extra DoF in stereo */
den = (C * N + ((C == 2 && N > 2 && (dual_stereo == 0) && j < intensity) ? 1 : 0));
NClogN = den * (m.logN[j] + logM);
/* Offset for the number of fine bits by log2(N)/2 + FINE_OFFSET
compared to their "fair share" of total/N */
offset = (NClogN >> 1) - den * CeltConstants.FINE_OFFSET;
/* N=2 is the only point that doesn't match the curve */
if (N == 2)
offset += den << EntropyCoder.BITRES >> 2;
/* Changing the offset for allocating the second and third
fine energy bit */
if (bits[j] + offset < den * 2 << EntropyCoder.BITRES)
offset += NClogN >> 2;
else if (bits[j] + offset < den * 3 << EntropyCoder.BITRES)
offset += NClogN >> 3;
/* Divide with rounding */
ebits[j] = Inlines.IMAX(0, (bits[j] + offset + (den << (EntropyCoder.BITRES - 1))));
ebits[j] = Inlines.celt_udiv(ebits[j], den) >> EntropyCoder.BITRES;
/* Make sure not to bust */
if (C * ebits[j] > (bits[j] >> EntropyCoder.BITRES))
ebits[j] = bits[j] >> stereo >> EntropyCoder.BITRES;
/* More than that is useless because that's about as far as PVQ can go */
ebits[j] = Inlines.IMIN(ebits[j], CeltConstants.MAX_FINE_BITS);
/* If we rounded down or capped this band, make it a candidate for the
final fine energy pass */
fine_priority[j] = (ebits[j] * (den << EntropyCoder.BITRES) >= bits[j] + offset) ? 1 : 0;
/* Remove the allocated fine bits; the rest are assigned to PVQ */
bits[j] -= C * ebits[j] << EntropyCoder.BITRES;
}
else {
/* For N=1, all bits go to fine energy except for a single sign bit */
excess = Inlines.MAX32(0, bit - (C << EntropyCoder.BITRES));
bits[j] = bit - excess;
ebits[j] = 0;
fine_priority[j] = 1;
}
/* Fine energy can't take advantage of the re-balancing in
quant_all_bands().
Instead, do the re-balancing here.*/
if (excess > 0)
{
int extra_fine;
int extra_bits;
extra_fine = Inlines.IMIN(excess >> (stereo + EntropyCoder.BITRES), CeltConstants.MAX_FINE_BITS - ebits[j]);
ebits[j] += extra_fine;
extra_bits = extra_fine * C << EntropyCoder.BITRES;
fine_priority[j] = (extra_bits >= excess - balance) ? 1 : 0;
excess -= extra_bits;
}
balance = excess;
Inlines.OpusAssert(bits[j] >= 0);
Inlines.OpusAssert(ebits[j] >= 0);
}
/* Save any remaining bits over the cap for the rebalancing in
quant_all_bands(). */
_balance = balance;
/* The skipped bands use all their bits for fine energy. */
for (; j < end; j++)
{
ebits[j] = bits[j] >> stereo >> EntropyCoder.BITRES;
Inlines.OpusAssert(C * ebits[j] << EntropyCoder.BITRES == bits[j]);
bits[j] = 0;
fine_priority[j] = (ebits[j] < 1) ? 1 : 0;
}
return codedBands;
}
internal static int compute_allocation(CeltMode m, int start, int end, int[] offsets, int[] cap, int alloc_trim, ref int intensity, ref int dual_stereo,
int total, out int balance, int[] pulses, int[] ebits, int[] fine_priority, int C, int LM, EntropyCoder ec, int encode, int prev, int signalBandwidth)
{
int lo, hi, len, j;
int codedBands;
int skip_start;
int skip_rsv;
int intensity_rsv;
int dual_stereo_rsv;
total = Inlines.IMAX(total, 0);
len = m.nbEBands;
skip_start = start;
/* Reserve a bit to signal the end of manually skipped bands. */
skip_rsv = total >= 1 << EntropyCoder.BITRES ? 1 << EntropyCoder.BITRES : 0;
total -= skip_rsv;
/* Reserve bits for the intensity and dual stereo parameters. */
intensity_rsv = dual_stereo_rsv = 0;
if (C == 2)
{
intensity_rsv = LOG2_FRAC_TABLE[end - start];
if (intensity_rsv > total)
intensity_rsv = 0;
else
{
total -= intensity_rsv;
dual_stereo_rsv = total >= 1 << EntropyCoder.BITRES ? 1 << EntropyCoder.BITRES : 0;
total -= dual_stereo_rsv;
}
}
int[] bits1 = new int[len];
int[] bits2 = new int[len];
int[] thresh = new int[len];
int[] trim_offset = new int[len];
for (j = start; j < end; j++)
{
/* Below this threshold, we're sure not to allocate any PVQ bits */
thresh[j] = Inlines.IMAX((C) << EntropyCoder.BITRES, (3 * (m.eBands[j + 1] - m.eBands[j]) << LM << EntropyCoder.BITRES) >> 4);
/* Tilt of the allocation curve */
trim_offset[j] = C * (m.eBands[j + 1] - m.eBands[j]) * (alloc_trim - 5 - LM) * (end - j - 1)
* (1 << (LM + EntropyCoder.BITRES)) >> 6;
/* Giving less resolution to single-coefficient bands because they get
more benefit from having one coarse value per coefficient*/
if ((m.eBands[j + 1] - m.eBands[j]) << LM == 1)
trim_offset[j] -= C << EntropyCoder.BITRES;
}
lo = 1;
hi = m.nbAllocVectors - 1;
do
{
int done = 0;
int psum = 0;
int mid = (lo + hi) >> 1;
for (j = end; j-- > start;)
{
int bitsj;
int N = m.eBands[j + 1] - m.eBands[j];
bitsj = C * N * m.allocVectors[mid * len + j] << LM >> 2;
if (bitsj > 0)
{
bitsj = Inlines.IMAX(0, bitsj + trim_offset[j]);
}
bitsj += offsets[j];
if (bitsj >= thresh[j] || done != 0)
{
done = 1;
/* Don't allocate more than we can actually use */
psum += Inlines.IMIN(bitsj, cap[j]);
}
else
{
if (bitsj >= C << EntropyCoder.BITRES)
{
psum += C << EntropyCoder.BITRES;
}
}
}
if (psum > total)
{
hi = mid - 1;
}
else
{
lo = mid + 1;
}
/*printf ("lo = %d, hi = %d\n", lo, hi);*/
} while (lo <= hi);
hi = lo--;
/*printf ("interp between %d and %d\n", lo, hi);*/
for (j = start; j < end; j++)
{
int bits1j, bits2j;
int N = m.eBands[j + 1] - m.eBands[j];
bits1j = C * N * m.allocVectors[lo * len + j] << LM >> 2;
bits2j = hi >= m.nbAllocVectors ?
cap[j] : C * N * m.allocVectors[hi * len + j] << LM >> 2;
if (bits1j > 0)
bits1j = Inlines.IMAX(0, bits1j + trim_offset[j]);
if (bits2j > 0)
bits2j = Inlines.IMAX(0, bits2j + trim_offset[j]);
if (lo > 0)
bits1j += offsets[j];
bits2j += offsets[j];
if (offsets[j] > 0)
skip_start = j;
bits2j = Inlines.IMAX(0, bits2j - bits1j);
bits1[j] = bits1j;
bits2[j] = bits2j;
}
codedBands = interp_bits2pulses(m, start, end, skip_start, bits1, bits2, thresh, cap,
total, out balance, skip_rsv, ref intensity, intensity_rsv, ref dual_stereo, dual_stereo_rsv,
pulses, ebits, fine_priority, C, LM, ec, encode, prev, signalBandwidth);
return codedBands;
}
}
}

View File

@@ -0,0 +1,74 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt.Structs
{
internal class AnalysisInfo
{
internal int valid = 0;
internal float tonality = 0;
internal float tonality_slope = 0;
internal float noisiness = 0;
internal float activity = 0;
internal float music_prob = 0;
internal int bandwidth = 0;
internal AnalysisInfo()
{
}
internal void Assign(AnalysisInfo other)
{
this.valid = other.valid;
this.tonality = other.tonality;
this.tonality_slope = other.tonality_slope;
this.noisiness = other.noisiness;
this.activity = other.activity;
this.music_prob = other.music_prob;
this.bandwidth = other.bandwidth;
}
internal void Reset()
{
valid = 0;
tonality = 0;
tonality_slope = 0;
noisiness = 0;
activity = 0;
music_prob = 0;
bandwidth = 0;
}
}
}

View File

@@ -0,0 +1,873 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt.Structs
{
using Concentus.Celt.Enums;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using System;
/// <summary>
/// Decoder state
/// </summary>
internal class CeltDecoder
{
internal CeltMode mode = null;
internal int overlap = 0;
internal int channels = 0;
internal int stream_channels = 0;
internal int downsample = 0;
internal int start = 0;
internal int end = 0;
internal int signalling = 0;
/* Everything beyond this point gets cleared on a reset */
internal uint rng = 0;
internal int error = 0;
internal int last_pitch_index = 0;
internal int loss_count = 0;
internal int postfilter_period = 0;
internal int postfilter_period_old = 0;
internal int postfilter_gain = 0;
internal int postfilter_gain_old = 0;
internal int postfilter_tapset = 0;
internal int postfilter_tapset_old = 0;
internal readonly int[] preemph_memD = new int[2];
/// <summary>
/// Scratch space used by the decoder. It is actually a variable-sized
/// field that resulted in a variable-sized struct. There are 6 distinct regions inside.
/// I have laid them out into separate variables here,
/// but these were the original definitions:
/// val32 decode_mem[], Size = channels*(DECODE_BUFFER_SIZE+mode.overlap)
/// val16 lpc[], Size = channels*LPC_ORDER
/// val16 oldEBands[], Size = 2*mode.nbEBands
/// val16 oldLogE[], Size = 2*mode.nbEBands
/// val16 oldLogE2[], Size = 2*mode.nbEBands
/// val16 backgroundLogE[], Size = 2*mode.nbEBands
/// </summary>
internal int[][] decode_mem = null;
internal int[][] lpc = null; // Porting note: Split two-part array into separate arrays (one per channel)
internal int[] oldEBands = null;
internal int[] oldLogE = null;
internal int[] oldLogE2 = null;
internal int[] backgroundLogE = null;
private void Reset()
{
mode = null;
overlap = 0;
channels = 0;
stream_channels = 0;
downsample = 0;
start = 0;
end = 0;
signalling = 0;
PartialReset();
}
private void PartialReset()
{
rng = 0;
error = 0;
last_pitch_index = 0;
loss_count = 0;
postfilter_period = 0;
postfilter_period_old = 0;
postfilter_gain = 0;
postfilter_gain_old = 0;
postfilter_tapset = 0;
postfilter_tapset_old = 0;
Arrays.MemSetInt(preemph_memD, 0, 2);
decode_mem = null;
lpc = null;
oldEBands = null;
oldLogE = null;
oldLogE2 = null;
backgroundLogE = null;
}
#region API functions
internal void ResetState()
{
int i;
this.PartialReset();
// We have to reconstitute the dynamic buffers here. fixme: this could be better implemented
this.decode_mem = new int[this.channels][];
this.lpc = new int[this.channels][];
for (int c = 0; c < this.channels; c++)
{
this.decode_mem[c] = new int[CeltConstants.DECODE_BUFFER_SIZE + this.mode.overlap];
this.lpc[c] = new int[CeltConstants.LPC_ORDER];
}
this.oldEBands = new int[2 * this.mode.nbEBands];
this.oldLogE = new int[2 * this.mode.nbEBands];
this.oldLogE2 = new int[2 * this.mode.nbEBands];
this.backgroundLogE = new int[2 * this.mode.nbEBands];
for (i = 0; i < 2 * this.mode.nbEBands; i++)
this.oldLogE[i] = this.oldLogE2[i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
internal int celt_decoder_init(int sampling_rate, int channels)
{
int ret;
ret = this.opus_custom_decoder_init(CeltMode.mode48000_960_120, channels);
if (ret != OpusError.OPUS_OK)
return ret;
this.downsample = CeltCommon.resampling_factor(sampling_rate);
if (this.downsample == 0)
return OpusError.OPUS_BAD_ARG;
else
return OpusError.OPUS_OK;
}
private int opus_custom_decoder_init(CeltMode mode, int channels)
{
if (channels < 0 || channels > 2)
return OpusError.OPUS_BAD_ARG;
if (this == null)
return OpusError.OPUS_ALLOC_FAIL;
this.Reset();
this.mode = mode;
this.overlap = mode.overlap;
this.stream_channels = this.channels = channels;
this.downsample = 1;
this.start = 0;
this.end = this.mode.effEBands;
this.signalling = 1;
this.loss_count = 0;
//this.decode_mem = new int[channels * (CeltConstants.DECODE_BUFFER_SIZE + mode.overlap));
//this.lpc = new int[channels * CeltConstants.LPC_ORDER);
//this.oldEBands = new int[2 * mode.nbEBands);
//this.oldLogE = new int[2 * mode.nbEBands);
//this.oldLogE2 = new int[2 * mode.nbEBands);
//this.backgroundLogE = new int[2 * mode.nbEBands);
this.ResetState();
return OpusError.OPUS_OK;
}
internal void celt_decode_lost(int N, int LM)
{
int c;
int i;
int C = this.channels;
int[][] out_syn = new int[2][];
int[] out_syn_ptrs = new int[2];
CeltMode mode;
int nbEBands;
int overlap;
int noise_based;
short[] eBands;
mode = this.mode;
nbEBands = mode.nbEBands;
overlap = mode.overlap;
eBands = mode.eBands;
c = 0; do
{
out_syn[c] = this.decode_mem[c];
out_syn_ptrs[c] = CeltConstants.DECODE_BUFFER_SIZE - N;
} while (++c < C);
noise_based = (loss_count >= 5 || start != 0) ? 1 : 0;
if (noise_based != 0)
{
/* Noise-based PLC/CNG */
int[][] X;
uint seed;
int end;
int effEnd;
int decay;
end = this.end;
effEnd = Inlines.IMAX(start, Inlines.IMIN(end, mode.effEBands));
X = Arrays.InitTwoDimensionalArray<int>(C, N); /*< Interleaved normalised MDCTs */
/* Energy decay */
decay = loss_count == 0 ? ((short)(0.5 + (1.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.5f, CeltConstants.DB_SHIFT)*/ : ((short)(0.5 + (0.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.5f, CeltConstants.DB_SHIFT)*/;
c = 0; do
{
for (i = start; i < end; i++)
this.oldEBands[c * nbEBands + i] = Inlines.MAX16(backgroundLogE[c * nbEBands + i], this.oldEBands[c * nbEBands + i] - decay);
} while (++c < C);
seed = this.rng;
for (c = 0; c < C; c++)
{
for (i = start; i < effEnd; i++)
{
int j;
int boffs;
int blen;
boffs = (eBands[i] << LM);
blen = (eBands[i + 1] - eBands[i]) << LM;
for (j = 0; j < blen; j++)
{
seed = Bands.celt_lcg_rand(seed);
X[c][boffs + j] = (unchecked((int)seed) >> 20);
}
VQ.renormalise_vector(X[c], 0, blen, CeltConstants.Q15ONE);
}
}
this.rng = seed;
c = 0;
do
{
Arrays.MemMoveInt(this.decode_mem[c], N, 0, CeltConstants.DECODE_BUFFER_SIZE - N + (overlap >> 1));
} while (++c < C);
CeltCommon.celt_synthesis(mode, X, out_syn, out_syn_ptrs, this.oldEBands, start, effEnd, C, C, 0, LM, this.downsample, 0);
}
else
{
/* Pitch-based PLC */
int[] window;
int fade = CeltConstants.Q15ONE;
int pitch_index;
int[] etmp;
int[] exc;
if (loss_count == 0)
{
this.last_pitch_index = pitch_index = CeltCommon.celt_plc_pitch_search(this.decode_mem, C);
}
else {
pitch_index = this.last_pitch_index;
fade = ((short)(0.5 + (.8f) * (((int)1) << (15))))/*Inlines.QCONST16(.8f, 15)*/;
}
etmp = new int[overlap];
exc = new int[CeltConstants.MAX_PERIOD];
window = mode.window;
c = 0; do
{
int decay;
int attenuation;
int S1 = 0;
int[] buf;
int extrapolation_offset;
int extrapolation_len;
int exc_length;
int j;
buf = this.decode_mem[c];
for (i = 0; i < CeltConstants.MAX_PERIOD; i++)
{
exc[i] = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - CeltConstants.MAX_PERIOD + i], CeltConstants.SIG_SHIFT);
}
if (loss_count == 0)
{
int[] ac = new int[CeltConstants.LPC_ORDER + 1];
/* Compute LPC coefficients for the last MAX_PERIOD samples before
the first loss so we can work in the excitation-filter domain. */
Autocorrelation._celt_autocorr(exc, ac, window, overlap,
CeltConstants.LPC_ORDER, CeltConstants.MAX_PERIOD);
/* Add a noise floor of -40 dB. */
ac[0] += Inlines.SHR32(ac[0], 13);
/* Use lag windowing to stabilize the Levinson-Durbin recursion. */
for (i = 1; i <= CeltConstants.LPC_ORDER; i++)
{
/*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/
ac[i] -= Inlines.MULT16_32_Q15(2 * i * i, ac[i]);
}
CeltLPC.celt_lpc(this.lpc[c], ac, CeltConstants.LPC_ORDER);
}
/* We want the excitation for 2 pitch periods in order to look for a
decaying signal, but we can't get more than MAX_PERIOD. */
exc_length = Inlines.IMIN(2 * pitch_index, CeltConstants.MAX_PERIOD);
/* Initialize the LPC history with the samples just before the start
of the region for which we're computing the excitation. */
{
int[] lpc_mem = new int[CeltConstants.LPC_ORDER];
for (i = 0; i < CeltConstants.LPC_ORDER; i++)
{
lpc_mem[i] =
Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - exc_length - 1 - i], CeltConstants.SIG_SHIFT);
}
/* Compute the excitation for exc_length samples before the loss. */
#if UNSAFE
unsafe
{
fixed (int* pexc_base = exc, lpc = this.lpc[c])
{
int* pexc = pexc_base + (CeltConstants.MAX_PERIOD - exc_length);
Kernels.celt_fir(pexc, lpc, pexc, exc_length, CeltConstants.LPC_ORDER, lpc_mem);
}
}
#else
Kernels.celt_fir(exc, (CeltConstants.MAX_PERIOD - exc_length), this.lpc[c], 0,
exc, (CeltConstants.MAX_PERIOD - exc_length), exc_length, CeltConstants.LPC_ORDER, lpc_mem);
#endif
}
/* Check if the waveform is decaying, and if so how fast.
We do this to avoid adding energy when concealing in a segment
with decaying energy. */
{
int E1 = 1, E2 = 1;
int decay_length;
int shift = Inlines.IMAX(0, 2 * Inlines.celt_zlog2(Inlines.celt_maxabs16(exc, (CeltConstants.MAX_PERIOD - exc_length), exc_length)) - 20);
decay_length = exc_length >> 1;
for (i = 0; i < decay_length; i++)
{
int e;
e = exc[CeltConstants.MAX_PERIOD - decay_length + i];
E1 += Inlines.SHR32(Inlines.MULT16_16(e, e), shift);
e = exc[CeltConstants.MAX_PERIOD - 2 * decay_length + i];
E2 += Inlines.SHR32(Inlines.MULT16_16(e, e), shift);
}
E1 = Inlines.MIN32(E1, E2);
decay = Inlines.celt_sqrt(Inlines.frac_div32(Inlines.SHR32(E1, 1), E2));
}
/* Move the decoder memory one frame to the left to give us room to
add the data for the new frame. We ignore the overlap that extends
past the end of the buffer, because we aren't going to use it. */
Arrays.MemMoveInt(buf, N, 0, CeltConstants.DECODE_BUFFER_SIZE - N);
/* Extrapolate from the end of the excitation with a period of
"pitch_index", scaling down each period by an additional factor of
"decay". */
extrapolation_offset = CeltConstants.MAX_PERIOD - pitch_index;
/* We need to extrapolate enough samples to cover a complete MDCT
window (including overlap/2 samples on both sides). */
extrapolation_len = N + overlap;
/* We also apply fading if this is not the first loss. */
attenuation = Inlines.MULT16_16_Q15(fade, decay);
for (i = j = 0; i < extrapolation_len; i++, j++)
{
int tmp;
if (j >= pitch_index)
{
j -= pitch_index;
attenuation = Inlines.MULT16_16_Q15(attenuation, decay);
}
buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] =
Inlines.SHL32((Inlines.MULT16_16_Q15(attenuation,
exc[extrapolation_offset + j])), CeltConstants.SIG_SHIFT);
/* Compute the energy of the previously decoded signal whose
excitation we're copying. */
tmp = Inlines.ROUND16(
buf[CeltConstants.DECODE_BUFFER_SIZE - CeltConstants.MAX_PERIOD - N + extrapolation_offset + j],
CeltConstants.SIG_SHIFT);
S1 += Inlines.SHR32(Inlines.MULT16_16(tmp, tmp), 8);
}
{
int[] lpc_mem = new int[CeltConstants.LPC_ORDER];
/* Copy the last decoded samples (prior to the overlap region) to
synthesis filter memory so we can have a continuous signal. */
for (i = 0; i < CeltConstants.LPC_ORDER; i++)
lpc_mem[i] = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - N - 1 - i], CeltConstants.SIG_SHIFT);
/* Apply the synthesis filter to convert the excitation back into
the signal domain. */
CeltLPC.celt_iir(buf, CeltConstants.DECODE_BUFFER_SIZE - N, this.lpc[c],
buf, CeltConstants.DECODE_BUFFER_SIZE - N, extrapolation_len, CeltConstants.LPC_ORDER,
lpc_mem);
}
/* Check if the synthesis energy is higher than expected, which can
happen with the signal changes during our window. If so,
attenuate. */
{
int S2 = 0;
for (i = 0; i < extrapolation_len; i++)
{
int tmp = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - N + i], CeltConstants.SIG_SHIFT);
S2 += Inlines.SHR32(Inlines.MULT16_16(tmp, tmp), 8);
}
/* This checks for an "explosion" in the synthesis. */
if (!(S1 > Inlines.SHR32(S2, 2)))
{
for (i = 0; i < extrapolation_len; i++)
buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] = 0;
}
else if (S1 < S2)
{
int ratio = Inlines.celt_sqrt(Inlines.frac_div32(Inlines.SHR32(S1, 1) + 1, S2 + 1));
for (i = 0; i < overlap; i++)
{
int tmp_g = CeltConstants.Q15ONE
- Inlines.MULT16_16_Q15(window[i], CeltConstants.Q15ONE - ratio);
buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] =
Inlines.MULT16_32_Q15(tmp_g, buf[CeltConstants.DECODE_BUFFER_SIZE - N + i]);
}
for (i = overlap; i < extrapolation_len; i++)
{
buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] =
Inlines.MULT16_32_Q15(ratio, buf[CeltConstants.DECODE_BUFFER_SIZE - N + i]);
}
}
}
/* Apply the pre-filter to the MDCT overlap for the next frame because
the post-filter will be re-applied in the decoder after the MDCT
overlap. */
CeltCommon.comb_filter(etmp, 0, buf, CeltConstants.DECODE_BUFFER_SIZE,
this.postfilter_period, this.postfilter_period, overlap,
-this.postfilter_gain, -this.postfilter_gain,
this.postfilter_tapset, this.postfilter_tapset, null, 0);
/* Simulate TDAC on the concealed audio so that it blends with the
MDCT of the next frame. */
for (i = 0; i < overlap / 2; i++)
{
buf[CeltConstants.DECODE_BUFFER_SIZE + i] =
Inlines.MULT16_32_Q15(window[i], etmp[overlap - 1 - i])
+ Inlines.MULT16_32_Q15(window[overlap - i - 1], etmp[i]);
}
} while (++c < C);
}
this.loss_count = loss_count + 1;
}
internal int celt_decode_with_ec(byte[] data, int data_ptr,
int len, short[] pcm, int pcm_ptr, int frame_size, EntropyCoder dec, int accum)
{
int c, i, N;
int spread_decision;
int bits;
int[][] X;
int[] fine_quant;
int[] pulses;
int[] cap;
int[] offsets;
int[] fine_priority;
int[] tf_res;
byte[] collapse_masks;
int[][] out_syn = new int[2][];
int[] out_syn_ptrs = new int[2];
int[] oldBandE, oldLogE, oldLogE2, backgroundLogE;
int shortBlocks;
int isTransient;
int intra_ener;
int CC = this.channels;
int LM, M;
int start;
int end;
int effEnd;
int codedBands;
int alloc_trim;
int postfilter_pitch;
int postfilter_gain;
int intensity = 0;
int dual_stereo = 0;
int total_bits;
int balance;
int tell;
int dynalloc_logp;
int postfilter_tapset;
int anti_collapse_rsv;
int anti_collapse_on = 0;
int silence;
int C = this.stream_channels;
CeltMode mode; // porting note: pointer
int nbEBands;
int overlap;
short[] eBands;
mode = this.mode;
nbEBands = mode.nbEBands;
overlap = mode.overlap;
eBands = mode.eBands;
start = this.start;
end = this.end;
frame_size *= this.downsample;
oldBandE = this.oldEBands;
oldLogE = this.oldLogE;
oldLogE2 = this.oldLogE2;
backgroundLogE = this.backgroundLogE;
{
for (LM = 0; LM <= mode.maxLM; LM++)
if (mode.shortMdctSize << LM == frame_size)
break;
if (LM > mode.maxLM)
return OpusError.OPUS_BAD_ARG;
}
M = 1 << LM;
if (len < 0 || len > 1275 || pcm == null)
return OpusError.OPUS_BAD_ARG;
N = M * mode.shortMdctSize;
c = 0; do
{
out_syn[c] = this.decode_mem[c];
out_syn_ptrs[c] = CeltConstants.DECODE_BUFFER_SIZE - N;
} while (++c < CC);
effEnd = end;
if (effEnd > mode.effEBands)
effEnd = mode.effEBands;
if (data == null || len <= 1)
{
this.celt_decode_lost(N, LM);
CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum);
return frame_size / this.downsample;
}
if (dec == null)
{
// If no entropy decoder was passed into this function, we need to create
// a new one here for local use only. It only exists in this function scope.
dec = new EntropyCoder();
dec.dec_init(data, data_ptr, (uint)len);
}
if (C == 1)
{
for (i = 0; i < nbEBands; i++)
oldBandE[i] = Inlines.MAX16(oldBandE[i], oldBandE[nbEBands + i]);
}
total_bits = len * 8;
tell = dec.tell();
if (tell >= total_bits)
silence = 1;
else if (tell == 1)
silence = dec.dec_bit_logp(15);
else
silence = 0;
if (silence != 0)
{
/* Pretend we've read all the remaining bits */
tell = len * 8;
dec.nbits_total += tell - dec.tell();
}
postfilter_gain = 0;
postfilter_pitch = 0;
postfilter_tapset = 0;
if (start == 0 && tell + 16 <= total_bits)
{
if (dec.dec_bit_logp(1) != 0)
{
int qg, octave;
octave = (int)dec.dec_uint(6);
postfilter_pitch = (16 << octave) + (int)dec.dec_bits(4 + (uint)octave) - 1;
qg = (int)dec.dec_bits(3);
if (dec.tell() + 2 <= total_bits)
postfilter_tapset = dec.dec_icdf(Tables.tapset_icdf, 2);
postfilter_gain = ((short)(0.5 + (.09375f) * (((int)1) << (15))))/*Inlines.QCONST16(.09375f, 15)*/ * (qg + 1);
}
tell = dec.tell();
}
if (LM > 0 && tell + 3 <= total_bits)
{
isTransient = dec.dec_bit_logp(3);
tell = dec.tell();
}
else
isTransient = 0;
if (isTransient != 0)
shortBlocks = M;
else
shortBlocks = 0;
/* Decode the global flags (first symbols in the stream) */
intra_ener = tell + 3 <= total_bits ? dec.dec_bit_logp(3) : 0;
/* Get band energies */
QuantizeBands.unquant_coarse_energy(mode, start, end, oldBandE,
intra_ener, dec, C, LM);
tf_res = new int[nbEBands];
CeltCommon.tf_decode(start, end, isTransient, tf_res, LM, dec);
tell = dec.tell();
spread_decision = Spread.SPREAD_NORMAL;
if (tell + 4 <= total_bits)
spread_decision = dec.dec_icdf(Tables.spread_icdf, 5);
cap = new int[nbEBands];
CeltCommon.init_caps(mode, cap, LM, C);
offsets = new int[nbEBands];
dynalloc_logp = 6;
total_bits <<= EntropyCoder.BITRES;
tell = (int)dec.tell_frac();
for (i = start; i < end; i++)
{
int width, quanta;
int dynalloc_loop_logp;
int boost;
width = C * (eBands[i + 1] - eBands[i]) << LM;
/* quanta is 6 bits, but no more than 1 bit/sample
and no less than 1/8 bit/sample */
quanta = Inlines.IMIN(width << EntropyCoder.BITRES, Inlines.IMAX(6 << EntropyCoder.BITRES, width));
dynalloc_loop_logp = dynalloc_logp;
boost = 0;
while (tell + (dynalloc_loop_logp << EntropyCoder.BITRES) < total_bits && boost < cap[i])
{
int flag;
flag = dec.dec_bit_logp((uint)dynalloc_loop_logp);
tell = (int)dec.tell_frac();
if (flag == 0)
break;
boost += quanta;
total_bits -= quanta;
dynalloc_loop_logp = 1;
}
offsets[i] = boost;
/* Making dynalloc more likely */
if (boost > 0)
dynalloc_logp = Inlines.IMAX(2, dynalloc_logp - 1);
}
fine_quant = new int[nbEBands];
alloc_trim = tell + (6 << EntropyCoder.BITRES) <= total_bits ?
dec.dec_icdf(Tables.trim_icdf, 7) : 5;
bits = (((int)len * 8) << EntropyCoder.BITRES) - (int)dec.tell_frac() - 1;
anti_collapse_rsv = isTransient != 0 && LM >= 2 && bits >= ((LM + 2) << EntropyCoder.BITRES) ? (1 << EntropyCoder.BITRES) : 0;
bits -= anti_collapse_rsv;
pulses = new int[nbEBands];
fine_priority = new int[nbEBands];
codedBands = Rate.compute_allocation(mode, start, end, offsets, cap,
alloc_trim, ref intensity, ref dual_stereo, bits, out balance, pulses,
fine_quant, fine_priority, C, LM, dec, 0, 0, 0);
QuantizeBands.unquant_fine_energy(mode, start, end, oldBandE, fine_quant, dec, C);
c = 0;
do
{
Arrays.MemMoveInt(decode_mem[c], N, 0, CeltConstants.DECODE_BUFFER_SIZE - N + overlap / 2);
} while (++c < CC);
/* Decode fixed codebook */
collapse_masks = new byte[C * nbEBands];
X = Arrays.InitTwoDimensionalArray<int>(C, N); /*< Interleaved normalised MDCTs */
Bands.quant_all_bands(0, mode, start, end, X[0], C == 2 ? X[1] : null, collapse_masks,
null, pulses, shortBlocks, spread_decision, dual_stereo, intensity, tf_res,
len * (8 << EntropyCoder.BITRES) - anti_collapse_rsv, balance, dec, LM, codedBands, ref this.rng);
if (anti_collapse_rsv > 0)
{
anti_collapse_on = (int)dec.dec_bits(1);
}
QuantizeBands.unquant_energy_finalise(mode, start, end, oldBandE,
fine_quant, fine_priority, len * 8 - dec.tell(), dec, C);
if (anti_collapse_on != 0)
Bands.anti_collapse(mode, X, collapse_masks, LM, C, N,
start, end, oldBandE, oldLogE, oldLogE2, pulses, this.rng);
if (silence != 0)
{
for (i = 0; i < C * nbEBands; i++)
oldBandE[i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
CeltCommon.celt_synthesis(mode, X, out_syn, out_syn_ptrs, oldBandE, start, effEnd,
C, CC, isTransient, LM, this.downsample, silence);
c = 0; do
{
this.postfilter_period = Inlines.IMAX(this.postfilter_period, CeltConstants.COMBFILTER_MINPERIOD);
this.postfilter_period_old = Inlines.IMAX(this.postfilter_period_old, CeltConstants.COMBFILTER_MINPERIOD);
CeltCommon.comb_filter(out_syn[c], out_syn_ptrs[c], out_syn[c], out_syn_ptrs[c], this.postfilter_period_old, this.postfilter_period, mode.shortMdctSize,
this.postfilter_gain_old, this.postfilter_gain, this.postfilter_tapset_old, this.postfilter_tapset,
mode.window, overlap);
if (LM != 0)
{
CeltCommon.comb_filter(
out_syn[c], out_syn_ptrs[c] + (mode.shortMdctSize),
out_syn[c], out_syn_ptrs[c] + (mode.shortMdctSize),
this.postfilter_period, postfilter_pitch, N - mode.shortMdctSize,
this.postfilter_gain, postfilter_gain, this.postfilter_tapset, postfilter_tapset,
mode.window, overlap);
}
} while (++c < CC);
this.postfilter_period_old = this.postfilter_period;
this.postfilter_gain_old = this.postfilter_gain;
this.postfilter_tapset_old = this.postfilter_tapset;
this.postfilter_period = postfilter_pitch;
this.postfilter_gain = postfilter_gain;
this.postfilter_tapset = postfilter_tapset;
if (LM != 0)
{
this.postfilter_period_old = this.postfilter_period;
this.postfilter_gain_old = this.postfilter_gain;
this.postfilter_tapset_old = this.postfilter_tapset;
}
if (C == 1)
{
Array.Copy(oldBandE, 0, oldBandE, nbEBands, nbEBands);
}
/* In case start or end were to change */
if (isTransient == 0)
{
int max_background_increase;
Array.Copy(oldLogE, oldLogE2, 2 * nbEBands);
Array.Copy(oldBandE, oldLogE, 2 * nbEBands);
/* In normal circumstances, we only allow the noise floor to increase by
up to 2.4 dB/second, but when we're in DTX, we allow up to 6 dB
increase for each update.*/
if (this.loss_count < 10)
max_background_increase = M * ((short)(0.5 + (0.001f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.001f, CeltConstants.DB_SHIFT)*/;
else
max_background_increase = ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/;
for (i = 0; i < 2 * nbEBands; i++)
backgroundLogE[i] = Inlines.MIN16(backgroundLogE[i] + max_background_increase, oldBandE[i]);
}
else {
for (i = 0; i < 2 * nbEBands; i++)
oldLogE[i] = Inlines.MIN16(oldLogE[i], oldBandE[i]);
}
c = 0; do
{
for (i = 0; i < start; i++)
{
oldBandE[c * nbEBands + i] = 0;
oldLogE[c * nbEBands + i] = oldLogE2[c * nbEBands + i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
for (i = end; i < nbEBands; i++)
{
oldBandE[c * nbEBands + i] = 0;
oldLogE[c * nbEBands + i] = oldLogE2[c * nbEBands + i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
} while (++c < 2);
this.rng = dec.rng;
CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum);
this.loss_count = 0;
if (dec.tell() > 8 * len)
return OpusError.OPUS_INTERNAL_ERROR;
if (dec.get_error() != 0)
this.error = 1;
return frame_size / this.downsample;
}
#endregion
#region Getters and Setters
internal void SetStartBand(int value)
{
if (value < 0 || value >= this.mode.nbEBands)
throw new ArgumentException("Start band above max number of ebands (or negative)");
this.start = value;
}
internal void SetEndBand(int value)
{
if (value < 1 || value > this.mode.nbEBands)
throw new ArgumentException("End band above max number of ebands (or less than 1)");
this.end = value;
}
internal void SetChannels(int value)
{
if (value < 1 || value > 2)
throw new ArgumentException("Channel count must be 1 or 2");
this.stream_channels = value;
}
internal int GetAndClearError()
{
int returnVal = this.error;
this.error = 0;
return returnVal;
}
public int GetLookahead()
{
return this.overlap / this.downsample;
}
public int GetPitch()
{
return this.postfilter_period;
}
public CeltMode GetMode()
{
return this.mode;
}
public void SetSignalling(int value)
{
this.signalling = value;
}
public uint GetFinalRange()
{
return this.rng;
}
#endregion
}
}

View File

@@ -0,0 +1,118 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt.Structs
{
using Concentus.Celt.Enums;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using System;
internal class CeltMode
{
internal int Fs = 0;
internal int overlap = 0;
internal int nbEBands = 0;
internal int effEBands = 0;
internal int[] preemph = { 0, 0, 0, 0 };
/// <summary>
/// Definition for each "pseudo-critical band"
/// </summary>
internal short[] eBands = null;
internal int maxLM = 0;
internal int nbShortMdcts = 0;
internal int shortMdctSize = 0;
/// <summary>
/// Number of lines in allocVectors
/// </summary>
internal int nbAllocVectors = 0;
/// <summary>
/// Number of bits in each band for several rates
/// </summary>
internal byte[] allocVectors = null;
internal short[] logN = null;
internal int[] window = null;
internal MDCTLookup mdct = new MDCTLookup();
internal PulseCache cache = new PulseCache();
private CeltMode()
{
}
internal static readonly CeltMode mode48000_960_120 = new CeltMode
{
Fs = 48000,
overlap = 120,
nbEBands = 21,
effEBands = 21,
preemph = new int[] { 27853, 0, 4096, 8192 },
eBands = Tables.eband5ms,
maxLM = 3,
nbShortMdcts = 8,
shortMdctSize = 120,
nbAllocVectors = 11,
allocVectors = Tables.band_allocation,
logN = Tables.logN400,
window = Tables.window120,
mdct = new MDCTLookup()
{
n = 1920,
maxshift = 3,
kfft = new FFTState[]
{
Tables.fft_state48000_960_0,
Tables.fft_state48000_960_1,
Tables.fft_state48000_960_2,
Tables.fft_state48000_960_3,
},
trig = Tables.mdct_twiddles960
},
cache = new PulseCache()
{
size = 392,
index = Tables.cache_index50,
bits = Tables.cache_bits50,
caps = Tables.cache_caps50,
}
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
/* Copyright (c) 2003-2004, Mark Borgerding
Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Modified from KISS-FFT by Jean-Marc Valin
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt.Structs
{
using Concentus.Celt.Enums;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using System;
internal class FFTState
{
internal int nfft = 0;
internal short scale = 0;
internal int scale_shift = 0;
internal int shift = 0;
internal short[] factors = new short[2 * KissFFT.MAXFACTORS];
internal short[] bitrev = null;
internal short[] twiddles = null;
}
}

View File

@@ -0,0 +1,59 @@
/* Copyright (c) 2003-2004, Mark Borgerding
Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Modified from KISS-FFT by Jean-Marc Valin
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt.Structs
{
using Concentus.Celt.Enums;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using System;
internal class MDCTLookup
{
internal int n = 0;
internal int maxshift = 0;
// [porting note] these are pointers to static states defined in tables.cs
internal FFTState[] kfft = new FFTState[4];
internal short[] trig = null;
internal MDCTLookup()
{
}
}
}

View File

@@ -0,0 +1,59 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt.Structs
{
using Concentus.Celt.Enums;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using System;
internal class PulseCache
{
internal int size = 0;
internal short[] index = null;
internal byte[] bits = null;
internal byte[] caps = null;
internal void Reset()
{
size = 0;
index = null;
bits = null;
caps = null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,430 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt
{
using Concentus.Celt.Enums;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
internal static class VQ
{
internal static void exp_rotation1(int[] X, int X_ptr, int len, int stride, int c, int s)
{
int i;
int ms;
int Xptr;
Xptr = X_ptr;
ms = Inlines.NEG16(s);
for (i = 0; i < len - stride; i++)
{
int x1, x2;
x1 = X[Xptr];
x2 = X[Xptr + stride];
X[Xptr + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x2), s, x1), 15));
X[Xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x1), ms, x2), 15));
Xptr++;
}
Xptr = X_ptr + (len - 2 * stride - 1);
for (i = len - 2 * stride - 1; i >= 0; i--)
{
int x1, x2;
x1 = X[Xptr];
x2 = X[Xptr + stride];
X[Xptr + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x2), s, x1), 15));
X[Xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x1), ms, x2), 15));
Xptr--;
}
}
private static int[] SPREAD_FACTOR = { 15, 10, 5 };
internal static void exp_rotation(int[] X, int X_ptr, int len, int dir, int stride, int K, int spread)
{
int i;
int c, s;
int gain, theta;
int stride2 = 0;
int factor;
if (2 * K >= len || spread == Spread.SPREAD_NONE)
{
return;
}
factor = SPREAD_FACTOR[spread - 1];
gain = (Inlines.celt_div((int)Inlines.MULT16_16(CeltConstants.Q15_ONE, len), (int)(len + factor * K)));
theta = Inlines.HALF16(Inlines.MULT16_16_Q15(gain, gain));
c = Inlines.celt_cos_norm(Inlines.EXTEND32(theta));
s = Inlines.celt_cos_norm(Inlines.EXTEND32(Inlines.SUB16(CeltConstants.Q15ONE, theta))); /* sin(theta) */
if (len >= 8 * stride)
{
stride2 = 1;
/* This is just a simple (equivalent) way of computing sqrt(len/stride) with rounding.
It's basically incrementing long as (stride2+0.5)^2 < len/stride. */
while ((stride2 * stride2 + stride2) * stride + (stride >> 2) < len)
{
stride2++;
}
}
/*NOTE: As a minor optimization, we could be passing around log2(B), not B, for both this and for
extract_collapse_mask().*/
len = Inlines.celt_udiv(len, stride);
for (i = 0; i < stride; i++)
{
if (dir < 0)
{
if (stride2 != 0)
{
exp_rotation1(X, X_ptr + (i * len), len, stride2, s, c);
}
exp_rotation1(X, X_ptr + (i * len), len, 1, c, s);
}
else
{
exp_rotation1(X, X_ptr + (i * len), len, 1, c, (short)(0 - s));
if (stride2 != 0)
{
exp_rotation1(X, X_ptr + (i * len), len, stride2, s, (short)(0 - c));
}
}
}
}
/** Takes the pitch vector and the decoded residual vector, computes the gain
that will give ||p+g*y||=1 and mixes the residual with the pitch. */
internal static void normalise_residual(int[] iy, int[] X, int X_ptr,
int N, int Ryy, int gain)
{
int i;
int k;
int t;
int g;
k = Inlines.celt_ilog2(Ryy) >> 1;
t = Inlines.VSHR32(Ryy, 2 * (k - 7));
g = Inlines.MULT16_16_P15(Inlines.celt_rsqrt_norm(t), gain);
i = 0;
do
X[X_ptr + i] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(g, iy[i]), k + 1));
while (++i < N);
}
internal static uint extract_collapse_mask(int[] iy, int N, int B)
{
uint collapse_mask;
int N0;
int i;
if (B <= 1)
return 1;
/*NOTE: As a minor optimization, we could be passing around log2(B), not B, for both this and for
exp_rotation().*/
N0 = Inlines.celt_udiv(N, B);
collapse_mask = 0;
i = 0;
do
{
int j;
uint tmp = 0;
j = 0;
do
{
tmp |= unchecked((uint)iy[i * N0 + j]);
} while (++j < N0);
collapse_mask |= (tmp != 0 ? 1U : 0) << i;
} while (++i < B);
return collapse_mask;
}
internal static uint alg_quant(int[] X, int X_ptr, int N, int K, int spread, int B, EntropyCoder enc
)
{
int[] y = new int[N];
int[] iy = new int[N];
int[] signx = new int[N];
int i, j;
int s;
int pulsesLeft;
int sum;
int xy;
int yy;
uint collapse_mask;
Inlines.OpusAssert(K > 0, "alg_quant() needs at least one pulse");
Inlines.OpusAssert(N > 1, "alg_quant() needs at least two dimensions");
exp_rotation(X, X_ptr, N, 1, B, K, spread);
/* Get rid of the sign */
sum = 0;
j = 0;
do
{
int xpj = X_ptr + j;
/* OPT: Make sure the following two lines result in conditional moves
rather than branches. */
signx[j] = X[xpj] > 0 ? 1 : -1;
X[xpj] = Inlines.ABS16(X[xpj]);
iy[j] = 0;
y[j] = 0;
} while (++j < N);
xy = yy = 0;
pulsesLeft = K;
/* Do a pre-search by projecting on the pyramid */
if (K > (N >> 1))
{
int rcp;
j = 0; do
{
sum += X[X_ptr + j];
} while (++j < N);
/* If X is too small, just replace it with a pulse at 0 */
/* Prevents infinities and NaNs from causing too many pulses
to be allocated. 64 is an approximation of infinity here. */
if (sum <= K)
{
X[X_ptr] = ((short)(0.5 + (1.0f) * (((int)1) << (14))))/*Inlines.QCONST16(1.0f, 14)*/;
j = X_ptr + 1;
do
{
X[j] = 0;
} while (++j < N + X_ptr);
sum = ((short)(0.5 + (1.0f) * (((int)1) << (14))))/*Inlines.QCONST16(1.0f, 14)*/;
}
rcp = Inlines.EXTRACT16(Inlines.MULT16_32_Q16((K - 1), Inlines.celt_rcp(sum)));
j = 0;
do
{
/* It's really important to round *towards zero* here */
iy[j] = Inlines.MULT16_16_Q15(X[X_ptr + j], rcp);
y[j] = (int)iy[j];
yy = (Inlines.MAC16_16(yy, y[j], y[j]));
xy = Inlines.MAC16_16(xy, X[X_ptr + j], y[j]);
y[j] *= 2;
pulsesLeft -= iy[j];
} while (++j < N);
}
Inlines.OpusAssert(pulsesLeft >= 1, "Allocated too many pulses in the quick pass");
/* This should never happen, but just in case it does (e.g. on silence)
we fill the first bin with pulses. */
if (pulsesLeft > N + 3)
{
int tmp = (int)pulsesLeft;
yy = (Inlines.MAC16_16(yy, tmp, tmp));
yy = (Inlines.MAC16_16(yy, tmp, y[0]));
iy[0] += pulsesLeft;
pulsesLeft = 0;
}
s = 1;
for (i = 0; i < pulsesLeft; i++)
{
int best_id;
int best_num = 0 - CeltConstants.VERY_LARGE16;
int best_den = 0;
int rshift = 1 + Inlines.celt_ilog2(K - pulsesLeft + i + 1);
best_id = 0;
/* The squared magnitude term gets added anyway, so we might as well
add it outside the loop */
yy = Inlines.ADD16(yy, 1); // opus bug - was add32
j = 0;
do
{
int Rxy, Ryy;
/* Temporary sums of the new pulse(s) */
Rxy = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + j])), rshift));
/* We're multiplying y[j] by two so we don't have to do it here */
Ryy = Inlines.ADD16(yy, y[j]);
/* Approximate score: we maximise Rxy/sqrt(Ryy) (we're guaranteed that
Rxy is positive because the sign is pre-computed) */
Rxy = Inlines.MULT16_16_Q15(Rxy, Rxy);
/* The idea is to check for num/den >= best_num/best_den, but that way
we can do it without any division */
/* OPT: Make sure to use conditional moves here */
if (Inlines.MULT16_16(best_den, Rxy) > Inlines.MULT16_16(Ryy, best_num))
{
best_den = Ryy;
best_num = Rxy;
best_id = j;
}
} while (++j < N);
/* Updating the sums of the new pulse(s) */
xy = Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + best_id]));
/* We're multiplying y[j] by two so we don't have to do it here */
yy = Inlines.ADD16(yy, y[best_id]);
/* Only now that we've made the final choice, update y/iy */
/* Multiplying y[j] by 2 so we don't have to do it everywhere else */
y[best_id] = (y[best_id] + (2 * s));
iy[best_id]++;
}
/* Put the original sign back */
j = 0;
do
{
X[X_ptr + j] = (Inlines.MULT16_16(signx[j], X[X_ptr + j]));
/* OPT: Make sure your compiler uses a conditional move here rather than
a branch. */
iy[j] = signx[j] < 0 ? -iy[j] : iy[j];
} while (++j < N);
CWRS.encode_pulses(iy, N, K, enc);
collapse_mask = extract_collapse_mask(iy, N, B);
return collapse_mask;
}
/** Decode pulse vector and combine the result with the pitch vector to produce
the final normalised signal in the current band. */
internal static uint alg_unquant(int[] X, int X_ptr, int N, int K, int spread, int B,
EntropyCoder dec, int gain)
{
int Ryy;
uint collapse_mask;
int[] iy = new int[N];
Inlines.OpusAssert(K > 0, "alg_unquant() needs at least one pulse");
Inlines.OpusAssert(N > 1, "alg_unquant() needs at least two dimensions");
Ryy = CWRS.decode_pulses(iy, N, K, dec);
normalise_residual(iy, X, X_ptr, N, Ryy, gain);
exp_rotation(X, X_ptr, N, -1, B, K, spread);
collapse_mask = extract_collapse_mask(iy, N, B);
return collapse_mask;
}
internal static void renormalise_vector(int[] X, int X_ptr, int N, int gain)
{
int i;
int k;
int E;
int g;
int t;
int xptr;
#if UNSAFE
unsafe
{
fixed (int* px_base = X)
{
int* px = px_base + X_ptr;
E = CeltConstants.EPSILON + Kernels.celt_inner_prod(px, px, N);
}
}
#else
E = CeltConstants.EPSILON + Kernels.celt_inner_prod(X, X_ptr, X, X_ptr, N);
#endif
k = Inlines.celt_ilog2(E) >> 1;
t = Inlines.VSHR32(E, 2 * (k - 7));
g = Inlines.MULT16_16_P15(Inlines.celt_rsqrt_norm(t), gain);
xptr = X_ptr;
for (i = 0; i < N; i++)
{
X[xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(g, X[xptr]), k + 1));
xptr++;
}
/*return celt_sqrt(E);*/
}
internal static int stereo_itheta(int[] X, int X_ptr, int[] Y, int Y_ptr, int stereo, int N)
{
int i;
int itheta;
int mid, side;
int Emid, Eside;
Emid = Eside = CeltConstants.EPSILON;
if (stereo != 0)
{
for (i = 0; i < N; i++)
{
int m, s;
m = Inlines.ADD16(Inlines.SHR16(X[X_ptr + i], 1), Inlines.SHR16(Y[Y_ptr + i], 1));
s = Inlines.SUB16(Inlines.SHR16(X[X_ptr + i], 1), Inlines.SHR16(Y[Y_ptr + i], 1));
Emid = Inlines.MAC16_16(Emid, m, m);
Eside = Inlines.MAC16_16(Eside, s, s);
}
}
else {
#if UNSAFE
unsafe
{
fixed (int* px_base = X, py_base = Y)
{
int* px = px_base + X_ptr;
int* py = py_base + Y_ptr;
Emid += Kernels.celt_inner_prod(px, px, N);
Eside += Kernels.celt_inner_prod(py, py, N);
}
}
#else
Emid += Kernels.celt_inner_prod(X, X_ptr, X, X_ptr, N);
Eside += Kernels.celt_inner_prod(Y, Y_ptr, Y, Y_ptr, N);
#endif
}
mid = (Inlines.celt_sqrt(Emid));
side = (Inlines.celt_sqrt(Eside));
/* 0.63662 = 2/pi */
itheta = Inlines.MULT16_16_Q15(((short)(0.5 + (0.63662f) * (((int)1) << (15))))/*Inlines.QCONST16(0.63662f, 15)*/, Inlines.celt_atan2p(side, mid));
return itheta;
}
}
}

View File

@@ -0,0 +1,284 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if !UNSAFE
namespace Concentus.Common
{
using Concentus.Celt;
using Concentus.Common.CPlusPlus;
internal static class Autocorrelation
{
/* Compute autocorrelation */
internal static void silk_autocorr(
int[] results, /* O Result (length correlationCount) */
BoxedValueInt scale, /* O Scaling of the correlation vector */
short[] inputData, /* I Input data to correlate */
int inputDataSize, /* I Length of input */
int correlationCount /* I Number of correlation taps to compute */
)
{
int corrCount = Inlines.silk_min_int(inputDataSize, correlationCount);
scale.Val = Autocorrelation._celt_autocorr(inputData, results, corrCount - 1, inputDataSize);
}
internal static int _celt_autocorr(
short[] x, /* in: [0...n-1] samples x */
int[] ac, /* out: [0...lag-1] ac values */
int lag,
int n
)
{
int d;
int i, k;
int fastN = n - lag;
int shift;
short[] xptr;
short[] xx = new short[n];
Inlines.OpusAssert(n > 0);
xptr = x;
shift = 0;
{
int ac0;
ac0 = 1 + (n << 7);
if ((n & 1) != 0)
{
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9);
}
for (i = (n & 1); i < n; i += 2)
{
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9);
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9);
}
shift = Inlines.celt_ilog2(ac0) - 30 + 10;
shift = (shift) / 2;
if (shift > 0)
{
for (i = 0; i < n; i++)
{
xx[i] = (short)(Inlines.PSHR32(xptr[i], shift));
}
xptr = xx;
}
else
shift = 0;
}
CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1);
for (k = 0; k <= lag; k++)
{
for (i = k + fastN, d = 0; i < n; i++)
d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]);
ac[k] += d;
}
shift = 2 * shift;
if (shift <= 0)
ac[0] += Inlines.SHL32((int)1, -shift);
if (ac[0] < 268435456)
{
int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]);
for (i = 0; i <= lag; i++)
{
ac[i] = Inlines.SHL32(ac[i], shift2);
}
shift -= shift2;
}
else if (ac[0] >= 536870912)
{
int shift2 = 1;
if (ac[0] >= 1073741824)
shift2++;
for (i = 0; i <= lag; i++)
{
ac[i] = Inlines.SHR32(ac[i], shift2);
}
shift += shift2;
}
return shift;
}
internal static int _celt_autocorr(
int[] x, /* in: [0...n-1] samples x */
int[] ac, /* out: [0...lag-1] ac values */
int[] window,
int overlap,
int lag,
int n)
{
int d;
int i, k;
int fastN = n - lag;
int shift;
int[] xptr;
int[] xx = new int[n];
Inlines.OpusAssert(n > 0);
Inlines.OpusAssert(overlap >= 0);
if (overlap == 0)
{
xptr = x;
}
else
{
for (i = 0; i < n; i++)
xx[i] = x[i];
for (i = 0; i < overlap; i++)
{
xx[i] = Inlines.MULT16_16_Q15(x[i], window[i]);
xx[n - i - 1] = Inlines.MULT16_16_Q15(x[n - i - 1], window[i]);
}
xptr = xx;
}
shift = 0;
int ac0;
ac0 = 1 + (n << 7);
if ((n & 1) != 0)
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9);
for (i = (n & 1); i < n; i += 2)
{
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9);
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9);
}
shift = Inlines.celt_ilog2(ac0) - 30 + 10;
shift = (shift) / 2;
if (shift > 0)
{
for (i = 0; i < n; i++)
xx[i] = (Inlines.PSHR32(xptr[i], shift));
xptr = xx;
}
else
shift = 0;
CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1);
for (k = 0; k <= lag; k++)
{
for (i = k + fastN, d = 0; i < n; i++)
d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]);
ac[k] += d;
}
shift = 2 * shift;
if (shift <= 0)
ac[0] += Inlines.SHL32((int)1, -shift);
if (ac[0] < 268435456)
{
int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]);
for (i = 0; i <= lag; i++)
ac[i] = Inlines.SHL32(ac[i], shift2);
shift -= shift2;
}
else if (ac[0] >= 536870912)
{
int shift2 = 1;
if (ac[0] >= 1073741824)
shift2++;
for (i = 0; i <= lag; i++)
ac[i] = Inlines.SHR32(ac[i], shift2);
shift += shift2;
}
return shift;
}
private const int QC = 10;
private const int QS = 14;
/* Autocorrelations for a warped frequency axis */
internal static void silk_warped_autocorrelation(
int[] corr, /* O Result [order + 1] */
BoxedValueInt scale, /* O Scaling of the correlation vector */
short[] input, /* I Input data to correlate */
int warping_Q16, /* I Warping coefficient */
int length, /* I Length of input */
int order /* I Correlation order (even) */
)
{
int n, i, lsh;
int tmp1_QS, tmp2_QS;
int[] state_QS = new int[order + 1];// = { 0 };
long[] corr_QC = new long[order + 1];// = { 0 };
/* Order must be even */
Inlines.OpusAssert((order & 1) == 0);
Inlines.OpusAssert(2 * QS - QC >= 0);
/* Loop over samples */
for (n = 0; n < length; n++)
{
tmp1_QS = Inlines.silk_LSHIFT32((int)input[n], QS);
/* Loop over allpass sections */
for (i = 0; i < order; i += 2)
{
/* Output of allpass section */
tmp2_QS = Inlines.silk_SMLAWB(state_QS[i], state_QS[i + 1] - tmp1_QS, warping_Q16);
state_QS[i] = tmp1_QS;
corr_QC[i] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 2 * QS - QC);
/* Output of allpass section */
tmp1_QS = Inlines.silk_SMLAWB(state_QS[i + 1], state_QS[i + 2] - tmp2_QS, warping_Q16);
state_QS[i + 1] = tmp2_QS;
corr_QC[i + 1] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp2_QS, state_QS[0]), 2 * QS - QC);
}
state_QS[order] = tmp1_QS;
corr_QC[order] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 2 * QS - QC);
}
lsh = Inlines.silk_CLZ64(corr_QC[0]) - 35;
lsh = Inlines.silk_LIMIT(lsh, -12 - QC, 30 - QC);
scale.Val = -(QC + lsh);
Inlines.OpusAssert(scale.Val >= -30 && scale.Val <= 12);
if (lsh >= 0)
{
for (i = 0; i < order + 1; i++)
{
corr[i] = (int)(Inlines.silk_LSHIFT64(corr_QC[i], lsh));
}
}
else {
for (i = 0; i < order + 1; i++)
{
corr[i] = (int)(Inlines.silk_RSHIFT64(corr_QC[i], -lsh));
}
}
Inlines.OpusAssert(corr_QC[0] >= 0); /* If breaking, decrease QC*/
}
}
}
#endif

View File

@@ -0,0 +1,221 @@
/* Copyright (c) 2016 Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Common.CPlusPlus
{
using System;
internal static class Arrays
{
internal static T[][] InitTwoDimensionalArray<T>(int x, int y)
{
T[][] returnVal = new T[x][];
for (int c = 0; c < x; c++)
{
returnVal[c] = new T[y];
}
return returnVal;
}
internal static Pointer<Pointer<T>> InitTwoDimensionalArrayPointer<T>(int x, int y)
{
Pointer<Pointer<T>> returnVal = Pointer.Malloc<Pointer<T>>(x);
for (int c = 0; c < x; c++)
{
returnVal[c] = Pointer.Malloc<T>(y);
}
return returnVal;
}
internal static T[][][] InitThreeDimensionalArray<T>(int x, int y, int z)
{
T[][][] returnVal = new T[x][][];
for (int c = 0; c < x; c++)
{
returnVal[c] = new T[y][];
for (int a = 0; a < y; a++)
{
returnVal[c][a] = new T[z];
}
}
return returnVal;
}
//FIXME: For the most part this method is used to zero-out arrays, which is usually already done by the runtime.
internal static void MemSetByte(byte[] array, byte value)
{
for (int c = 0; c < array.Length; c++)
{
array[c] = value;
}
}
internal static void MemSetInt(int[] array, int value, int length)
{
for (int c = 0; c < length; c++)
{
array[c] = value;
}
}
internal static void MemSetShort(short[] array, short value, int length)
{
for (int c = 0; c < length; c++)
{
array[c] = value;
}
}
internal static void MemSetFloat(float[] array, float value, int length)
{
for (int c = 0; c < length; c++)
{
array[c] = value;
}
}
internal static void MemSetSbyte(sbyte[] array, sbyte value, int length)
{
for (int c = 0; c < length; c++)
{
array[c] = value;
}
}
internal static void MemSetWithOffset<T>(T[] array, T value, int offset, int length)
{
for (int c = offset; c < offset + length; c++)
{
array[c] = value;
}
}
internal static void MemMove<T>(T[] array, int src_idx, int dst_idx, int length)
{
if (src_idx == dst_idx || length == 0)
return;
// Do regions overlap?
if (src_idx + length > dst_idx || dst_idx + length > src_idx)
{
// Take extra precautions
if (dst_idx < src_idx)
{
// Copy forwards
for (int c = 0; c < length; c++)
{
array[c + dst_idx] = array[c + src_idx];
}
}
else
{
// Copy backwards
for (int c = length - 1; c >= 0; c--)
{
array[c + dst_idx] = array[c + src_idx];
}
}
}
else
{
// Memory regions cannot overlap; just do a fast copy
Array.Copy(array, src_idx, array, dst_idx, length);
}
}
internal static void MemMoveInt(int[] array, int src_idx, int dst_idx, int length)
{
if (src_idx == dst_idx || length == 0)
return;
// Do regions overlap?
if (src_idx + length > dst_idx || dst_idx + length > src_idx)
{
// Take extra precautions
if (dst_idx < src_idx)
{
// Copy forwards
for (int c = 0; c < length; c++)
{
array[c + dst_idx] = array[c + src_idx];
}
}
else
{
// Copy backwards
for (int c = length - 1; c >= 0; c--)
{
array[c + dst_idx] = array[c + src_idx];
}
}
}
else
{
// Memory regions cannot overlap; just do a fast copy
Array.Copy(array, src_idx, array, dst_idx, length);
}
}
internal static void MemMoveShort(short[] array, int src_idx, int dst_idx, int length)
{
if (src_idx == dst_idx || length == 0)
return;
// Do regions overlap?
if (src_idx + length > dst_idx || dst_idx + length > src_idx)
{
// Take extra precautions
if (dst_idx < src_idx)
{
// Copy forwards
for (int c = 0; c < length; c++)
{
array[c + dst_idx] = array[c + src_idx];
}
}
else
{
// Copy backwards
for (int c = length - 1; c >= 0; c--)
{
array[c + dst_idx] = array[c + src_idx];
}
}
}
else
{
// Memory regions cannot overlap; just do a fast copy
Array.Copy(array, src_idx, array, dst_idx, length);
}
}
}
}

View File

@@ -0,0 +1,97 @@
/* Copyright (c) 2016 Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Common.CPlusPlus
{
public class BoxedValueInt
{
public int Val;
public BoxedValueInt(int v = 0)
{
Val = v;
}
public override string ToString()
{
return Val.ToString();
}
}
public class BoxedValueShort
{
public short Val;
public BoxedValueShort(short v = 0)
{
Val = v;
}
public override string ToString()
{
return Val.ToString();
}
}
public class BoxedValueSbyte
{
public sbyte Val;
public BoxedValueSbyte(sbyte v = 0)
{
Val = v;
}
public override string ToString()
{
return Val.ToString();
}
}
/// <summary>
/// For performance reasons, do not use this generic class if possible
/// </summary>
/// <typeparam name="T"></typeparam>
public class BoxedValue<T>
{
public T Val;
public BoxedValue(T v = default(T))
{
Val = v;
}
public override string ToString()
{
return Val == null ? "null" : Val.ToString();
}
}
}

View File

@@ -0,0 +1,563 @@
/* Copyright (c) 2016 Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Common.CPlusPlus
{
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
/// <summary>
/// This simulates a C++ style pointer as far as can be implemented in C#. It represents a handle
/// to an array of objects, along with a base offset that represents the address.
/// When you are programming in debug mode, this class also enforces memory boundaries,
/// tracks uninitialized values, and also records all statistics of accesses to its base array.
/// </summary>
/// <typeparam name="T"></typeparam>
public class Pointer<T>
{
private const bool CHECK_UNINIT_MEM = false;
#if DEBUG && !NET35
private class Statistics
{
public Statistics(int baseOffset)
{
this.baseOffset = baseOffset;
}
public int baseOffset;
public int minReadIndex = int.MaxValue;
public int maxReadIndex = int.MinValue;
public int minWriteIndex = int.MaxValue;
public int maxWriteIndex = int.MinValue;
public Tuple<int, int> ReadRange
{
get
{
if (minReadIndex == int.MaxValue || maxReadIndex == int.MinValue)
return null;
return new Tuple<int, int>(minReadIndex - baseOffset, maxReadIndex - baseOffset);
}
}
public Tuple<int, int> WriteRange
{
get
{
if (minWriteIndex == int.MaxValue || maxWriteIndex == int.MinValue)
return null;
return new Tuple<int, int>(minWriteIndex - baseOffset, maxWriteIndex - baseOffset);
}
}
}
private bool[] _initialized;
private Statistics _statistics;
private int _length;
#endif
private T[] _array;
private int _offset;
public Pointer(int capacity)
{
_array = new T[capacity];
_offset = 0;
#if DEBUG && !NET35
_length = capacity;
_statistics = new Statistics(0);
_initialized = new bool[capacity];
for (int c = 0; c < capacity; c++)
{
_initialized[c] = false;
}
#endif
}
public Pointer(T[] buffer)
{
_array = buffer;
_offset = 0;
#if DEBUG && !NET35
_length = buffer.Length;
_statistics = new Statistics(0);
_initialized = new bool[buffer.Length];
for (int c = 0; c < buffer.Length; c++)
{
_initialized[c] = true;
}
#endif
}
public Pointer(T[] buffer, int absoluteOffset)
{
_array = buffer;
_offset = absoluteOffset;
#if DEBUG && !NET35
_length = buffer.Length - absoluteOffset;
//Inlines.OpusAssert(_length >= 0, "Attempted to point past the end of an array");
_statistics = new Statistics(absoluteOffset);
_initialized = new bool[buffer.Length];
for (int c = 0; c < buffer.Length; c++)
{
_initialized[c] = true;
}
#endif
}
#if DEBUG && !NET35
private Pointer(T[] buffer, int absoluteOffset, Statistics statistics, bool[] initializedStatus)
{
_array = buffer;
_offset = absoluteOffset;
_length = buffer.Length - absoluteOffset;
//Inlines.OpusAssert(_length >= 0, "Attempted to point past the end of an array");
_statistics = statistics;
_initialized = initializedStatus;
}
public Tuple<int, int> ReadRange
{
get
{
return _statistics.ReadRange;
}
}
public Tuple<int, int> WriteRange
{
get
{
return _statistics.WriteRange;
}
}
public int Length
{
get
{
return _length;
}
}
#endif
public int Offset
{
get
{
return _offset;
}
}
// This should only be temporary while I migrate from pointers to arrays
public T[] Data
{
get
{
return _array;
}
}
public T this[int index]
{
get
{
#if DEBUG && !NET35
#pragma warning disable 162
if (CHECK_UNINIT_MEM) Inlines.OpusAssert(_initialized[index + _offset], "Attempted to read from uninitialized memory!");
#pragma warning restore 162
// Inlines.OpusAssert(index < _length, "Attempted to read past the end of an array!");
_statistics.maxReadIndex = Math.Max(_statistics.maxReadIndex, index + _offset);
_statistics.minReadIndex = Math.Min(_statistics.minReadIndex, index + _offset);
#endif
return _array[index + _offset];
}
set
{
#if DEBUG && !NET35
// Inlines.OpusAssert(index < _length, "Attempted to write past the end of an array!");
_statistics.maxWriteIndex = Math.Max(_statistics.maxWriteIndex, index + _offset);
_statistics.minWriteIndex = Math.Min(_statistics.minWriteIndex, index + _offset);
_initialized[index + _offset] = true;
#endif
_array[index + _offset] = value;
}
}
public T this[uint index]
{
get
{
return this[(int)index];
}
set
{
this[(int)index] = value;
}
}
/// <summary>
/// Returns the value currently under the pointer, and returns a new pointer with +1 offset.
/// This method is not very efficient because it creates new pointers; this is because we must preserve
/// the pass-by-value nature of C++ pointers when they are used as arguments to functions
/// </summary>
/// <returns></returns>
public Pointer<T> Iterate(out T returnVal)
{
returnVal = _array[_offset];
return Point(1);
}
#if DEBUG && !NET35
public Pointer<T> Point(int relativeOffset)
{
if (relativeOffset == 0) return this;
return new Pointer<T>(_array, _offset + relativeOffset, _statistics, _initialized);
}
public Pointer<T> Point(uint relativeOffset)
{
if (relativeOffset == 0) return this;
return new Pointer<T>(_array, _offset + (int)relativeOffset, _statistics, _initialized);
}
#else
public Pointer<T> Point(int relativeOffset)
{
if (relativeOffset == 0) return this;
return new Pointer<T>(_array, _offset + relativeOffset);
}
public Pointer<T> Point(uint relativeOffset)
{
if (relativeOffset == 0) return this;
return new Pointer<T>(_array, _offset + (int)relativeOffset);
}
#endif
private static string invert_endianness(string hexstring)
{
StringBuilder b = new StringBuilder(hexstring.Length);
for (int c = 0; c < hexstring.Length / 2; c++)
{
b.Append(hexstring.Substring(hexstring.Length - ((c + 1) * 2), 2));
}
return b.ToString();
}
private static void PrintMemCopy<E>(E[] source, int sourceOffset, int length)
{
if (typeof(E) == typeof(int) || typeof(E) == typeof(uint))
{
Debug.WriteLine(string.Format("memcpy of {0} bytes", length * 4));
string buf = string.Empty;
for (int c = 0; c < length; c++)
{
buf += invert_endianness(string.Format("{0:x8}", source[c + sourceOffset]));
}
Debug.WriteLine(buf);
}
else if (typeof(E) == typeof(short) || typeof(E) == typeof(ushort))
{
Debug.WriteLine(string.Format("memcpy of {0} bytes", length * 2));
string buf = string.Empty;
for (int c = 0; c < length; c++)
{
buf += invert_endianness(string.Format("{0:x4}", source[c + sourceOffset]));
}
Debug.WriteLine(buf);
}
else if (typeof(E) == typeof(byte) || typeof(E) == typeof(sbyte))
{
Debug.WriteLine(string.Format("memcpy of {0} bytes", length));
string buf = string.Empty;
for (int c = 0; c < length; c++)
{
buf += invert_endianness(string.Format("{0:x2}", source[c + sourceOffset]));
}
Debug.WriteLine(buf);
}
}
/// <summary>
/// Copies the contents of this pointer, starting at its current address, into the space of another pointer.
/// !!! IMPORTANT !!! REMEMBER THAT C++ memcpy is (DEST, SOURCE, LENGTH) !!!!
/// IN C# IT IS (SOURCE, DEST, LENGTH). DON'T GET SCOOPED LIKE I DID
/// </summary>
/// <param name="destination"></param>
/// <param name="length"></param>
#if DEBUG
public void MemCopyTo(Pointer<T> destination, int length, bool debug = false)
{
Inlines.OpusAssert(length >= 0, "Cannot memcopy() with a negative length!");
if (debug)
PrintMemCopy(_array, _offset, length);
for (int c = 0; c < length; c++)
{
destination[c] = _array[c + _offset];
}
}
#else
public void MemCopyTo(Pointer<T> destination, int length)
{
if (destination is Pointer<T>)
{
// Use the fast way if we have access to the base array
Array.Copy(_array, _offset, ((Pointer<T>)destination)._array, destination.Offset, length);
}
else
{
// Otherwise do it the slow way
for (int c = 0; c < length; c++)
{
destination[c] = _array[c + _offset];
}
}
}
#endif
/// <summary>
/// Copies the contents of this pointer, starting at its current address, into an array.
/// !!! IMPORTANT !!! REMEMBER THAT C++ memcpy is (DEST, SOURCE, LENGTH) !!!!
/// </summary>
/// <param name="destination"></param>
/// <param name="length"></param>
#if DEBUG
public void MemCopyTo(T[] destination, int destOffset, int length)
{
Inlines.OpusAssert(length >= 0, "Cannot memcopy() with a negative length!");
//PrintMemCopy(_array, _offset, length);
for (int c = 0; c < length; c++)
{
destination[c + destOffset] = _array[c + _offset];
}
}
#else
public void MemCopyTo(T[] destination, int offset, int length)
{
// Use the fast way if we have access to the base array
Array.Copy(_array, _offset, destination, offset, length);
}
#endif
/// <summary>
/// Loads N values from a source array into this pointer's space
/// </summary>
/// <param name="length"></param>
#if DEBUG && !NET35
public void MemCopyFrom(T[] source, int sourceOffset, int length)
{
Inlines.OpusAssert(length >= 0, "Cannot memcopy() with a negative length!");
//PrintMemCopy(source, sourceOffset, length);
for (int c = 0; c < length; c++)
{
_array[c + _offset] = source[c + sourceOffset];
_initialized[c + _offset] = true;
}
}
#else
public void MemCopyFrom(T[] source, int sourceOffset, int length)
{
Array.Copy(source, sourceOffset, _array, _offset, length);
}
#endif
/// <summary>
/// Assigns a certain value to a range of spaces in this array
/// </summary>
/// <param name="value">The value to set</param>
/// <param name="length">The number of values to write</param>
public void MemSet(T value, int length)
{
#if DEBUG
Inlines.OpusAssert(length >= 0, "Cannot memset() with a negative length!");
#endif
MemSet(value, (uint)length);
}
/// <summary>
/// Assigns a certain value to a range of spaces in this array
/// </summary>
/// <param name="value">The value to set</param>
/// <param name="length">The number of values to write</param>
public void MemSet(T value, uint length)
{
for (int c = _offset; c < _offset + length; c++)
{
_array[c] = value;
#if DEBUG && !NET35
_initialized[c] = true;
#endif
}
}
public void MemMoveTo(Pointer<T> other, int length)
{
if (_array == other._array)
{
// Pointers refer to the same array, perform a move
//if (debug)
// PrintMemCopy(_array, _offset, length);
MemMove(other.Offset - Offset, length);
}
else
{
// Pointers refer to different arrays (if you end up here you probably wanted to just to MemCopy())
// Debug.WriteLine("Unnecessary memmove detected");
MemCopyTo(other, length);
}
}
/// <summary>
/// Moves regions of memory within the bounds of this pointer's array.
/// Extra checks are done to ensure that the data is not corrupted if the copy
/// regions overlap
/// </summary>
/// <param name="move_dist">The offset to send this pointer's data to</param>
/// <param name="length">The number of values to copy</param>
#if DEBUG && !NET35
public void MemMove(int move_dist, int length)
{
Inlines.OpusAssert(length >= 0, "Cannot memmove() with a negative length!");
if (move_dist == 0 || length == 0)
return;
// Do regions overlap?
if ((move_dist > 0 && move_dist < length) || (move_dist < 0 && 0 - move_dist > length))
{
// Take extra precautions
if (move_dist < 0)
{
// Copy forwards
for (int c = 0; c < length; c++)
{
_array[c + _offset + move_dist] = _array[c + _offset];
_initialized[c + _offset + move_dist] = true;
}
}
else
{
// Copy backwards
for (int c = length - 1; c >= 0; c--)
{
_array[c + _offset + move_dist] = _array[c + _offset];
_initialized[c + _offset + move_dist] = true;
}
}
}
else
{
for (int c = 0; c < length; c++)
{
_array[c + _offset + move_dist] = _array[c + _offset];
_initialized[c + _offset + move_dist] = true;
}
}
}
#else
public void MemMove(int move_dist, int length)
{
Arrays.MemMove(_array, _offset, _offset + move_dist, length);
}
#endif
/*/// <summary>
/// Simulates pointer zooming: newPtr = &amp;ptr[offset].
/// Returns a pointer that is offset from this one within the same buffer.
/// </summary>
/// <param name="arg"></param>
/// <param name="offset"></param>
/// <returns></returns>
internal static Pointer<T> operator +(Pointer<T> arg, int offset)
{
return new Pointer<T>(arg._array, arg._offset + offset);
}*/
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
Pointer<T> other = (Pointer<T>)obj;
return other._offset == _offset &&
other._array == _array;
}
public override int GetHashCode()
{
return _array.GetHashCode() + _offset.GetHashCode();
}
}
/// <summary>
/// This is a helper class which contains static methods that involve pointers
/// </summary>
public static class Pointer
{
/// <summary>
/// Allocates a new array and returns a pointer to it
/// </summary>
/// <typeparam name="E"></typeparam>
/// <param name="capacity"></param>
/// <returns></returns>
public static Pointer<E> Malloc<E>(int capacity)
{
//this returns a pointer inside of a random field, to make sure offset indexing works properly
//E[] field = new E[capacity * 2];
//return new Pointer<E>(field, new Random().Next(0, capacity - 1));
return new Pointer<E>(capacity);
}
/// <summary>
/// Creates a pointer to an existing array
/// </summary>
/// <typeparam name="E"></typeparam>
/// <param name="memory"></param>
/// <param name="offset"></param>
/// <returns></returns>
public static Pointer<E> GetPointer<E>(this E[] memory, int offset = 0)
{
if (memory == null)
return null;
//if (Debugger.IsAttached && offset == memory.Length / 2)
//{
// // This may be a partitioned array. Signal the debugger
// Debugger.Break();
//}
return new Pointer<E>(memory, offset);
}
}
}

View File

@@ -0,0 +1,790 @@
/* Copyright (c) 2001-2011 Timothy B. Terriberry
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Common
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System.Diagnostics;
/*A range decoder.
This is an entropy decoder based upon \cite{Mar79}, which is itself a
rediscovery of the FIFO arithmetic code introduced by \cite{Pas76}.
It is very similar to arithmetic encoding, except that encoding is done with
digits in any base, instead of with bits, and so it is faster when using
larger bases (i.e.: a byte).
The author claims an average waste of $\frac{1}{2}\log_b(2b)$ bits, where $b$
is the base, longer than the theoretical optimum, but to my knowledge there
is no published justification for this claim.
This only seems true when using near-infinite precision arithmetic so that
the process is carried out with no rounding errors.
An excellent description of implementation details is available at
http://www.arturocampos.com/ac_range.html
A recent work \cite{MNW98} which proposes several changes to arithmetic
encoding for efficiency actually re-discovers many of the principles
behind range encoding, and presents a good theoretical analysis of them.
End of stream is handled by writing out the smallest number of bits that
ensures that the stream will be correctly decoded regardless of the value of
any subsequent bits.
ec_tell() can be used to determine how many bits were needed to decode
all the symbols thus far; other data can be packed in the remaining bits of
the input buffer.
@PHDTHESIS{Pas76,
author="Richard Clark Pasco",
title="Source coding algorithms for fast data compression",
school="Dept. of Electrical Engineering, Stanford University",
address="Stanford, CA",
month=May,
year=1976
}
@INPROCEEDINGS{Mar79,
author="Martin, G.N.N.",
title="Range encoding: an algorithm for removing redundancy from a digitised
message",
booktitle="Video & Data Recording Conference",
year=1979,
address="Southampton",
month=Jul
}
@ARTICLE{MNW98,
author="Alistair Moffat and Radford Neal and Ian H. Witten",
title="Arithmetic Coding Revisited",
journal="{ACM} Transactions on Information Systems",
year=1998,
volume=16,
number=3,
pages="256--294",
month=Jul,
URL="http://www.stanford.edu/class/ee398a/handouts/papers/Moffat98ArithmCoding.pdf"
}*/
internal class EntropyCoder
{
private const int EC_WINDOW_SIZE = ((int)sizeof(uint) * 8);
///*The number of bits to use for the range-coded part of uint integers.*/
private const int EC_UINT_BITS = 8;
///*The resolution of fractional-precision bit usage measurements, i.e.,
// 3 => 1/8th bits.*/
public const int BITRES = 3;
/*The number of bits to output at a time.*/
private const int EC_SYM_BITS = (8);
/*The total number of bits in each of the state registers.*/
private const int EC_CODE_BITS = (32);
/*The maximum symbol value.*/
private const uint EC_SYM_MAX = ((1U << EC_SYM_BITS) - 1);
/*Bits to shift by to move a symbol into the high-order position.*/
private const uint EC_CODE_SHIFT = (EC_CODE_BITS - EC_SYM_BITS - 1);
/*Carry bit of the high-order range symbol.*/
private const uint EC_CODE_TOP = ((1U) << (EC_CODE_BITS - 1));
/*Low-order bit of the high-order range symbol.*/
private const uint EC_CODE_BOT = (EC_CODE_TOP >> EC_SYM_BITS);
/*The number of bits available for the last, partial symbol in the code field.*/
private const int EC_CODE_EXTRA = ((EC_CODE_BITS - 2) % EC_SYM_BITS + 1);
//////////////// Coder State ////////////////////
/*POINTER to Buffered input/output.*/
public byte[] buf;
public int buf_ptr;
/*The size of the buffer.*/
public uint storage;
/*The offset at which the last byte containing raw bits was read/written.*/
public uint end_offs;
/*Bits that will be read from/written at the end.*/
public uint end_window;
/*Number of valid bits in end_window.*/
public int nend_bits;
/*The total number of whole bits read/written.
This does not include partial bits currently in the range coder.*/
public int nbits_total;
/*The offset at which the next range coder byte will be read/written.*/
public uint offs;
/*The number of values in the current range.*/
public uint rng;
/*In the decoder: the difference between the top of the current range and
the input value, minus one.
In the encoder: the low end of the current range.*/
public uint val;
/*In the decoder: the saved normalization factor from ec_decode().
In the encoder: the number of oustanding carry propagating symbols.*/
public uint ext;
/*A buffered input/output symbol, awaiting carry propagation.*/
public int rem;
/*Nonzero if an error occurred.*/
public int error;
public EntropyCoder()
{
Reset();
}
public void Reset()
{
buf = null;
buf_ptr = 0;
storage = 0;
end_offs = 0;
end_window = 0;
nend_bits = 0;
offs = 0;
rng = 0;
val = 0;
ext = 0;
rem = 0;
error = 0;
}
public void Assign(EntropyCoder other)
{
this.buf = other.buf;
this.buf_ptr = other.buf_ptr;
this.storage = other.storage;
this.end_offs = other.end_offs;
this.end_window = other.end_window;
this.nend_bits = other.nend_bits;
this.nbits_total = other.nbits_total;
this.offs = other.offs;
this.rng = other.rng;
this.val = other.val;
this.ext = other.ext;
this.rem = other.rem;
this.error = other.error;
}
internal int read_byte()
{
return this.offs < this.storage ? this.buf[buf_ptr + this.offs++] : 0;
}
internal int read_byte_from_end()
{
return this.end_offs < this.storage ?
this.buf[buf_ptr + (this.storage - ++(this.end_offs))] : 0;
}
internal int write_byte(uint _value)
{
if (this.offs + this.end_offs >= this.storage)
{
return -1;
}
this.buf[buf_ptr + this.offs++] = (byte)_value;
return 0;
}
internal int write_byte_at_end(uint _value)
{
if (this.offs + this.end_offs >= this.storage)
{
return -1;
}
this.buf[buf_ptr + (this.storage - ++(this.end_offs))] = (byte)_value;
return 0;
}
/// <summary>
/// Normalizes the contents of val and rng so that rng lies entirely in the high-order symbol.
/// </summary>
internal void dec_normalize()
{
/*If the range is too small, rescale it and input some bits.*/
while (this.rng <= EC_CODE_BOT)
{
int sym;
this.nbits_total += EC_SYM_BITS;
this.rng <<= EC_SYM_BITS;
/*Use up the remaining bits from our last symbol.*/
sym = this.rem;
/*Read the next value from the input.*/
this.rem = read_byte();
/*Take the rest of the bits we need from this new symbol.*/
sym = (sym << EC_SYM_BITS | this.rem) >> (EC_SYM_BITS - EC_CODE_EXTRA);
/*And subtract them from val, capped to be less than EC_CODE_TOP.*/
this.val = (uint)((this.val << EC_SYM_BITS) + (EC_SYM_MAX & ~sym)) & (EC_CODE_TOP - 1);
}
}
internal void dec_init(byte[] _buf, int _buf_ptr, uint _storage)
{
this.buf = _buf;
this.buf_ptr = _buf_ptr;
this.storage = _storage;
this.end_offs = 0;
this.end_window = 0;
this.nend_bits = 0;
/*This is the offset from which ec_tell() will subtract partial bits.
The final value after the ec_dec_normalize() call will be the same as in
the encoder, but we have to compensate for the bits that are added there.*/
this.nbits_total = EC_CODE_BITS + 1
- ((EC_CODE_BITS - EC_CODE_EXTRA) / EC_SYM_BITS) * EC_SYM_BITS;
this.offs = 0;
this.rng = 1U << EC_CODE_EXTRA;
this.rem = read_byte();
this.val = this.rng - 1 - (uint)(this.rem >> (EC_SYM_BITS - EC_CODE_EXTRA));
this.error = 0;
/*Normalize the interval.*/
dec_normalize();
}
internal uint decode(uint _ft)
{
uint s;
this.ext = this.rng / _ft;
s = (uint)(this.val / this.ext);
return _ft - Inlines.EC_MINI(s + 1, _ft);
}
internal uint decode_bin(uint _bits)
{
uint s;
this.ext = this.rng >> (int)_bits;
s = (uint)(this.val / this.ext);
return (1U << (int)_bits) - Inlines.EC_MINI(s + 1U, 1U << (int)_bits);
}
internal void dec_update(uint _fl, uint _fh, uint _ft)
{
uint s;
s = this.ext * (_ft - _fh);
this.val -= s;
this.rng = _fl > 0 ? this.ext * (_fh - _fl) : this.rng - s;
dec_normalize();
}
/// <summary>
/// The probability of having a "one" is 1/(1&lt;&lt;_logp).
/// </summary>
/// <param name="_logp"></param>
/// <returns></returns>
internal int dec_bit_logp(uint _logp)
{
uint r;
uint d;
uint s;
int ret;
r = this.rng;
d = this.val;
s = r >> (int)_logp;
ret = d < s ? 1 : 0;
if (ret == 0) this.val = d - s;
this.rng = ret != 0 ? s : r - s;
dec_normalize();
return ret;
}
internal int dec_icdf(byte[] _icdf, uint _ftb)
{
uint r;
uint d;
uint s;
uint t;
int ret;
s = this.rng;
d = this.val;
r = s >> (int)_ftb;
ret = -1;
do
{
t = s;
s = r * _icdf[++ret];
}
while (d < s);
this.val = d - s;
this.rng = t - s;
dec_normalize();
return ret;
}
internal int dec_icdf(byte[] _icdf, int _icdf_offset, uint _ftb)
{
uint r;
uint d;
uint s;
uint t;
int ret;
s = this.rng;
d = this.val;
r = s >> (int)_ftb;
ret = _icdf_offset - 1;
do
{
t = s;
s = r * _icdf[++ret];
}
while (d < s);
this.val = d - s;
this.rng = t - s;
dec_normalize();
return ret - _icdf_offset;
}
internal uint dec_uint(uint _ft)
{
uint ft;
uint s;
int ftb;
/*In order to optimize EC_ILOG(), it is undefined for the value 0.*/
Inlines.OpusAssert(_ft > 1);
_ft--;
ftb = Inlines.EC_ILOG(_ft);
if (ftb > EC_UINT_BITS)
{
uint t;
ftb -= EC_UINT_BITS;
ft = (uint)(_ft >> ftb) + 1;
s = decode(ft);
dec_update(s, s + 1, ft);
t = (uint)s << ftb | dec_bits((uint)ftb);
if (t <= _ft) return t;
this.error = 1;
return _ft;
}
else {
_ft++;
s = decode((uint)_ft);
dec_update(s, s + 1, (uint)_ft);
return s;
}
}
internal uint dec_bits(uint _bits)
{
uint window;
int available;
uint ret;
window = this.end_window;
available = this.nend_bits;
if ((uint)available < _bits)
{
do
{
window |= (uint)read_byte_from_end() << available;
available += EC_SYM_BITS;
}
while (available <= EC_WINDOW_SIZE - EC_SYM_BITS);
}
ret = (uint)window & (((uint)1 << (int)_bits) - 1U);
window = window >> (int)_bits;
available = available - (int)_bits;
this.end_window = window;
this.nend_bits = available;
this.nbits_total = this.nbits_total + (int)_bits;
return ret;
}
/// <summary>
/// Outputs a symbol, with a carry bit.
/// If there is a potential to propagate a carry over several symbols, they are
/// buffered until it can be determined whether or not an actual carry will
/// occur.
/// If the counter for the buffered symbols overflows, then the stream becomes
/// undecodable.
/// This gives a theoretical limit of a few billion symbols in a single packet on
/// 32-bit systems.
/// The alternative is to truncate the range in order to force a carry, but
/// requires similar carry tracking in the decoder, needlessly slowing it down.
/// </summary>
/// <param name="_c"></param>
internal void enc_carry_out(int _c)
{
if (_c != EC_SYM_MAX)
{
/*No further carry propagation possible, flush buffer.*/
int carry;
carry = _c >> EC_SYM_BITS;
/*Don't output a byte on the first write.
This compare should be taken care of by branch-prediction thereafter.*/
if (this.rem >= 0)
{
this.error |= write_byte((uint)(this.rem + carry));
}
if (this.ext > 0)
{
uint sym;
sym = (EC_SYM_MAX + (uint)carry) & EC_SYM_MAX;
do this.error |= write_byte(sym);
while (--(this.ext) > 0);
}
this.rem = (int)((uint)_c & EC_SYM_MAX);
}
else
{
this.ext++;
}
}
internal void enc_normalize()
{
/*If the range is too small, output some bits and rescale it.*/
while (this.rng <= EC_CODE_BOT)
{
enc_carry_out((int)(this.val >> (int)EC_CODE_SHIFT));
/*Move the next-to-high-order symbol into the high-order position.*/
this.val = (this.val << EC_SYM_BITS) & (EC_CODE_TOP - 1);
this.rng = this.rng << EC_SYM_BITS;
this.nbits_total += EC_SYM_BITS;
}
}
internal void enc_init(byte[] _buf, int buf_ptr, uint _size)
{
this.buf = _buf;
this.buf_ptr = buf_ptr;
this.end_offs = 0;
this.end_window = 0;
this.nend_bits = 0;
/*This is the offset from which ec_tell() will subtract partial bits.*/
this.nbits_total = EC_CODE_BITS + 1;
this.offs = 0;
this.rng = EC_CODE_TOP;
this.rem = -1;
this.val = 0;
this.ext = 0;
this.storage = _size;
this.error = 0;
}
internal void encode(uint _fl, uint _fh, uint _ft)
{
uint r;
r = this.rng / _ft;
if (_fl > 0)
{
this.val += this.rng - (r * (_ft - _fl));
this.rng = (r * (_fh - _fl));
}
else
{
this.rng -= (r * (_ft - _fh));
}
enc_normalize();
}
internal void encode_bin(uint _fl, uint _fh, uint _bits)
{
uint r;
r = this.rng >> (int)_bits;
if (_fl > 0)
{
this.val += this.rng - (r * ((1U << (int)_bits) - _fl));
this.rng = (r * (_fh - _fl));
}
else this.rng -= (r * ((1U << (int)_bits) - _fh));
enc_normalize();
}
/*The probability of having a "one" is 1/(1<<_logp).*/
internal void enc_bit_logp(int _val, uint _logp)
{
uint r;
uint s;
uint l;
r = this.rng;
l = this.val;
s = r >> (int)_logp;
r -= s;
if (_val != 0)
{
this.val = l + r;
}
this.rng = _val != 0 ? s : r;
enc_normalize();
}
internal void enc_icdf(int _s, byte[] _icdf, uint _ftb)
{
uint r;
r = this.rng >> (int)_ftb;
if (_s > 0)
{
this.val += this.rng - (r * _icdf[_s - 1]);
this.rng = (r * (uint)(_icdf[_s - 1] - _icdf[_s]));
}
else
{
this.rng -= (r * _icdf[_s]);
}
enc_normalize();
}
internal void enc_icdf(int _s, byte[] _icdf, int icdf_ptr, uint _ftb)
{
uint r;
r = this.rng >> (int)_ftb;
if (_s > 0)
{
this.val += this.rng - (r * _icdf[icdf_ptr + _s - 1]);
this.rng = (r * (uint)(_icdf[icdf_ptr + _s - 1] - _icdf[icdf_ptr + _s]));
}
else
{
this.rng -= (r * _icdf[icdf_ptr + _s]);
}
enc_normalize();
}
internal void enc_uint(uint _fl, uint _ft)
{
uint ft;
uint fl;
int ftb;
/*In order to optimize EC_ILOG(), it is undefined for the value 0.*/
Inlines.OpusAssert(_ft > 1);
_ft--;
ftb = Inlines.EC_ILOG(_ft);
if (ftb > EC_UINT_BITS)
{
ftb -= EC_UINT_BITS;
ft = (_ft >> ftb) + 1;
fl = (uint)(_fl >> ftb);
encode(fl, fl + 1, ft);
enc_bits(_fl & (((uint)1 << ftb) - 1U), (uint)ftb);
}
else encode(_fl, _fl + 1, _ft + 1);
}
internal void enc_bits(uint _fl, uint _bits)
{
uint window;
int used;
window = this.end_window;
used = this.nend_bits;
Inlines.OpusAssert(_bits > 0);
if (used + _bits > EC_WINDOW_SIZE)
{
do
{
this.error |= write_byte_at_end((uint)window & EC_SYM_MAX);
window >>= EC_SYM_BITS;
used -= EC_SYM_BITS;
}
while (used >= EC_SYM_BITS);
}
window |= (uint)_fl << used;
used += (int)_bits;
this.end_window = window;
this.nend_bits = used;
this.nbits_total += (int)_bits;
}
internal void enc_patch_initial_bits(uint _val, uint _nbits)
{
int shift;
uint mask;
Inlines.OpusAssert(_nbits <= EC_SYM_BITS);
shift = EC_SYM_BITS - (int)_nbits;
mask = ((1U << (int)_nbits) - 1) << shift;
if (this.offs > 0)
{
/*The first byte has been finalized.*/
this.buf[buf_ptr] = (byte)((this.buf[buf_ptr] & ~mask) | _val << shift);
}
else if (this.rem >= 0)
{
/*The first byte is still awaiting carry propagation.*/
this.rem = (int)(((uint)this.rem & ~mask) | _val) << shift;
}
else if (this.rng <= (EC_CODE_TOP >> (int)_nbits))
{
/*The renormalization loop has never been run.*/
this.val = (this.val & ~((uint)mask << (int)EC_CODE_SHIFT)) |
(uint)_val << (int)(EC_CODE_SHIFT + shift);
}
else
{
/*The encoder hasn't even encoded _nbits of data yet.*/
this.error = -1;
}
}
internal void enc_shrink(uint _size)
{
Inlines.OpusAssert(this.offs + this.end_offs <= _size);
//(memmove(this.buf + _size - this.end_offs, this.buf + this.storage - this.end_offs, this.end_offs * sizeof(*(dst))))
Arrays.MemMove<byte>(this.buf, buf_ptr + (int)_size - (int)this.end_offs, buf_ptr + (int)this.storage - (int)this.end_offs, (int)this.end_offs);
this.storage = _size;
}
internal uint range_bytes()
{
return this.offs;
}
internal int get_error()
{
return this.error;
}
/// <summary>
/// Returns the number of bits "used" by the encoded or decoded symbols so far.
/// This same number can be computed in either the encoder or the decoder, and is
/// suitable for making coding decisions.
/// This will always be slightly larger than the exact value (e.g., all
/// rounding error is in the positive direction).
/// </summary>
/// <returns>The number of bits.</returns>
internal int tell()
{
int returnVal = this.nbits_total - Inlines.EC_ILOG(this.rng);
return returnVal;
}
private static readonly uint[] correction = {35733, 38967, 42495, 46340, 50535, 55109, 60097, 65535};
/// <summary>
/// This is a faster version of ec_tell_frac() that takes advantage
/// of the low(1/8 bit) resolution to use just a linear function
/// followed by a lookup to determine the exact transition thresholds.
/// </summary>
/// <returns></returns>
internal uint tell_frac()
{
int nbits;
int r;
int l;
uint b;
nbits = this.nbits_total << EntropyCoder.BITRES;
l = Inlines.EC_ILOG(this.rng);
r = (int)(this.rng >> (l - 16));
b = (uint)((r >> 12) - 8);
b += (r > correction[b] ? 1u : 0);
l = (int)((l << 3) + b);
return (uint)(nbits - l);
}
internal void enc_done()
{
uint window;
int used;
uint msk;
uint end;
int l;
/*We output the minimum number of bits that ensures that the symbols encoded
thus far will be decoded correctly regardless of the bits that follow.*/
l = EC_CODE_BITS - Inlines.EC_ILOG(this.rng);
msk = (EC_CODE_TOP - 1) >> l;
end = (this.val + msk) & ~msk;
if ((end | msk) >= this.val + this.rng)
{
l++;
msk >>= 1;
end = (this.val + msk) & ~msk;
}
while (l > 0)
{
enc_carry_out((int)(end >> (int)EC_CODE_SHIFT));
end = (end << EC_SYM_BITS) & (EC_CODE_TOP - 1);
l -= EC_SYM_BITS;
}
/*If we have a buffered byte flush it into the output buffer.*/
if (this.rem >= 0 || this.ext > 0)
{
enc_carry_out(0);
}
/*If we have buffered extra bits, flush them as well.*/
window = this.end_window;
used = this.nend_bits;
while (used >= EC_SYM_BITS)
{
this.error |= write_byte_at_end((uint)window & EC_SYM_MAX);
window >>= EC_SYM_BITS;
used -= EC_SYM_BITS;
}
/*Clear any excess space and add any remaining extra bits to the last byte.*/
if (this.error == 0)
{
Arrays.MemSetWithOffset<byte>(this.buf, 0, buf_ptr + (int)this.offs, (int)this.storage - (int)this.offs - (int)this.end_offs);
if (used > 0)
{
/*If there's no range coder data at all, give up.*/
if (this.end_offs >= this.storage)
{
this.error = -1;
}
else
{
l = -l;
/*If we've busted, don't add too many extra bits to the last byte; it
would corrupt the range coder data, and that's more important.*/
if (this.offs + this.end_offs >= this.storage && l < used)
{
window = window & ((1U << l) - 1);
this.error = -1;
}
this.buf[buf_ptr + this.storage - this.end_offs - 1] |= (byte)window;
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{0E7FEE6A-15E5-4A4E-943C-80276003478C}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Concentus</RootNamespace>
<AssemblyName>Concentus</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="Celt\AutocorrelationUnsafe.cs" />
<Compile Include="Celt\Bands.cs" />
<Compile Include="Celt\CeltCommon.cs" />
<Compile Include="Celt\CeltConstants.cs" />
<Compile Include="Celt\CeltLPC.cs" />
<Compile Include="Celt\CeltLPCUnsafe.cs" />
<Compile Include="Celt\CeltPitchXCorr.cs" />
<Compile Include="Celt\CeltPitchXCorrUnsafe.cs" />
<Compile Include="Celt\CWRS.cs" />
<Compile Include="Celt\Enums\Spread.cs" />
<Compile Include="Celt\Kernels.cs" />
<Compile Include="Celt\KernelsUnsafe.cs" />
<Compile Include="Celt\KissFFT.cs" />
<Compile Include="Celt\KissFFTUnsafe.cs" />
<Compile Include="Celt\Laplace.cs" />
<Compile Include="Celt\MDCT.cs" />
<Compile Include="Celt\Pitch.cs" />
<Compile Include="Celt\QuantizeBands.cs" />
<Compile Include="Celt\Rate.cs" />
<Compile Include="Celt\Structs\AnalysisInfo.cs" />
<Compile Include="Celt\Structs\CELTDecoder.cs" />
<Compile Include="Celt\Structs\CeltEncoder.cs" />
<Compile Include="Celt\Structs\CELTMode.cs" />
<Compile Include="Celt\Structs\FFTState.cs" />
<Compile Include="Celt\Structs\MDCTLookup.cs" />
<Compile Include="Celt\Structs\PulseCache.cs" />
<Compile Include="Celt\Tables.cs" />
<Compile Include="Celt\VQ.cs" />
<Compile Include="Common\Autocorrelation.cs" />
<Compile Include="Common\CPlusPlus\Arrays.cs" />
<Compile Include="Common\CPlusPlus\BoxedValue.cs" />
<Compile Include="Common\CPlusPlus\Pointer.cs" />
<Compile Include="Common\EntropyCoder.cs" />
<Compile Include="Common\Inlines.cs" />
<Compile Include="Common\Resampler.cs" />
<Compile Include="Opus\Analysis.cs" />
<Compile Include="Opus\CodecHelpers.cs" />
<Compile Include="Opus\Downmix.cs" />
<Compile Include="Opus\Enums\OpusApplication.cs" />
<Compile Include="Opus\Enums\OpusBandwidth.cs" />
<Compile Include="Opus\Enums\OpusControl.cs" />
<Compile Include="Opus\Enums\OpusError.cs" />
<Compile Include="Opus\Enums\OpusFramesize.cs" />
<Compile Include="Opus\Enums\OpusMode.cs" />
<Compile Include="Opus\Enums\OpusSignal.cs" />
<Compile Include="Opus\MultiLayerPerceptron.cs" />
<Compile Include="Opus\OpusCompare.cs" />
<Compile Include="Opus\OpusConstants.cs" />
<Compile Include="Opus\OpusException.cs" />
<Compile Include="Opus\OpusMultistream.cs" />
<Compile Include="Opus\Structs\ChannelLayout.cs" />
<Compile Include="Opus\Structs\MLP.cs" />
<Compile Include="Opus\Structs\OpusDecoder.cs" />
<Compile Include="Opus\Structs\OpusEncoder.cs" />
<Compile Include="Opus\Structs\OpusMSDecoder.cs" />
<Compile Include="Opus\Structs\OpusMSEncoder.cs" />
<Compile Include="Opus\Structs\OpusPacketInfo.cs" />
<Compile Include="Opus\Structs\OpusRepacketizer.cs" />
<Compile Include="Opus\Structs\StereoWidthState.cs" />
<Compile Include="Opus\Structs\TonalityAnalysisState.cs" />
<Compile Include="Opus\Structs\VorbisLayout.cs" />
<Compile Include="Opus\Tables.cs" />
<Compile Include="Silk\ApplySineWindow.cs" />
<Compile Include="Silk\BurgModified.cs" />
<Compile Include="Silk\BurgModifiedUnsafe.cs" />
<Compile Include="Silk\BWExpander.cs" />
<Compile Include="Silk\CNG.cs" />
<Compile Include="Silk\CodeSigns.cs" />
<Compile Include="Silk\CorrelateMatrix.cs" />
<Compile Include="Silk\DecodeAPI.cs" />
<Compile Include="Silk\DecodeCore.cs" />
<Compile Include="Silk\DecodeIndices.cs" />
<Compile Include="Silk\DecodeParameters.cs" />
<Compile Include="Silk\DecodePitch.cs" />
<Compile Include="Silk\DecodePulses.cs" />
<Compile Include="Silk\EncodeAPI.cs" />
<Compile Include="Silk\EncodeIndices.cs" />
<Compile Include="Silk\EncodePulses.cs" />
<Compile Include="Silk\Enums\DecoderAPIFlag.cs" />
<Compile Include="Silk\Enums\SilkError.cs" />
<Compile Include="Silk\Filters.cs" />
<Compile Include="Silk\FindLPC.cs" />
<Compile Include="Silk\FindLTP.cs" />
<Compile Include="Silk\FindPitchLags.cs" />
<Compile Include="Silk\FindPredCoefs.cs" />
<Compile Include="Silk\GainQuantization.cs" />
<Compile Include="Silk\HPVariableCutoff.cs" />
<Compile Include="Silk\K2A.cs" />
<Compile Include="Silk\LinearAlgebra.cs" />
<Compile Include="Silk\LPCInversePredGain.cs" />
<Compile Include="Silk\LTPAnalysisFilter.cs" />
<Compile Include="Silk\LTPScaleControl.cs" />
<Compile Include="Silk\NLSF.cs" />
<Compile Include="Silk\NoiseShapeAnalysis.cs" />
<Compile Include="Silk\PitchAnalysisCore.cs" />
<Compile Include="Silk\PLC.cs" />
<Compile Include="Silk\ProcessGains.cs" />
<Compile Include="Silk\QuantizeLTPGains.cs" />
<Compile Include="Silk\RegularizeCorrelations.cs" />
<Compile Include="Silk\Resampler.cs" />
<Compile Include="Silk\ResidualEnergy.cs" />
<Compile Include="Silk\Schur.cs" />
<Compile Include="Silk\ShellCoder.cs" />
<Compile Include="Silk\Sigmoid.cs" />
<Compile Include="Silk\SilkConstants.cs" />
<Compile Include="Silk\Sort.cs" />
<Compile Include="Silk\Stereo.cs" />
<Compile Include="Silk\Structs\CNGState.cs" />
<Compile Include="Silk\Structs\DecControlState.cs" />
<Compile Include="Silk\Structs\EncControlState.cs" />
<Compile Include="Silk\Structs\NLSFCodebook.cs" />
<Compile Include="Silk\Structs\PLCStruct.cs" />
<Compile Include="Silk\Structs\SideInfoIndices.cs" />
<Compile Include="Silk\Structs\SilkChannelDecoder.cs" />
<Compile Include="Silk\Structs\SilkChannelEncoder.cs" />
<Compile Include="Silk\Structs\SilkDecoder.cs" />
<Compile Include="Silk\Structs\SilkDecoderControl.cs" />
<Compile Include="Silk\Structs\SilkEncoder.cs" />
<Compile Include="Silk\Structs\SilkEncoderControl.cs" />
<Compile Include="Silk\Structs\SilkLPState.cs" />
<Compile Include="Silk\Structs\SilkNSQState.cs" />
<Compile Include="Silk\Structs\SilkPrefilterState.cs" />
<Compile Include="Silk\Structs\SilkResamplerState.cs" />
<Compile Include="Silk\Structs\SilkShapeState.cs" />
<Compile Include="Silk\Structs\SilkVADState.cs" />
<Compile Include="Silk\Structs\StereoDecodeState.cs" />
<Compile Include="Silk\Structs\StereoEncodeState.cs" />
<Compile Include="Silk\Structs\TOCStruct.cs" />
<Compile Include="Silk\SumSqrShift.cs" />
<Compile Include="Silk\Tables.cs" />
<Compile Include="Silk\TuningParameters.cs" />
<Compile Include="Silk\VoiceActivityDetection.cs" />
<Compile Include="Silk\VQ_WMat_EC.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,590 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Celt;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus;
using Concentus.Structs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus
{
internal static class Analysis
{
private const double M_PI = 3.141592653;
private const float cA = 0.43157974f;
private const float cB = 0.67848403f;
private const float cC = 0.08595542f;
private const float cE = ((float)M_PI / 2);
private const int NB_TONAL_SKIP_BANDS = 9;
internal static float fast_atan2f(float y, float x)
{
float x2, y2;
/* Should avoid underflow on the values we'll get */
if (Inlines.ABS16(x) + Inlines.ABS16(y) < 1e-9f)
{
x *= 1e12f;
y *= 1e12f;
}
x2 = x * x;
y2 = y * y;
if (x2 < y2)
{
float den = (y2 + cB * x2) * (y2 + cC * x2);
if (den != 0)
return -x * y * (y2 + cA * x2) / den + (y < 0 ? -cE : cE);
else
return (y < 0 ? -cE : cE);
}
else {
float den = (x2 + cB * y2) * (x2 + cC * y2);
if (den != 0)
return x * y * (x2 + cA * y2) / den + (y < 0 ? -cE : cE) - (x * y < 0 ? -cE : cE);
else
return (y < 0 ? -cE : cE) - (x * y < 0 ? -cE : cE);
}
}
internal static void tonality_analysis_init(TonalityAnalysisState tonal)
{
tonal.Reset();
}
internal static void tonality_get_info(TonalityAnalysisState tonal, AnalysisInfo info_out, int len)
{
int pos;
int curr_lookahead;
float psum;
int i;
pos = tonal.read_pos;
curr_lookahead = tonal.write_pos - tonal.read_pos;
if (curr_lookahead < 0)
curr_lookahead += OpusConstants.DETECT_SIZE;
if (len > 480 && pos != tonal.write_pos)
{
pos++;
if (pos == OpusConstants.DETECT_SIZE)
pos = 0;
}
if (pos == tonal.write_pos)
pos--;
if (pos < 0)
pos = OpusConstants.DETECT_SIZE - 1;
info_out.Assign(tonal.info[pos]);
tonal.read_subframe += len / 120;
while (tonal.read_subframe >= 4)
{
tonal.read_subframe -= 4;
tonal.read_pos++;
}
if (tonal.read_pos >= OpusConstants.DETECT_SIZE)
tonal.read_pos -= OpusConstants.DETECT_SIZE;
/* Compensate for the delay in the features themselves.
FIXME: Need a better estimate the 10 I just made up */
curr_lookahead = Inlines.IMAX(curr_lookahead - 10, 0);
psum = 0;
/* Summing the probability of transition patterns that involve music at
time (DETECT_SIZE-curr_lookahead-1) */
for (i = 0; i < OpusConstants.DETECT_SIZE - curr_lookahead; i++)
psum += tonal.pmusic[i];
for (; i < OpusConstants.DETECT_SIZE; i++)
psum += tonal.pspeech[i];
psum = psum * tonal.music_confidence + (1 - psum) * tonal.speech_confidence;
/*printf("%f %f %f\n", psum, info_out.music_prob, info_out.tonality);*/
info_out.music_prob = psum;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T">The type of signal being handled (either short or float) - changes based on which API is used</typeparam>
/// <param name="tonal"></param>
/// <param name="celt_mode"></param>
/// <param name="x"></param>
/// <param name="len"></param>
/// <param name="offset"></param>
/// <param name="c1"></param>
/// <param name="c2"></param>
/// <param name="C"></param>
/// <param name="lsb_depth"></param>
/// <param name="downmix"></param>
internal static void tonality_analysis<T>(TonalityAnalysisState tonal, CeltMode celt_mode, T[] x, int x_ptr, int len, int offset, int c1, int c2, int C, int lsb_depth, Downmix.downmix_func<T> downmix)
{
int i, b;
FFTState kfft;
int[] input;
int[] output;
int N = 480, N2 = 240;
float[] A = tonal.angle;
float[] dA = tonal.d_angle;
float[] d2A = tonal.d2_angle;
float[] tonality;
float[] noisiness;
float[] band_tonality = new float[OpusConstants.NB_TBANDS];
float[] logE = new float[OpusConstants.NB_TBANDS];
float[] BFCC = new float[8];
float[] features = new float[25];
float frame_tonality;
float max_frame_tonality;
/*float tw_sum=0;*/
float frame_noisiness;
float pi4 = (float)(M_PI * M_PI * M_PI * M_PI);
float slope = 0;
float frame_stationarity;
float relativeE;
float[] frame_probs = new float[2];
float alpha, alphaE, alphaE2;
float frame_loudness;
float bandwidth_mask;
int bandwidth = 0;
float maxE = 0;
float noise_floor;
int remaining;
AnalysisInfo info; //[porting note] pointer
tonal.last_transition++;
alpha = 1.0f / Inlines.IMIN(20, 1 + tonal.count);
alphaE = 1.0f / Inlines.IMIN(50, 1 + tonal.count);
alphaE2 = 1.0f / Inlines.IMIN(1000, 1 + tonal.count);
if (tonal.count < 4)
tonal.music_prob = 0.5f;
kfft = celt_mode.mdct.kfft[0];
if (tonal.count == 0)
tonal.mem_fill = 240;
downmix(x, x_ptr, tonal.inmem, tonal.mem_fill, Inlines.IMIN(len, OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill), offset, c1, c2, C);
if (tonal.mem_fill + len < OpusConstants.ANALYSIS_BUF_SIZE)
{
tonal.mem_fill += len;
/* Don't have enough to update the analysis */
return;
}
info = tonal.info[tonal.write_pos++];
if (tonal.write_pos >= OpusConstants.DETECT_SIZE)
tonal.write_pos -= OpusConstants.DETECT_SIZE;
input = new int[960];
output = new int[960];
tonality = new float[240];
noisiness = new float[240];
for (i = 0; i < N2; i++)
{
float w = Tables.analysis_window[i];
input[2 * i] = (int)(w * tonal.inmem[i]);
input[2 * i + 1] = (int)(w * tonal.inmem[N2 + i]);
input[(2 * (N - i - 1))] = (int)(w * tonal.inmem[N - i - 1]);
input[(2 * (N - i - 1)) + 1] = (int)(w * tonal.inmem[N + N2 - i - 1]);
}
Arrays.MemMoveInt(tonal.inmem, OpusConstants.ANALYSIS_BUF_SIZE - 240, 0, 240);
remaining = len - (OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill);
downmix(x, x_ptr, tonal.inmem, 240, remaining, offset + OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill, c1, c2, C);
tonal.mem_fill = 240 + remaining;
KissFFT.opus_fft(kfft, input, output);
for (i = 1; i < N2; i++)
{
float X1r, X2r, X1i, X2i;
float angle, d_angle, d2_angle;
float angle2, d_angle2, d2_angle2;
float mod1, mod2, avg_mod;
X1r = (float)output[2 * i] + output[2 * (N - i)];
X1i = (float)output[(2 * i) + 1] - output[2 * (N - i) + 1];
X2r = (float)output[(2 * i) + 1] + output[2 * (N - i) + 1];
X2i = (float)output[2 * (N - i)] - output[2 * i];
angle = (float)(.5f / M_PI) * fast_atan2f(X1i, X1r);
d_angle = angle - A[i];
d2_angle = d_angle - dA[i];
angle2 = (float)(.5f / M_PI) * fast_atan2f(X2i, X2r);
d_angle2 = angle2 - angle;
d2_angle2 = d_angle2 - d_angle;
mod1 = d2_angle - (float)Math.Floor(0.5f + d2_angle);
noisiness[i] = Inlines.ABS16(mod1);
mod1 *= mod1;
mod1 *= mod1;
mod2 = d2_angle2 - (float)Math.Floor(0.5f + d2_angle2);
noisiness[i] += Inlines.ABS16(mod2);
mod2 *= mod2;
mod2 *= mod2;
avg_mod = .25f * (d2A[i] + 2.0f * mod1 + mod2);
tonality[i] = 1.0f / (1.0f + 40.0f * 16.0f * pi4 * avg_mod) - .015f;
A[i] = angle2;
dA[i] = d_angle2;
d2A[i] = mod2;
}
frame_tonality = 0;
max_frame_tonality = 0;
/*tw_sum = 0;*/
info.activity = 0;
frame_noisiness = 0;
frame_stationarity = 0;
if (tonal.count == 0)
{
for (b = 0; b < OpusConstants.NB_TBANDS; b++)
{
tonal.lowE[b] = 1e10f;
tonal.highE[b] = -1e10f;
}
}
relativeE = 0;
frame_loudness = 0;
for (b = 0; b < OpusConstants.NB_TBANDS; b++)
{
float E = 0, tE = 0, nE = 0;
float L1, L2;
float stationarity;
for (i = Tables.tbands[b]; i < Tables.tbands[b + 1]; i++)
{
float binE = output[2 * i] * (float)output[2 * i] + output[2 * (N - i)] * (float)output[2 * (N - i)]
+ output[2 * i + 1] * (float)output[2 * i + 1] + output[2 * (N - i) + 1] * (float)output[2 * (N - i) + 1];
/* FIXME: It's probably best to change the BFCC filter initial state instead */
binE *= 5.55e-17f;
E += binE;
tE += binE * tonality[i];
nE += binE * 2.0f * (.5f - noisiness[i]);
}
tonal.E[tonal.E_count][b] = E;
frame_noisiness += nE / (1e-15f + E);
frame_loudness += (float)Math.Sqrt(E + 1e-10f);
logE[b] = (float)Math.Log(E + 1e-10f);
tonal.lowE[b] = Inlines.MIN32(logE[b], tonal.lowE[b] + 0.01f);
tonal.highE[b] = Inlines.MAX32(logE[b], tonal.highE[b] - 0.1f);
if (tonal.highE[b] < tonal.lowE[b] + 1.0f)
{
tonal.highE[b] += 0.5f;
tonal.lowE[b] -= 0.5f;
}
relativeE += (logE[b] - tonal.lowE[b]) / (1e-15f + tonal.highE[b] - tonal.lowE[b]);
L1 = L2 = 0;
for (i = 0; i < OpusConstants.NB_FRAMES; i++)
{
L1 += (float)Math.Sqrt(tonal.E[i][b]);
L2 += tonal.E[i][b];
}
stationarity = Inlines.MIN16(0.99f, L1 / (float)Math.Sqrt(1e-15 + OpusConstants.NB_FRAMES * L2));
stationarity *= stationarity;
stationarity *= stationarity;
frame_stationarity += stationarity;
/*band_tonality[b] = tE/(1e-15+E)*/
band_tonality[b] = Inlines.MAX16(tE / (1e-15f + E), stationarity * tonal.prev_band_tonality[b]);
frame_tonality += band_tonality[b];
if (b >= OpusConstants.NB_TBANDS - OpusConstants.NB_TONAL_SKIP_BANDS)
frame_tonality -= band_tonality[b - OpusConstants.NB_TBANDS + OpusConstants.NB_TONAL_SKIP_BANDS];
max_frame_tonality = Inlines.MAX16(max_frame_tonality, (1.0f + .03f * (b - OpusConstants.NB_TBANDS)) * frame_tonality);
slope += band_tonality[b] * (b - 8);
tonal.prev_band_tonality[b] = band_tonality[b];
}
bandwidth_mask = 0;
bandwidth = 0;
maxE = 0;
noise_floor = 5.7e-4f / (1 << (Inlines.IMAX(0, lsb_depth - 8)));
noise_floor *= 1 << (15 + CeltConstants.SIG_SHIFT);
noise_floor *= noise_floor;
for (b = 0; b < OpusConstants.NB_TOT_BANDS; b++)
{
float E = 0;
int band_start, band_end;
/* Keep a margin of 300 Hz for aliasing */
band_start = Tables.extra_bands[b];
band_end = Tables.extra_bands[b + 1];
for (i = band_start; i < band_end; i++)
{
float binE = output[2 * i] * (float)output[2 * i] + output[2 * (N - i)] * (float)output[2 * (N - i)]
+ output[2 * i + 1] * (float)output[2 * i + 1] + output[2 * (N - i) + 1] * (float)output[2 * (N - i) + 1];
E += binE;
}
maxE = Inlines.MAX32(maxE, E);
tonal.meanE[b] = Inlines.MAX32((1 - alphaE2) * tonal.meanE[b], E);
E = Inlines.MAX32(E, tonal.meanE[b]);
/* Use a simple follower with 13 dB/Bark slope for spreading function */
bandwidth_mask = Inlines.MAX32(.05f * bandwidth_mask, E);
/* Consider the band "active" only if all these conditions are met:
1) less than 10 dB below the simple follower
2) less than 90 dB below the peak band (maximal masking possible considering
both the ATH and the loudness-dependent slope of the spreading function)
3) above the PCM quantization noise floor
*/
if (E > .1 * bandwidth_mask && E * 1e9f > maxE && E > noise_floor * (band_end - band_start))
bandwidth = b;
}
if (tonal.count <= 2)
bandwidth = 20;
frame_loudness = 20 * (float)Math.Log10(frame_loudness);
tonal.Etracker = Inlines.MAX32(tonal.Etracker - .03f, frame_loudness);
tonal.lowECount *= (1 - alphaE);
if (frame_loudness < tonal.Etracker - 30)
tonal.lowECount += alphaE;
for (i = 0; i < 8; i++)
{
float sum = 0;
for (b = 0; b < 16; b++)
sum += Tables.dct_table[i * 16 + b] * logE[b];
BFCC[i] = sum;
}
frame_stationarity /= OpusConstants.NB_TBANDS;
relativeE /= OpusConstants.NB_TBANDS;
if (tonal.count < 10)
relativeE = 0.5f;
frame_noisiness /= OpusConstants.NB_TBANDS;
info.activity = frame_noisiness + (1 - frame_noisiness) * relativeE;
frame_tonality = (max_frame_tonality / (OpusConstants.NB_TBANDS - OpusConstants.NB_TONAL_SKIP_BANDS));
frame_tonality = Inlines.MAX16(frame_tonality, tonal.prev_tonality * .8f);
tonal.prev_tonality = frame_tonality;
slope /= 8 * 8;
info.tonality_slope = slope;
tonal.E_count = (tonal.E_count + 1) % OpusConstants.NB_FRAMES;
tonal.count++;
info.tonality = frame_tonality;
for (i = 0; i < 4; i++)
features[i] = -0.12299f * (BFCC[i] + tonal.mem[i + 24]) + 0.49195f * (tonal.mem[i] + tonal.mem[i + 16]) + 0.69693f * tonal.mem[i + 8] - 1.4349f * tonal.cmean[i];
for (i = 0; i < 4; i++)
tonal.cmean[i] = (1 - alpha) * tonal.cmean[i] + alpha * BFCC[i];
for (i = 0; i < 4; i++)
features[4 + i] = 0.63246f * (BFCC[i] - tonal.mem[i + 24]) + 0.31623f * (tonal.mem[i] - tonal.mem[i + 16]);
for (i = 0; i < 3; i++)
features[8 + i] = 0.53452f * (BFCC[i] + tonal.mem[i + 24]) - 0.26726f * (tonal.mem[i] + tonal.mem[i + 16]) - 0.53452f * tonal.mem[i + 8];
if (tonal.count > 5)
{
for (i = 0; i < 9; i++)
tonal.std[i] = (1 - alpha) * tonal.std[i] + alpha * features[i] * features[i];
}
for (i = 0; i < 8; i++)
{
tonal.mem[i + 24] = tonal.mem[i + 16];
tonal.mem[i + 16] = tonal.mem[i + 8];
tonal.mem[i + 8] = tonal.mem[i];
tonal.mem[i] = BFCC[i];
}
for (i = 0; i < 9; i++)
features[11 + i] = (float)Math.Sqrt(tonal.std[i]);
features[20] = info.tonality;
features[21] = info.activity;
features[22] = frame_stationarity;
features[23] = info.tonality_slope;
features[24] = tonal.lowECount;
mlp.mlp_process(Tables.net, features, frame_probs);
frame_probs[0] = .5f * (frame_probs[0] + 1);
/* Curve fitting between the MLP probability and the actual probability */
frame_probs[0] = .01f + 1.21f * frame_probs[0] * frame_probs[0] - .23f * (float)Math.Pow(frame_probs[0], 10);
/* Probability of active audio (as opposed to silence) */
frame_probs[1] = .5f * frame_probs[1] + .5f;
/* Consider that silence has a 50-50 probability. */
frame_probs[0] = frame_probs[1] * frame_probs[0] + (1 - frame_probs[1]) * .5f;
/*printf("%f %f ", frame_probs[0], frame_probs[1]);*/
{
/* Probability of state transition */
float tau;
/* Represents independence of the MLP probabilities, where
beta=1 means fully independent. */
float beta;
/* Denormalized probability of speech (p0) and music (p1) after update */
float p0, p1;
/* Probabilities for "all speech" and "all music" */
float s0, m0;
/* Probability sum for renormalisation */
float psum;
/* Instantaneous probability of speech and music, with beta pre-applied. */
float speech0;
float music0;
/* One transition every 3 minutes of active audio */
tau = .00005f * frame_probs[1];
beta = .05f;
//if (1)
{
/* Adapt beta based on how "unexpected" the new prob is */
float p, q;
p = Inlines.MAX16(.05f, Inlines.MIN16(.95f, frame_probs[0]));
q = Inlines.MAX16(.05f, Inlines.MIN16(.95f, tonal.music_prob));
beta = .01f + .05f * Inlines.ABS16(p - q) / (p * (1 - q) + q * (1 - p));
}
/* p0 and p1 are the probabilities of speech and music at this frame
using only information from previous frame and applying the
state transition model */
p0 = (1 - tonal.music_prob) * (1 - tau) + tonal.music_prob * tau;
p1 = tonal.music_prob * (1 - tau) + (1 - tonal.music_prob) * tau;
/* We apply the current probability with exponent beta to work around
the fact that the probability estimates aren't independent. */
p0 *= (float)Math.Pow(1 - frame_probs[0], beta);
p1 *= (float)Math.Pow(frame_probs[0], beta);
/* Normalise the probabilities to get the Marokv probability of music. */
tonal.music_prob = p1 / (p0 + p1);
info.music_prob = tonal.music_prob;
/* This chunk of code deals with delayed decision. */
psum = 1e-20f;
/* Instantaneous probability of speech and music, with beta pre-applied. */
speech0 = (float)Math.Pow(1 - frame_probs[0], beta);
music0 = (float)Math.Pow(frame_probs[0], beta);
if (tonal.count == 1)
{
tonal.pspeech[0] = 0.5f;
tonal.pmusic[0] = 0.5f;
}
/* Updated probability of having only speech (s0) or only music (m0),
before considering the new observation. */
s0 = tonal.pspeech[0] + tonal.pspeech[1];
m0 = tonal.pmusic[0] + tonal.pmusic[1];
/* Updates s0 and m0 with instantaneous probability. */
tonal.pspeech[0] = s0 * (1 - tau) * speech0;
tonal.pmusic[0] = m0 * (1 - tau) * music0;
/* Propagate the transition probabilities */
for (i = 1; i < OpusConstants.DETECT_SIZE - 1; i++)
{
tonal.pspeech[i] = tonal.pspeech[i + 1] * speech0;
tonal.pmusic[i] = tonal.pmusic[i + 1] * music0;
}
/* Probability that the latest frame is speech, when all the previous ones were music. */
tonal.pspeech[OpusConstants.DETECT_SIZE - 1] = m0 * tau * speech0;
/* Probability that the latest frame is music, when all the previous ones were speech. */
tonal.pmusic[OpusConstants.DETECT_SIZE - 1] = s0 * tau * music0;
/* Renormalise probabilities to 1 */
for (i = 0; i < OpusConstants.DETECT_SIZE; i++)
psum += tonal.pspeech[i] + tonal.pmusic[i];
psum = 1.0f / psum;
for (i = 0; i < OpusConstants.DETECT_SIZE; i++)
{
tonal.pspeech[i] *= psum;
tonal.pmusic[i] *= psum;
}
psum = tonal.pmusic[0];
for (i = 1; i < OpusConstants.DETECT_SIZE; i++)
psum += tonal.pspeech[i];
/* Estimate our confidence in the speech/music decisions */
if (frame_probs[1] > .75)
{
if (tonal.music_prob > .9)
{
float adapt;
adapt = 1.0f / (++tonal.music_confidence_count);
tonal.music_confidence_count = Inlines.IMIN(tonal.music_confidence_count, 500);
tonal.music_confidence += adapt * Inlines.MAX16(-.2f, frame_probs[0] - tonal.music_confidence);
}
if (tonal.music_prob < .1)
{
float adapt;
adapt = 1.0f / (++tonal.speech_confidence_count);
tonal.speech_confidence_count = Inlines.IMIN(tonal.speech_confidence_count, 500);
tonal.speech_confidence += adapt * Inlines.MIN16(.2f, frame_probs[0] - tonal.speech_confidence);
}
}
else {
if (tonal.music_confidence_count == 0)
tonal.music_confidence = .9f;
if (tonal.speech_confidence_count == 0)
tonal.speech_confidence = .1f;
}
}
if (tonal.last_music != ((tonal.music_prob > .5f) ? 1 : 0))
tonal.last_transition = 0;
tonal.last_music = (tonal.music_prob > .5f) ? 1 : 0;
info.bandwidth = bandwidth;
info.noisiness = frame_noisiness;
info.valid = 1;
}
internal static void run_analysis<T>(TonalityAnalysisState analysis, CeltMode celt_mode, T[] analysis_pcm, int analysis_pcm_ptr,
int analysis_frame_size, int frame_size, int c1, int c2, int C, int Fs,
int lsb_depth, Downmix.downmix_func<T> downmix, AnalysisInfo analysis_info)
{
int offset;
int pcm_len;
if (analysis_pcm != null)
{
/* Avoid overflow/wrap-around of the analysis buffer */
analysis_frame_size = Inlines.IMIN((OpusConstants.DETECT_SIZE - 5) * Fs / 100, analysis_frame_size);
pcm_len = analysis_frame_size - analysis.analysis_offset;
offset = analysis.analysis_offset;
do
{
tonality_analysis(analysis, celt_mode, analysis_pcm, analysis_pcm_ptr, Inlines.IMIN(480, pcm_len), offset, c1, c2, C, lsb_depth, downmix);
offset += 480;
pcm_len -= 480;
} while (pcm_len > 0);
analysis.analysis_offset = analysis_frame_size;
analysis.analysis_offset -= frame_size;
}
analysis_info.valid = 0;
tonality_get_info(analysis, analysis_info, frame_size);
}
}
}

View File

@@ -0,0 +1,712 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Celt;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus;
using Concentus.Enums;
using Concentus.Silk;
using Concentus.Silk.Structs;
using Concentus.Structs;
using System;
namespace Concentus
{
public static class CodecHelpers
{
internal static byte gen_toc(OpusMode mode, int framerate, OpusBandwidth bandwidth, int channels)
{
int period;
byte toc;
period = 0;
while (framerate < 400)
{
framerate <<= 1;
period++;
}
if (mode == OpusMode.MODE_SILK_ONLY)
{
toc = (byte)((bandwidth - OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) << 5);
toc |= (byte)((period - 2) << 3);
}
else if (mode == OpusMode.MODE_CELT_ONLY)
{
int tmp = bandwidth - OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND;
if (tmp < 0)
tmp = 0;
toc = 0x80;
toc |= (byte)(tmp << 5);
toc |= (byte)(period << 3);
}
else /* Hybrid */
{
toc = 0x60;
toc |= (byte)((bandwidth - OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) << 4);
toc |= (byte)((period - 2) << 3);
}
toc |= (byte)((channels == 2 ? 1 : 0) << 2);
return toc;
}
internal static void hp_cutoff(short[] input, int input_ptr, int cutoff_Hz, short[] output, int output_ptr, int[] hp_mem, int len, int channels, int Fs)
{
int[] B_Q28 = new int[3];
int[] A_Q28 = new int[2];
int Fc_Q19, r_Q28, r_Q22;
Inlines.OpusAssert(cutoff_Hz <= int.MaxValue / ((int)((1.5f * 3.14159f / 1000) * ((long)1 << (19)) + 0.5))/*Inlines.SILK_CONST(1.5f * 3.14159f / 1000, 19)*/);
Fc_Q19 = Inlines.silk_DIV32_16(Inlines.silk_SMULBB(((int)((1.5f * 3.14159f / 1000) * ((long)1 << (19)) + 0.5))/*Inlines.SILK_CONST(1.5f * 3.14159f / 1000, 19)*/, cutoff_Hz), Fs / 1000);
Inlines.OpusAssert(Fc_Q19 > 0 && Fc_Q19 < 32768);
r_Q28 = ((int)((1.0f) * ((long)1 << (28)) + 0.5))/*Inlines.SILK_CONST(1.0f, 28)*/ - Inlines.silk_MUL(((int)((0.92f) * ((long)1 << (9)) + 0.5))/*Inlines.SILK_CONST(0.92f, 9)*/, Fc_Q19);
/* b = r * [ 1; -2; 1 ]; */
/* a = [ 1; -2 * r * ( 1 - 0.5 * Fc^2 ); r^2 ]; */
B_Q28[0] = r_Q28;
B_Q28[1] = Inlines.silk_LSHIFT(-r_Q28, 1);
B_Q28[2] = r_Q28;
/* -r * ( 2 - Fc * Fc ); */
r_Q22 = Inlines.silk_RSHIFT(r_Q28, 6);
A_Q28[0] = Inlines.silk_SMULWW(r_Q22, Inlines.silk_SMULWW(Fc_Q19, Fc_Q19) - ((int)((2.0f) * ((long)1 << (22)) + 0.5))/*Inlines.SILK_CONST(2.0f, 22)*/);
A_Q28[1] = Inlines.silk_SMULWW(r_Q22, r_Q22);
Filters.silk_biquad_alt(input, input_ptr, B_Q28, A_Q28, hp_mem, 0, output, output_ptr, len, channels);
if (channels == 2)
{
Filters.silk_biquad_alt(input, input_ptr + 1, B_Q28, A_Q28, hp_mem, 2, output, output_ptr + 1, len, channels);
}
}
internal static void dc_reject(short[] input, int input_ptr, int cutoff_Hz, short[] output, int output_ptr, int[] hp_mem, int len, int channels, int Fs)
{
int c, i;
int shift;
/* Approximates -round(log2(4.*cutoff_Hz/Fs)) */
shift = Inlines.celt_ilog2(Fs / (cutoff_Hz * 3));
for (c = 0; c < channels; c++)
{
for (i = 0; i < len; i++)
{
int x, tmp, y;
x = Inlines.SHL32(Inlines.EXTEND32(input[channels * i + c + input_ptr]), 15);
/* First stage */
tmp = x - hp_mem[2 * c];
hp_mem[2 * c] = hp_mem[2 * c] + Inlines.PSHR32(x - hp_mem[2 * c], shift);
/* Second stage */
y = tmp - hp_mem[2 * c + 1];
hp_mem[2 * c + 1] = hp_mem[2 * c + 1] + Inlines.PSHR32(tmp - hp_mem[2 * c + 1], shift);
output[channels * i + c + output_ptr] = Inlines.EXTRACT16(Inlines.SATURATE(Inlines.PSHR32(y, 15), 32767));
}
}
}
internal static void stereo_fade(
short[] pcm_buf,
int g1,
int g2,
int overlap48,
int frame_size,
int channels,
int[] window,
int Fs)
{
int i;
int overlap;
int inc;
inc = 48000 / Fs;
overlap = overlap48 / inc;
g1 = CeltConstants.Q15ONE - g1;
g2 = CeltConstants.Q15ONE - g2;
for (i = 0; i < overlap; i++)
{
int diff;
int g, w;
w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]);
g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2),
CeltConstants.Q15ONE - w, g1), 15);
diff = Inlines.EXTRACT16(Inlines.HALF32((int)pcm_buf[i * channels] - (int)pcm_buf[i * channels + 1]));
diff = Inlines.MULT16_16_Q15(g, diff);
pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - diff);
pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + diff);
}
for (; i < frame_size; i++)
{
int diff;
diff = Inlines.EXTRACT16(Inlines.HALF32((int)pcm_buf[i * channels] - (int)pcm_buf[i * channels + 1]));
diff = Inlines.MULT16_16_Q15(g2, diff);
pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - diff);
pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + diff);
}
}
internal static void gain_fade(short[] buffer, int buf_ptr, int g1, int g2,
int overlap48, int frame_size, int channels, int[] window, int Fs)
{
int i;
int inc;
int overlap;
int c;
inc = 48000 / Fs;
overlap = overlap48 / inc;
if (channels == 1)
{
for (i = 0; i < overlap; i++)
{
int g, w;
w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]);
g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2),
CeltConstants.Q15ONE - w, g1), 15);
buffer[buf_ptr + i] = (short)Inlines.MULT16_16_Q15(g, buffer[buf_ptr + i]);
}
}
else {
for (i = 0; i < overlap; i++)
{
int g, w;
w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]);
g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2),
CeltConstants.Q15ONE - w, g1), 15);
buffer[buf_ptr + i * 2] = (short)Inlines.MULT16_16_Q15(g, buffer[buf_ptr + i * 2]);
buffer[buf_ptr + i * 2 + 1] = (short)Inlines.MULT16_16_Q15(g, buffer[buf_ptr + i * 2 + 1]);
}
}
c = 0; do
{
for (i = overlap; i < frame_size; i++)
{
buffer[buf_ptr + i * channels + c] = (short)Inlines.MULT16_16_Q15(g2, buffer[buf_ptr + i * channels + c]);
}
}
while (++c < channels);
}
/* Don't use more than 60 ms for the frame size analysis */
private const int MAX_DYNAMIC_FRAMESIZE = 24;
/* Estimates how much the bitrate will be boosted based on the sub-frame energy */
internal static float transient_boost(float[] E, int E_ptr, float[] E_1, int LM, int maxM)
{
int i;
int M;
float sumE = 0, sumE_1 = 0;
float metric;
M = Inlines.IMIN(maxM, (1 << LM) + 1);
for (i = E_ptr; i < M + E_ptr; i++)
{
sumE += E[i];
sumE_1 += E_1[i];
}
metric = sumE * sumE_1 / (M * M);
/*if (LM==3)
printf("%f\n", metric);*/
/*return metric>10 ? 1 : 0;*/
/*return Inlines.MAX16(0,1-exp(-.25*(metric-2.)));*/
return Inlines.MIN16(1, (float)Math.Sqrt(Inlines.MAX16(0, .05f * (metric - 2))));
}
/* Viterbi decoding trying to find the best frame size combination using look-ahead
State numbering:
0: unused
1: 2.5 ms
2: 5 ms (#1)
3: 5 ms (#2)
4: 10 ms (#1)
5: 10 ms (#2)
6: 10 ms (#3)
7: 10 ms (#4)
8: 20 ms (#1)
9: 20 ms (#2)
10: 20 ms (#3)
11: 20 ms (#4)
12: 20 ms (#5)
13: 20 ms (#6)
14: 20 ms (#7)
15: 20 ms (#8)
*/
internal static int transient_viterbi(float[] E, float[] E_1, int N, int frame_cost, int rate)
{
int i;
float[][] cost = Arrays.InitTwoDimensionalArray<float>(MAX_DYNAMIC_FRAMESIZE, 16);
int[][] states = Arrays.InitTwoDimensionalArray<int>(MAX_DYNAMIC_FRAMESIZE, 16);
float best_cost;
int best_state;
float factor;
/* Take into account that we damp VBR in the 32 kb/s to 64 kb/s range. */
if (rate < 80)
factor = 0;
else if (rate > 160)
factor = 1;
else
factor = (rate - 80.0f) / 80.0f;
/* Makes variable framesize less aggressive at lower bitrates, but I can't
find any valid theoretical justification for this (other than it seems
to help) */
for (i = 0; i < 16; i++)
{
/* Impossible state */
states[0][i] = -1;
cost[0][i] = 1e10f;
}
for (i = 0; i < 4; i++)
{
cost[0][1 << i] = (frame_cost + rate * (1 << i)) * (1 + factor * transient_boost(E, 0, E_1, i, N + 1));
states[0][1 << i] = i;
}
for (i = 1; i < N; i++)
{
int j;
/* Follow continuations */
for (j = 2; j < 16; j++)
{
cost[i][j] = cost[i - 1][j - 1];
states[i][j] = j - 1;
}
/* New frames */
for (j = 0; j < 4; j++)
{
int k;
float min_cost;
float curr_cost;
states[i][1 << j] = 1;
min_cost = cost[i - 1][1];
for (k = 1; k < 4; k++)
{
float tmp = cost[i - 1][(1 << (k + 1)) - 1];
if (tmp < min_cost)
{
states[i][1 << j] = (1 << (k + 1)) - 1;
min_cost = tmp;
}
}
curr_cost = (frame_cost + rate * (1 << j)) * (1 + factor * transient_boost(E, i, E_1, j, N - i + 1));
cost[i][1 << j] = min_cost;
/* If part of the frame is outside the analysis window, only count part of the cost */
if (N - i < (1 << j))
cost[i][1 << j] += curr_cost * (float)(N - i) / (1 << j);
else
cost[i][1 << j] += curr_cost;
}
}
best_state = 1;
best_cost = cost[N - 1][1];
/* Find best end state (doesn't force a frame to end at N-1) */
for (i = 2; i < 16; i++)
{
if (cost[N - 1][i] < best_cost)
{
best_cost = cost[N - 1][i];
best_state = i;
}
}
/* Follow transitions back */
for (i = N - 1; i >= 0; i--)
{
/*printf("%d ", best_state);*/
best_state = states[i][best_state];
}
/*printf("%d\n", best_state);*/
return best_state;
}
internal static int optimize_framesize<T>(T[] x, int x_ptr, int len, int C, int Fs,
int bitrate, int tonality, float[] mem, int buffering,
Downmix.downmix_func<T> downmix)
{
int N;
int i;
float[] e = new float[MAX_DYNAMIC_FRAMESIZE + 4];
float[] e_1 = new float[MAX_DYNAMIC_FRAMESIZE + 3];
int memx;
int bestLM = 0;
int subframe;
int pos;
int offset;
int[] sub;
subframe = Fs / 400;
sub = new int[subframe];
e[0] = mem[0];
e_1[0] = 1.0f / (CeltConstants.EPSILON + mem[0]);
if (buffering != 0)
{
/* Consider the CELT delay when not in restricted-lowdelay */
/* We assume the buffering is between 2.5 and 5 ms */
offset = 2 * subframe - buffering;
Inlines.OpusAssert(offset >= 0 && offset <= subframe);
len -= offset;
e[1] = mem[1];
e_1[1] = 1.0f / (CeltConstants.EPSILON + mem[1]);
e[2] = mem[2];
e_1[2] = 1.0f / (CeltConstants.EPSILON + mem[2]);
pos = 3;
}
else {
pos = 1;
offset = 0;
}
N = Inlines.IMIN(len / subframe, MAX_DYNAMIC_FRAMESIZE);
/* Just silencing a warning, it's really initialized later */
memx = 0;
for (i = 0; i < N; i++)
{
float tmp;
int tmpx;
int j;
tmp = CeltConstants.EPSILON;
downmix(x, x_ptr, sub, 0, subframe, i * subframe + offset, 0, -2, C);
if (i == 0)
memx = sub[0];
for (j = 0; j < subframe; j++)
{
tmpx = sub[j];
tmp += (tmpx - memx) * (float)(tmpx - memx);
memx = tmpx;
}
e[i + pos] = tmp;
e_1[i + pos] = 1.0f / tmp;
}
/* Hack to get 20 ms working with APPLICATION_AUDIO
The real problem is that the corresponding memory needs to use 1.5 ms
from this frame and 1 ms from the next frame */
e[i + pos] = e[i + pos - 1];
if (buffering != 0)
N = Inlines.IMIN(MAX_DYNAMIC_FRAMESIZE, N + 2);
bestLM = transient_viterbi(e, e_1, N, (int)((1.0f + .5f * tonality) * (60 * C + 40)), bitrate / 400);
mem[0] = e[1 << bestLM];
if (buffering != 0)
{
mem[1] = e[(1 << bestLM) + 1];
mem[2] = e[(1 << bestLM) + 2];
}
return bestLM;
}
internal static int frame_size_select(int frame_size, OpusFramesize variable_duration, int Fs)
{
int new_size;
if (frame_size < Fs / 400)
return -1;
if (variable_duration == OpusFramesize.OPUS_FRAMESIZE_ARG)
new_size = frame_size;
else if (variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE)
new_size = Fs / 50;
else if (variable_duration >= OpusFramesize.OPUS_FRAMESIZE_2_5_MS && variable_duration <= OpusFramesize.OPUS_FRAMESIZE_60_MS)
new_size = Inlines.IMIN(3 * Fs / 50, (Fs / 400) << (variable_duration - OpusFramesize.OPUS_FRAMESIZE_2_5_MS));
else
return -1;
if (new_size > frame_size)
return -1;
if (400 * new_size != Fs && 200 * new_size != Fs && 100 * new_size != Fs &&
50 * new_size != Fs && 25 * new_size != Fs && 50 * new_size != 3 * Fs)
return -1;
return new_size;
}
internal static int compute_frame_size<T>(T[] analysis_pcm, int analysis_pcm_ptr, int frame_size,
OpusFramesize variable_duration, int C, int Fs, int bitrate_bps,
int delay_compensation, Downmix.downmix_func<T> downmix, float[] subframe_mem, bool analysis_enabled
)
{
if (analysis_enabled && variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size >= Fs / 200)
{
int LM = 3;
LM = optimize_framesize(analysis_pcm, analysis_pcm_ptr, frame_size, C, Fs, bitrate_bps,
0, subframe_mem, delay_compensation, downmix);
while ((Fs / 400 << LM) > frame_size)
LM--;
frame_size = (Fs / 400 << LM);
}
else
{
frame_size = frame_size_select(frame_size, variable_duration, Fs);
}
if (frame_size < 0)
return -1;
return frame_size;
}
internal static int compute_stereo_width(short[] pcm, int pcm_ptr, int frame_size, int Fs, StereoWidthState mem)
{
int corr;
int ldiff;
int width;
int xx, xy, yy;
int sqrt_xx, sqrt_yy;
int qrrt_xx, qrrt_yy;
int frame_rate;
int i;
int short_alpha;
frame_rate = Fs / frame_size;
short_alpha = CeltConstants.Q15ONE - (25 * CeltConstants.Q15ONE / Inlines.IMAX(50, frame_rate));
xx = xy = yy = 0;
for (i = 0; i < frame_size - 3; i += 4)
{
int pxx = 0;
int pxy = 0;
int pyy = 0;
int x, y;
int p2i = pcm_ptr + (2 * i);
x = pcm[p2i];
y = pcm[p2i + 1];
pxx = Inlines.SHR32(Inlines.MULT16_16(x, x), 2);
pxy = Inlines.SHR32(Inlines.MULT16_16(x, y), 2);
pyy = Inlines.SHR32(Inlines.MULT16_16(y, y), 2);
x = pcm[p2i + 2];
y = pcm[p2i + 3];
pxx += Inlines.SHR32(Inlines.MULT16_16(x, x), 2);
pxy += Inlines.SHR32(Inlines.MULT16_16(x, y), 2);
pyy += Inlines.SHR32(Inlines.MULT16_16(y, y), 2);
x = pcm[p2i + 4];
y = pcm[p2i + 5];
pxx += Inlines.SHR32(Inlines.MULT16_16(x, x), 2);
pxy += Inlines.SHR32(Inlines.MULT16_16(x, y), 2);
pyy += Inlines.SHR32(Inlines.MULT16_16(y, y), 2);
x = pcm[p2i + 6];
y = pcm[p2i + 7];
pxx += Inlines.SHR32(Inlines.MULT16_16(x, x), 2);
pxy += Inlines.SHR32(Inlines.MULT16_16(x, y), 2);
pyy += Inlines.SHR32(Inlines.MULT16_16(y, y), 2);
xx += Inlines.SHR32(pxx, 10);
xy += Inlines.SHR32(pxy, 10);
yy += Inlines.SHR32(pyy, 10);
}
mem.XX += Inlines.MULT16_32_Q15(short_alpha, xx - mem.XX);
mem.XY += Inlines.MULT16_32_Q15(short_alpha, xy - mem.XY);
mem.YY += Inlines.MULT16_32_Q15(short_alpha, yy - mem.YY);
mem.XX = Inlines.MAX32(0, mem.XX);
mem.XY = Inlines.MAX32(0, mem.XY);
mem.YY = Inlines.MAX32(0, mem.YY);
if (Inlines.MAX32(mem.XX, mem.YY) > ((short)(0.5 + (8e-4f) * (((int)1) << (18))))/*Inlines.QCONST16(8e-4f, 18)*/)
{
sqrt_xx = Inlines.celt_sqrt(mem.XX);
sqrt_yy = Inlines.celt_sqrt(mem.YY);
qrrt_xx = Inlines.celt_sqrt(sqrt_xx);
qrrt_yy = Inlines.celt_sqrt(sqrt_yy);
/* Inter-channel correlation */
mem.XY = Inlines.MIN32(mem.XY, sqrt_xx * sqrt_yy);
corr = Inlines.SHR32(Inlines.frac_div32(mem.XY, CeltConstants.EPSILON + Inlines.MULT16_16(sqrt_xx, sqrt_yy)), 16);
/* Approximate loudness difference */
ldiff = CeltConstants.Q15ONE * Inlines.ABS16(qrrt_xx - qrrt_yy) / (CeltConstants.EPSILON + qrrt_xx + qrrt_yy);
width = Inlines.MULT16_16_Q15(Inlines.celt_sqrt(((int)(0.5 + (1.0f) * (((int)1) << (30))))/*Inlines.QCONST32(1.0f, 30)*/ - Inlines.MULT16_16(corr, corr)), ldiff);
/* Smoothing over one second */
mem.smoothed_width += (width - mem.smoothed_width) / frame_rate;
/* Peak follower */
mem.max_follower = Inlines.MAX16(mem.max_follower - ((short)(0.5 + (.02f) * (((int)1) << (15))))/*Inlines.QCONST16(.02f, 15)*/ / frame_rate, mem.smoothed_width);
}
else {
width = 0;
corr = CeltConstants.Q15ONE;
ldiff = 0;
}
/*printf("%f %f %f %f %f ", corr/(float)1.0f, ldiff/(float)1.0f, width/(float)1.0f, mem.smoothed_width/(float)1.0f, mem.max_follower/(float)1.0f);*/
return Inlines.EXTRACT16(Inlines.MIN32(CeltConstants.Q15ONE, 20 * mem.max_follower));
}
internal static void smooth_fade(short[] in1, int in1_ptr, short[] in2, int in2_ptr,
short[] output, int output_ptr, int overlap, int channels,
int[] window, int Fs)
{
int i, c;
int inc = 48000 / Fs;
for (c = 0; c < channels; c++)
{
for (i = 0; i < overlap; i++)
{
int w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]);
output[output_ptr + (i * channels) + c] = (short)(Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, in2[in2_ptr + (i * channels) + c]),
CeltConstants.Q15ONE - w, in1[in1_ptr + (i * channels) + c]), 15));
}
}
}
//internal static void opus_pcm_soft_clip(Pointer<float> _x, int N, int C, Pointer<float> declip_mem)
//{
// int c;
// int i;
// Pointer<float> x;
// if (C < 1 || N < 1 || _x == null || declip_mem == null) return;
// /* First thing: saturate everything to +/- 2 which is the highest level our
// non-linearity can handle. At the point where the signal reaches +/-2,
// the derivative will be zero anyway, so this doesn't introduce any
// discontinuity in the derivative. */
// for (i = 0; i < N * C; i++)
// _x[i] = Inlines.MAX16(-2.0f, Inlines.MIN16(2.0f, _x[i]));
// for (c = 0; c < C; c++)
// {
// float a;
// float x0;
// int curr;
// x = _x.Point(c);
// a = declip_mem[c];
// /* Continue applying the non-linearity from the previous frame to avoid
// any discontinuity. */
// for (i = 0; i < N; i++)
// {
// if (x[i * C] * a >= 0)
// break;
// x[i * C] = x[i * C] + a * x[i * C] * x[i * C];
// }
// curr = 0;
// x0 = x[0];
// while (true)
// {
// int start, end;
// float maxval;
// int special = 0;
// int peak_pos;
// for (i = curr; i < N; i++)
// {
// if (x[i * C] > 1 || x[i * C] < -1)
// break;
// }
// if (i == N)
// {
// a = 0;
// break;
// }
// peak_pos = i;
// start = end = i;
// maxval = Inlines.ABS16(x[i * C]);
// /* Look for first zero crossing before clipping */
// while (start > 0 && x[i * C] * x[(start - 1) * C] >= 0)
// start--;
// /* Look for first zero crossing after clipping */
// while (end < N && x[i * C] * x[end * C] >= 0)
// {
// /* Look for other peaks until the next zero-crossing. */
// if (Inlines.ABS16(x[end * C]) > maxval)
// {
// maxval = Inlines.ABS16(x[end * C]);
// peak_pos = end;
// }
// end++;
// }
// /* Detect the special case where we clip before the first zero crossing */
// special = (start == 0 && x[i * C] * x[0] >= 0) ? 1 : 0;
// /* Compute a such that maxval + a*maxval^2 = 1 */
// a = (maxval - 1) / (maxval * maxval);
// if (x[i * C] > 0)
// a = -a;
// /* Apply soft clipping */
// for (i = start; i < end; i++)
// x[i * C] = x[i * C] + a * x[i * C] * x[i * C];
// if (special != 0 && peak_pos >= 2)
// {
// /* Add a linear ramp from the first sample to the signal peak.
// This avoids a discontinuity at the beginning of the frame. */
// float delta;
// float offset = x0 - x[0];
// delta = offset / peak_pos;
// for (i = curr; i < peak_pos; i++)
// {
// offset -= delta;
// x[i * C] += offset;
// x[i * C] = Inlines.MAX16(-1.0f, Inlines.MIN16(1.0f, x[i * C]));
// }
// }
// curr = end;
// if (curr == N)
// {
// break;
// }
// }
// declip_mem[c] = a;
// }
//}
internal static string opus_strerror(int error)
{
string[] error_strings = {
"success",
"invalid argument",
"buffer too small",
"internal error",
"corrupted stream",
"request not implemented",
"invalid state",
"memory allocation failed"
};
if (error > 0 || error < -7)
return "unknown error";
else
return error_strings[-error];
}
/// <summary>
/// Returns the version number of this library
/// </summary>
/// <returns></returns>
public static string GetVersionString()
{
return "Concentus 1.1.6"
#if DEBUG
+ "-debug"
#endif
#if FUZZING
+ "-fuzzing"
#endif
#if PARITY
#else
+ "-nonparity"
#endif
;
}
}
}

View File

@@ -0,0 +1,125 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Celt;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus
{
internal static class Downmix
{
/// <summary>
///
/// </summary>
/// <typeparam name="T">The type of signal being handled (either short or float)</typeparam>
/// <param name="_x"></param>
/// <param name="sub"></param>
/// <param name="subframe"></param>
/// <param name="offset"></param>
/// <param name="c1"></param>
/// <param name="c2"></param>
/// <param name="C"></param>
public delegate void downmix_func<T>(T[] _x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C);
internal static void downmix_float(float[] x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C)
{
int scale;
int j;
int c1x = c1 + x_ptr;
for (j = 0; j < subframe; j++)
sub[sub_ptr + j] = Inlines.FLOAT2INT16(x[(j + offset) * C + c1x]);
if (c2 > -1)
{
int c2x = c2 + x_ptr;
for (j = 0; j < subframe; j++)
sub[sub_ptr + j] += Inlines.FLOAT2INT16(x[(j + offset) * C + c2x]);
}
else if (c2 == -2)
{
int c;
int cx;
for (c = 1; c < C; c++)
{
cx = c + x_ptr;
for (j = 0; j < subframe; j++)
sub[sub_ptr + j] += Inlines.FLOAT2INT16(x[(j + offset) * C + cx]);
}
}
scale = (1 << CeltConstants.SIG_SHIFT);
if (C == -2)
scale /= C;
else
scale /= 2;
for (j = 0; j < subframe; j++)
sub[sub_ptr + j] *= scale;
}
internal static void downmix_int(short[] x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C)
{
int scale;
int j;
for (j = 0; j < subframe; j++)
sub[j + sub_ptr] = x[(j + offset) * C + c1];
if (c2 > -1)
{
for (j = 0; j < subframe; j++)
sub[j + sub_ptr] += x[(j + offset) * C + c2];
}
else if (c2 == -2)
{
int c;
for (c = 1; c < C; c++)
{
for (j = 0; j < subframe; j++)
sub[j + sub_ptr] += x[(j + offset) * C + c];
}
}
scale = (1 << CeltConstants.SIG_SHIFT);
if (C == -2)
scale /= C;
else
scale /= 2;
for (j = 0; j < subframe; j++)
sub[j + sub_ptr] *= scale;
}
}
}

View File

@@ -0,0 +1,57 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Enums
{
public enum OpusApplication
{
OPUS_APPLICATION_UNIMPLEMENTED = 0,
/// <summary>
/// Best for most VoIP/videoconference applications where listening quality and intelligibility matter most
/// </summary>
OPUS_APPLICATION_VOIP = 2048,
/// <summary>
/// Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input
/// </summary>
OPUS_APPLICATION_AUDIO = 2049,
/// <summary>
/// Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used.
/// </summary>
OPUS_APPLICATION_RESTRICTED_LOWDELAY = 2051
}
}

View File

@@ -0,0 +1,70 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Enums
{
public enum OpusBandwidth
{
OPUS_BANDWIDTH_AUTO = -1000,
OPUS_BANDWIDTH_NARROWBAND = 1101,
OPUS_BANDWIDTH_MEDIUMBAND = 1102,
OPUS_BANDWIDTH_WIDEBAND = 1103,
OPUS_BANDWIDTH_SUPERWIDEBAND = 1104,
OPUS_BANDWIDTH_FULLBAND = 1105
}
// FIXME: We should remove all cases where bandwidth is cast to int, it's.....improper
internal static class OpusBandwidthHelpers
{
internal static int GetOrdinal(OpusBandwidth bw)
{
return (int)bw - (int)OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND;
}
internal static OpusBandwidth MIN(OpusBandwidth a, OpusBandwidth b)
{
if ((int)a < (int)b)
return a;
return b;
}
internal static OpusBandwidth MAX(OpusBandwidth a, OpusBandwidth b)
{
if ((int)a > (int)b)
return a;
return b;
}
}
}

View File

@@ -0,0 +1,94 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Enums
{
public static class OpusControl
{
/** These are the actual Encoder CTL ID numbers.
* They should not be used directly by applications.
* In general, SETs should be even and GETs should be odd.*/
public const int OPUS_SET_APPLICATION_REQUEST = 4000;
public const int OPUS_GET_APPLICATION_REQUEST = 4001;
public const int OPUS_SET_BITRATE_REQUEST = 4002;
public const int OPUS_GET_BITRATE_REQUEST = 4003;
public const int OPUS_SET_MAX_BANDWIDTH_REQUEST = 4004;
public const int OPUS_GET_MAX_BANDWIDTH_REQUEST = 4005;
public const int OPUS_SET_VBR_REQUEST = 4006;
public const int OPUS_GET_VBR_REQUEST = 4007;
public const int OPUS_SET_BANDWIDTH_REQUEST = 4008;
public const int OPUS_GET_BANDWIDTH_REQUEST = 4009;
public const int OPUS_SET_COMPLEXITY_REQUEST = 4010;
public const int OPUS_GET_COMPLEXITY_REQUEST = 4011;
public const int OPUS_SET_INBAND_FEC_REQUEST = 4012;
public const int OPUS_GET_INBAND_FEC_REQUEST = 4013;
public const int OPUS_SET_PACKET_LOSS_PERC_REQUEST = 4014;
public const int OPUS_GET_PACKET_LOSS_PERC_REQUEST = 4015;
public const int OPUS_SET_DTX_REQUEST = 4016;
public const int OPUS_GET_DTX_REQUEST = 4017;
public const int OPUS_SET_VBR_CONSTRAINT_REQUEST = 4020;
public const int OPUS_GET_VBR_CONSTRAINT_REQUEST = 4021;
public const int OPUS_SET_FORCE_CHANNELS_REQUEST = 4022;
public const int OPUS_GET_FORCE_CHANNELS_REQUEST = 4023;
public const int OPUS_SET_SIGNAL_REQUEST = 4024;
public const int OPUS_GET_SIGNAL_REQUEST = 4025;
public const int OPUS_GET_LOOKAHEAD_REQUEST = 4027;
/* public const int OPUS_RESET_STATE 4028 */
public const int OPUS_GET_SAMPLE_RATE_REQUEST = 4029;
public const int OPUS_GET_FINAL_RANGE_REQUEST = 4031;
public const int OPUS_GET_PITCH_REQUEST = 4033;
public const int OPUS_SET_GAIN_REQUEST = 4034;
public const int OPUS_GET_GAIN_REQUEST = 4045;
public const int OPUS_SET_LSB_DEPTH_REQUEST = 4036;
public const int OPUS_GET_LSB_DEPTH_REQUEST = 4037;
public const int OPUS_GET_LAST_PACKET_DURATION_REQUEST = 4039;
public const int OPUS_SET_EXPERT_FRAME_DURATION_REQUEST = 4040;
public const int OPUS_GET_EXPERT_FRAME_DURATION_REQUEST = 4041;
public const int OPUS_SET_PREDICTION_DISABLED_REQUEST = 4042;
public const int OPUS_GET_PREDICTION_DISABLED_REQUEST = 4043;
/// <summary>
/// Resets the codec state to be equivalent to a freshly initialized state.
/// This should be called when switching streams in order to prevent
/// the back to back decoding from giving different results from
/// one at a time decoding.
/// </summary>
public const int OPUS_RESET_STATE = 4028;
public const int OPUS_SET_VOICE_RATIO_REQUEST = 11018;
public const int OPUS_GET_VOICE_RATIO_REQUEST = 11019;
public const int OPUS_SET_FORCE_MODE_REQUEST = 11002;
}
}

View File

@@ -0,0 +1,68 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Enums
{
/// <summary>
/// Note that since most API-level errors are detected and thrown as
/// OpusExceptions, direct use of this class is not usually needed
/// </summary>
public static class OpusError
{
/** No error*/
public const int OPUS_OK = 0;
/** One or more invalid/out of range arguments*/
public const int OPUS_BAD_ARG = -1;
/** Not enough bytes allocated in the buffer*/
public const int OPUS_BUFFER_TOO_SMALL = -2;
/** An public error was detected*/
public const int OPUS_INTERNAL_ERROR = -3;
/** The compressed data passed is corrupted*/
public const int OPUS_INVALID_PACKET = -4;
/** Invalid/unsupported request number*/
public const int OPUS_UNIMPLEMENTED = -5;
/** An encoder or decoder structure is invalid or already freed*/
public const int OPUS_INVALID_STATE = -6;
/** Memory allocation has failed*/
public const int OPUS_ALLOC_FAIL = -7;
}
}

View File

@@ -0,0 +1,80 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Enums
{
public enum OpusFramesize
{
/// <summary>
/// Select frame size from the argument (default)
/// </summary>
OPUS_FRAMESIZE_ARG = 5000,
/// <summary>
/// Use 2.5 ms frames
/// </summary>
OPUS_FRAMESIZE_2_5_MS = 5001,
/// <summary>
/// Use 5 ms frames
/// </summary>
OPUS_FRAMESIZE_5_MS = 5002,
/// <summary>
/// Use 10 ms frames
/// </summary>
OPUS_FRAMESIZE_10_MS = 5003,
/// <summary>
/// Use 20 ms frames
/// </summary>
OPUS_FRAMESIZE_20_MS = 5004,
/// <summary>
/// Use 40 ms frames
/// </summary>
OPUS_FRAMESIZE_40_MS = 5005,
/// <summary>
/// Use 60 ms frames
/// </summary>
OPUS_FRAMESIZE_60_MS = 5006,
/// <summary>
/// Do not use - not fully implemented. Optimize the frame size dynamically.
/// </summary>
OPUS_FRAMESIZE_VARIABLE = 5010
}
}

View File

@@ -0,0 +1,45 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Enums
{
public enum OpusMode
{
MODE_AUTO = -1000,
MODE_SILK_ONLY = 1000,
MODE_HYBRID = 1001,
MODE_CELT_ONLY = 1002
}
}

View File

@@ -0,0 +1,52 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Enums
{
public enum OpusSignal
{
OPUS_SIGNAL_AUTO = -1000,
/// <summary>
/// Signal being encoded is voice
/// </summary>
OPUS_SIGNAL_VOICE = 3001,
/// <summary>
/// Signal being encoded is music
/// </summary>
OPUS_SIGNAL_MUSIC = 3002
}
}

View File

@@ -0,0 +1,111 @@
/* Copyright (c) 2008-2011 Octasic Inc.
Originally written by Jean-Marc Valin
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Structs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus
{
/// <summary>
/// multi-layer perceptron processor
/// </summary>
internal static class mlp
{
private const int MAX_NEURONS = 100;
internal static float tansig_approx(float x)
{
int i;
float y, dy;
float sign = 1;
/* Tests are reversed to catch NaNs */
if (!(x < 8))
return 1;
if (!(x > -8))
return -1;
if (x < 0)
{
x = -x;
sign = -1;
}
i = (int)Math.Floor(.5f + 25 * x);
x -= .04f * i;
y = Tables.tansig_table[i];
dy = 1 - y * y;
y = y + x * dy * (1 - y * x);
return sign * y;
}
internal static void mlp_process(MLP m, float[] input, float[] output)
{
int j;
float[] hidden = new float[MAX_NEURONS];
float[] W = m.weights;
int W_ptr = 0;
/* Copy to tmp_in */
for (j = 0; j < m.topo[1]; j++)
{
int k;
float sum = W[W_ptr];
W_ptr++;
for (k = 0; k < m.topo[0]; k++)
{
sum = sum + input[k] * W[W_ptr];
W_ptr++;
}
hidden[j] = tansig_approx(sum);
}
for (j = 0; j < m.topo[2]; j++)
{
int k;
float sum = W[W_ptr];
W_ptr++;
for (k = 0; k < m.topo[1]; k++)
{
sum = sum + hidden[k] * W[W_ptr];
W_ptr++;
}
output[j] = tansig_approx(sum);
}
}
}
}

View File

@@ -0,0 +1,340 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Common.CPlusPlus;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace Concentus
{
internal static class OpusCompare
{
private const int NBANDS = 21;
private const int NFREQS = 240;
private const int TEST_WIN_SIZE = 480;
private const int TEST_WIN_STEP = 120;
/*Bands on which we compute the pseudo-NMR (Bark-derived CELT bands).*/
private static readonly int[] BANDS/*[NBANDS + 1]*/ ={
0,2,4,6,8,10,12,14,16,20,24,28,32,40,48,56,68,80,96,120,156,200
};
private static void band_energy(Pointer<float> _out, Pointer<float> _ps,
Pointer<int> _bands, int _nbands,
Pointer<float> _in, int _nchannels, int _nframes, int _window_sz,
int _step, int _downsample)
{
Pointer<float> window;
Pointer<float> x;
Pointer<float> c;
Pointer<float> s;
int xi;
int xj;
int ps_sz;
window = Pointer.Malloc<float>((3 + _nchannels) * _window_sz);
c = window.Point(_window_sz);
s = c.Point(_window_sz);
x = s.Point(_window_sz);
ps_sz = _window_sz / 2;
for (xj = 0; xj < _window_sz; xj++)
{
window[xj] = (float)(0.5 - 0.5 * Math.Cos((2 * Math.PI / (_window_sz - 1)) * xj));
}
for (xj = 0; xj < _window_sz; xj++)
{
c[xj] = (float)Math.Cos((2 * Math.PI / _window_sz) * xj);
}
for (xj = 0; xj < _window_sz; xj++)
{
s[xj] = (float)Math.Sin((2 * Math.PI / _window_sz) * xj);
}
for (xi = 0; xi < _nframes; xi++)
{
int ci;
int xk;
int bi;
for (ci = 0; ci < _nchannels; ci++)
{
for (xk = 0; xk < _window_sz; xk++)
{
x[ci * _window_sz + xk] = window[xk] * _in[(xi * _step + xk) * _nchannels + ci];
}
}
for (bi = xj = 0; bi < _nbands; bi++)
{
float[] p = { 0, 0 };
for (; xj < _bands[bi + 1]; xj++)
{
for (ci = 0; ci < _nchannels; ci++)
{
float re;
float im;
int ti;
ti = 0;
re = im = 0;
for (xk = 0; xk < _window_sz; xk++)
{
re += c[ti] * x[ci * _window_sz + xk];
im -= s[ti] * x[ci * _window_sz + xk];
ti += xj;
if (ti >= _window_sz) ti -= _window_sz;
}
re *= _downsample;
im *= _downsample;
_ps[(xi * ps_sz + xj) * _nchannels + ci] = re * re + im * im + 100000;
p[ci] += _ps[(xi * ps_sz + xj) * _nchannels + ci];
}
}
if (_out != null)
{
_out[(xi * _nbands + bi) * _nchannels] = p[0] / (_bands[bi + 1] - _bands[bi]);
if (_nchannels == 2)
{
_out[(xi * _nbands + bi) * _nchannels + 1] = p[1] / (_bands[bi + 1] - _bands[bi]);
}
}
}
}
}
internal static float compare(float[] x, float[] y, int nchannels, int rate = 48000)
{
Pointer<float> xb;
Pointer<float> X;
Pointer<float> Y;
double err;
float Q;
int xlength = x.Length;
int ylength = y.Length;
int nframes;
int xi;
int ci;
int xj;
int bi;
int downsample;
int ybands;
int yfreqs;
int max_compare;
ybands = NBANDS;
yfreqs = NFREQS;
if (rate != 8000 && rate != 12000 && rate != 16000 && rate != 24000 && rate != 48000)
{
throw new ArgumentException("Sampling rate must be 8000, 12000, 16000, 24000, or 48000\n");
}
if (rate != 48000)
{
downsample = 48000 / rate;
switch (rate)
{
case 8000: ybands = 13; break;
case 12000: ybands = 15; break;
case 16000: ybands = 17; break;
case 24000: ybands = 19; break;
}
yfreqs = NFREQS / downsample;
}
else
{
downsample = 1;
}
if (xlength != ylength * downsample)
{
throw new ArgumentException("Sample counts do not match");
}
if (xlength < TEST_WIN_SIZE)
{
throw new ArgumentException("Insufficient sample data");
}
nframes = (xlength - TEST_WIN_SIZE + TEST_WIN_STEP) / TEST_WIN_STEP;
xb = Pointer.Malloc<float>(nframes * NBANDS * nchannels);
X = Pointer.Malloc<float>(nframes * NFREQS * nchannels);
Y = Pointer.Malloc<float>(nframes * yfreqs * nchannels);
/*Compute the per-band spectral energy of the original signal
and the error.*/
band_energy(xb, X, BANDS.GetPointer(), NBANDS, x.GetPointer(), nchannels, nframes,
TEST_WIN_SIZE, TEST_WIN_STEP, 1);
band_energy(null, Y, BANDS.GetPointer(), ybands, y.GetPointer(), nchannels, nframes,
TEST_WIN_SIZE / downsample, TEST_WIN_STEP / downsample, downsample);
for (xi = 0; xi < nframes; xi++)
{
/*Frequency masking (low to high): 10 dB/Bark slope.*/
for (bi = 1; bi < NBANDS; bi++)
{
for (ci = 0; ci < nchannels; ci++)
{
xb[(xi * NBANDS + bi) * nchannels + ci] +=
0.1F * xb[(xi * NBANDS + bi - 1) * nchannels + ci];
}
}
/*Frequency masking (high to low): 15 dB/Bark slope.*/
for (bi = NBANDS - 1; bi-- > 0;)
{
for (ci = 0; ci < nchannels; ci++)
{
xb[(xi * NBANDS + bi) * nchannels + ci] +=
0.03F * xb[(xi * NBANDS + bi + 1) * nchannels + ci];
}
}
if (xi > 0)
{
/*Temporal masking: -3 dB/2.5ms slope.*/
for (bi = 0; bi < NBANDS; bi++)
{
for (ci = 0; ci < nchannels; ci++)
{
xb[(xi * NBANDS + bi) * nchannels + ci] +=
0.5F * xb[((xi - 1) * NBANDS + bi) * nchannels + ci];
}
}
}
/* Allowing some cross-talk */
if (nchannels == 2)
{
for (bi = 0; bi < NBANDS; bi++)
{
float l, r;
l = xb[(xi * NBANDS + bi) * nchannels + 0];
r = xb[(xi * NBANDS + bi) * nchannels + 1];
xb[(xi * NBANDS + bi) * nchannels + 0] += 0.01F * r;
xb[(xi * NBANDS + bi) * nchannels + 1] += 0.01F * l;
}
}
/* Apply masking */
for (bi = 0; bi < ybands; bi++)
{
for (xj = BANDS[bi]; xj < BANDS[bi + 1]; xj++)
{
for (ci = 0; ci < nchannels; ci++)
{
X[(xi * NFREQS + xj) * nchannels + ci] +=
0.1F * xb[(xi * NBANDS + bi) * nchannels + ci];
Y[(xi * yfreqs + xj) * nchannels + ci] +=
0.1F * xb[(xi * NBANDS + bi) * nchannels + ci];
}
}
}
}
/* Average of consecutive frames to make comparison slightly less sensitive */
for (bi = 0; bi < ybands; bi++)
{
for (xj = BANDS[bi]; xj < BANDS[bi + 1]; xj++)
{
for (ci = 0; ci < nchannels; ci++)
{
float xtmp;
float ytmp;
xtmp = X[xj * nchannels + ci];
ytmp = Y[xj * nchannels + ci];
for (xi = 1; xi < nframes; xi++)
{
float xtmp2;
float ytmp2;
xtmp2 = X[(xi * NFREQS + xj) * nchannels + ci];
ytmp2 = Y[(xi * yfreqs + xj) * nchannels + ci];
X[(xi * NFREQS + xj) * nchannels + ci] += xtmp;
Y[(xi * yfreqs + xj) * nchannels + ci] += ytmp;
xtmp = xtmp2;
ytmp = ytmp2;
}
}
}
}
/*If working at a lower sampling rate, don't take into account the last
300 Hz to allow for different transition bands.
For 12 kHz, we don't skip anything, because the last band already skips
400 Hz.*/
if (rate == 48000) max_compare = BANDS[NBANDS];
else if (rate == 12000) max_compare = BANDS[ybands];
else max_compare = BANDS[ybands] - 3;
err = 0;
for (xi = 0; xi < nframes; xi++)
{
double Ef;
Ef = 0;
for (bi = 0; bi < ybands; bi++)
{
double Eb;
Eb = 0;
for (xj = BANDS[bi]; xj < BANDS[bi + 1] && xj < max_compare; xj++)
{
for (ci = 0; ci < nchannels; ci++)
{
float re;
float im;
re = Y[(xi * yfreqs + xj) * nchannels + ci] / X[(xi * NFREQS + xj) * nchannels + ci];
im = re - (float)Math.Log(re) - 1;
/*Make comparison less sensitive around the SILK/CELT cross-over to
allow for mode freedom in the filters.*/
if (xj >= 79 && xj <= 81) im *= 0.1F;
if (xj == 80) im *= 0.1F;
Eb += im;
}
}
Eb /= (BANDS[bi + 1] - BANDS[bi]) * nchannels;
Ef += Eb * Eb;
}
/*Using a fixed normalization value means we're willing to accept slightly
lower quality for lower sampling rates.*/
Ef /= NBANDS;
Ef *= Ef;
err += Ef * Ef;
}
err = Math.Pow(err / nframes, 1.0 / 16);
Q = (float)(100 * (1 - 0.5 * Math.Log(1 + err) / Math.Log(1.13)));
if (Q < 0)
{
Debug.WriteLine("Test vector FAILS");
Debug.WriteLine(string.Format("Internal weighted error is {0}", err));
}
else {
Debug.WriteLine("Test vector PASSES");
Debug.WriteLine(string.Format("Opus quality metric: {0} (internal weighted error is {1})", Q, err));
}
return Q;
}
}
}

View File

@@ -0,0 +1,66 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus
{
public static class OpusConstants
{
/// <summary>
/// Auto/default setting
/// </summary>
public const int OPUS_AUTO = -1000;
/// <summary>
/// Maximum bitrate
/// </summary>
public const int OPUS_BITRATE_MAX = -1;
// from analysis.c
public const int NB_FRAMES = 8;
public const int NB_TBANDS = 18;
public const int NB_TOT_BANDS = 21;
public const int NB_TONAL_SKIP_BANDS = 9;
public const int ANALYSIS_BUF_SIZE = 720; /* 15 ms at 48 kHz */
public const int DETECT_SIZE = 200;
public const int MAX_ENCODER_BUFFER = 480;
}
}

View File

@@ -0,0 +1,46 @@
/* Copyright (c) 2016 Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus
{
public class OpusException : Exception
{
public OpusException() : base() { }
public OpusException(string message) : base(message) { }
public OpusException(string message, int opus_error_code) : base(message + ": " + CodecHelpers.opus_strerror(opus_error_code)) { }
}
}

View File

@@ -0,0 +1,99 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Structs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus
{
internal static class OpusMultistream
{
internal static int validate_layout(ChannelLayout layout)
{
int i, max_channel;
max_channel = layout.nb_streams + layout.nb_coupled_streams;
if (max_channel > 255)
return 0;
for (i = 0; i < layout.nb_channels; i++)
{
if (layout.mapping[i] >= max_channel && layout.mapping[i] != 255)
return 0;
}
return 1;
}
internal static int get_left_channel(ChannelLayout layout, int stream_id, int prev)
{
int i;
i = (prev < 0) ? 0 : prev + 1;
for (; i < layout.nb_channels; i++)
{
if (layout.mapping[i] == stream_id * 2)
return i;
}
return -1;
}
internal static int get_right_channel(ChannelLayout layout, int stream_id, int prev)
{
int i;
i = (prev < 0) ? 0 : prev + 1;
for (; i < layout.nb_channels; i++)
{
if (layout.mapping[i] == stream_id * 2 + 1)
return i;
}
return -1;
}
internal static int get_mono_channel(ChannelLayout layout, int stream_id, int prev)
{
int i;
i = (prev < 0) ? 0 : prev + 1;
for (; i < layout.nb_channels; i++)
{
if (layout.mapping[i] == stream_id + layout.nb_coupled_streams)
return i;
}
return -1;
}
}
}

View File

@@ -0,0 +1,60 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Common.CPlusPlus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus.Structs
{
internal class ChannelLayout
{
internal int nb_channels;
internal int nb_streams;
internal int nb_coupled_streams;
internal readonly byte[] mapping = new byte[256];
internal void Reset()
{
nb_channels = 0;
nb_streams = 0;
nb_coupled_streams = 0;
Arrays.MemSetByte(mapping, 0);
}
}
}

View File

@@ -0,0 +1,52 @@
/* Copyright (c) 2008-2011 Octasic Inc.
Originally written by Jean-Marc Valin
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Common.CPlusPlus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus.Structs
{
/// <summary>
/// state object for multi-layer perceptron
/// </summary>
internal class MLP
{
internal int layers;
internal int[] topo;
internal float[] weights;
}
}

View File

@@ -0,0 +1,933 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Celt;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using Concentus.Silk;
using Concentus.Silk.Structs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus.Structs
{
/// <summary>
/// The Opus decoder structure.
///
/// Opus is a stateful codec with overlapping blocks and as a result Opus
/// packets are not coded independently of each other. Packets must be
/// passed into the decoder serially and in the correct order for a correct
/// decode. Lost packets can be replaced with loss concealment by calling
/// the decoder with a null reference and zero length for the missing packet.
///
/// A single codec state may only be accessed from a single thread at
/// a time and any required locking must be performed by the caller. Separate
/// streams must be decoded with separate decoder states and can be decoded
/// in parallel.
/// </summary>
public class OpusDecoder
{
internal int channels;
internal int Fs; /** Sampling rate (at the API level) */
internal readonly DecControlState DecControl = new DecControlState();
internal int decode_gain;
/* Everything beyond this point gets cleared on a reset */
internal int stream_channels;
internal OpusBandwidth bandwidth;
internal OpusMode mode;
internal OpusMode prev_mode;
internal int frame_size;
internal int prev_redundancy;
internal int last_packet_duration;
internal uint rangeFinal;
internal SilkDecoder SilkDecoder = new SilkDecoder();
internal CeltDecoder Celt_Decoder = new CeltDecoder();
// Used by multistream decoder
internal OpusDecoder() { }
internal void Reset()
{
channels = 0;
Fs = 0; /* Sampling rate (at the API level) */
DecControl.Reset();
decode_gain = 0;
PartialReset();
}
/// <summary>
/// OPUS_DECODER_RESET_START
/// </summary>
internal void PartialReset()
{
stream_channels = 0;
bandwidth = 0;
mode = 0;
prev_mode = 0;
frame_size = 0;
prev_redundancy = 0;
last_packet_duration = 0;
rangeFinal = 0;
// fixme: do these get reset here? I don't think they do because init_celt and init_silk should both call RESET_STATE on their respective states
//SilkDecoder.Reset();
//CeltDecoder.Reset();
}
/// <summary>
/// Deprecated. Just use the regular constructor
/// </summary>
public static OpusDecoder Create(int Fs, int channels)
{
return new OpusDecoder(Fs, channels);
}
/// <summary>
/// Allocates and initializes a decoder state.
/// Internally Opus stores data at 48000 Hz, so that should be the default
/// value for Fs. However, the decoder can efficiently decode to buffers
/// at 8, 12, 16, and 24 kHz so if for some reason the caller cannot use
/// data at the full sample rate, or knows the compressed data doesn't
/// use the full frequency range, it can request decoding at a reduced
/// rate. Likewise, the decoder is capable of filling in either mono or
/// interleaved stereo pcm buffers, at the caller's request.
/// </summary>
/// <param name="Fs">Sample rate to decode at (Hz). This must be one of 8000, 12000, 16000, 24000, or 48000.</param>
/// <param name="channels">Number of channels (1 or 2) to decode</param>
/// <returns>The created encoder</returns>
public OpusDecoder(int Fs, int channels)
{
int ret;
if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000))
{
throw new ArgumentException("Sample rate is invalid (must be 8/12/16/24/48 Khz)");
}
if (channels != 1 && channels != 2)
{
throw new ArgumentException("Number of channels must be 1 or 2");
}
ret = this.opus_decoder_init(Fs, channels);
if (ret != OpusError.OPUS_OK)
{
if (ret == OpusError.OPUS_BAD_ARG)
throw new ArgumentException("OPUS_BAD_ARG when creating decoder");
throw new OpusException("Error while initializing decoder", ret);
}
}
/** Initializes a previously allocated decoder state.
* The state must be at least the size returned by opus_decoder_get_size().
* This is intended for applications which use their own allocator instead of malloc. @see opus_decoder_create,opus_decoder_get_size
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state.
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate to decode to (Hz).
* This must be one of 8000, 12000, 16000,
* 24000, or 48000.
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) to decode
* @retval #OPUS_OK Success or @ref opus_errorcodes
*/
internal int opus_decoder_init(int Fs, int channels)
{
SilkDecoder silk_dec;
CeltDecoder celt_dec;
int ret;
if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000)
|| (channels != 1 && channels != 2))
return OpusError.OPUS_BAD_ARG;
this.Reset();
/* Initialize SILK encoder */
silk_dec = this.SilkDecoder;
celt_dec = this.Celt_Decoder;
this.stream_channels = this.channels = channels;
this.Fs = Fs;
this.DecControl.API_sampleRate = this.Fs;
this.DecControl.nChannelsAPI = this.channels;
/* Reset decoder */
ret = DecodeAPI.silk_InitDecoder(silk_dec);
if (ret != 0) return OpusError.OPUS_INTERNAL_ERROR;
/* Initialize CELT decoder */
ret = celt_dec.celt_decoder_init(Fs, channels);
if (ret != OpusError.OPUS_OK)
return OpusError.OPUS_INTERNAL_ERROR;
celt_dec.SetSignalling(0);
this.prev_mode = 0;
this.frame_size = Fs / 400;
return OpusError.OPUS_OK;
}
private static readonly byte[] SILENCE = { 0xFF, 0xFF };
internal int opus_decode_frame(byte[] data, int data_ptr,
int len, short[] pcm, int pcm_ptr, int frame_size, int decode_fec)
{
SilkDecoder silk_dec;
CeltDecoder celt_dec;
int i, silk_ret = 0, celt_ret = 0;
EntropyCoder dec = new EntropyCoder(); // porting note: stack var
int silk_frame_size;
int pcm_silk_size;
short[] pcm_silk;
int pcm_transition_silk_size;
short[] pcm_transition_silk;
int pcm_transition_celt_size;
short[] pcm_transition_celt;
short[] pcm_transition = null;
int redundant_audio_size;
short[] redundant_audio;
int audiosize;
OpusMode mode;
int transition = 0;
int start_band;
int redundancy = 0;
int redundancy_bytes = 0;
int celt_to_silk = 0;
int c;
int F2_5, F5, F10, F20;
int[] window;
uint redundant_rng = 0;
int celt_accum;
silk_dec = this.SilkDecoder;
celt_dec = this.Celt_Decoder;
F20 = this.Fs / 50;
F10 = F20 >> 1;
F5 = F10 >> 1;
F2_5 = F5 >> 1;
if (frame_size < F2_5)
{
return OpusError.OPUS_BUFFER_TOO_SMALL;
}
/* Limit frame_size to avoid excessive stack allocations. */
frame_size = Inlines.IMIN(frame_size, this.Fs / 25 * 3);
/* Payloads of 1 (2 including ToC) or 0 trigger the PLC/DTX */
if (len <= 1)
{
data = null;
/* In that case, don't conceal more than what the ToC says */
frame_size = Inlines.IMIN(frame_size, this.frame_size);
}
if (data != null)
{
audiosize = this.frame_size;
mode = this.mode;
dec.dec_init(data, data_ptr, (uint)len);
}
else {
audiosize = frame_size;
mode = this.prev_mode;
if (mode == 0)
{
/* If we haven't got any packet yet, all we can do is return zeros */
for (i = pcm_ptr; i < pcm_ptr + (audiosize * this.channels); i++)
pcm[i] = 0;
return audiosize;
}
/* Avoids trying to run the PLC on sizes other than 2.5 (CELT), 5 (CELT),
10, or 20 (e.g. 12.5 or 30 ms). */
if (audiosize > F20)
{
do
{
int ret = opus_decode_frame(null, 0, 0, pcm, pcm_ptr, Inlines.IMIN(audiosize, F20), 0);
if (ret < 0)
{
return ret;
}
pcm_ptr += ret * this.channels;
audiosize -= ret;
} while (audiosize > 0);
return frame_size;
}
else if (audiosize < F20)
{
if (audiosize > F10)
audiosize = F10;
else if (mode != OpusMode.MODE_SILK_ONLY && audiosize > F5 && audiosize < F10)
audiosize = F5;
}
}
/* In fixed-point, we can tell CELT to do the accumulation on top of the
SILK PCM buffer. This saves some stack space. */
celt_accum = ((mode != OpusMode.MODE_CELT_ONLY) && (frame_size >= F10)) ? 1 : 0;
pcm_transition_silk_size = 0;
pcm_transition_celt_size = 0;
if (data != null && this.prev_mode > 0 && (
(mode == OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY && (this.prev_redundancy == 0))
|| (mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY))
)
{
transition = 1;
/* Decide where to allocate the stack memory for pcm_transition */
if (mode == OpusMode.MODE_CELT_ONLY)
pcm_transition_celt_size = F5 * this.channels;
else
pcm_transition_silk_size = F5 * this.channels;
}
pcm_transition_celt = new short[pcm_transition_celt_size];
if (transition != 0 && mode == OpusMode.MODE_CELT_ONLY)
{
pcm_transition = pcm_transition_celt;
opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0);
}
if (audiosize > frame_size)
{
/*fprintf(stderr, "PCM buffer too small: %d vs %d (mode = %d)\n", audiosize, frame_size, mode);*/
return OpusError.OPUS_BAD_ARG;
}
else {
frame_size = audiosize;
}
/* Don't allocate any memory when in CELT-only mode */
pcm_silk_size = (mode != OpusMode.MODE_CELT_ONLY && (celt_accum == 0)) ? Inlines.IMAX(F10, frame_size) * this.channels : 0;
pcm_silk = new short[pcm_silk_size];
/* SILK processing */
if (mode != OpusMode.MODE_CELT_ONLY)
{
int lost_flag, decoded_samples;
short[] pcm_ptr2;
int pcm_ptr2_ptr = 0;
if (celt_accum != 0)
{
pcm_ptr2 = pcm;
pcm_ptr2_ptr = pcm_ptr;
}
else
{
pcm_ptr2 = pcm_silk;
pcm_ptr2_ptr = 0;
}
if (this.prev_mode == OpusMode.MODE_CELT_ONLY)
DecodeAPI.silk_InitDecoder(silk_dec);
/* The SILK PLC cannot produce frames of less than 10 ms */
this.DecControl.payloadSize_ms = Inlines.IMAX(10, 1000 * audiosize / this.Fs);
if (data != null)
{
this.DecControl.nChannelsInternal = this.stream_channels;
if (mode == OpusMode.MODE_SILK_ONLY)
{
if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND)
{
this.DecControl.internalSampleRate = 8000;
}
else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)
{
this.DecControl.internalSampleRate = 12000;
}
else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)
{
this.DecControl.internalSampleRate = 16000;
}
else {
this.DecControl.internalSampleRate = 16000;
Inlines.OpusAssert(false);
}
}
else {
/* Hybrid mode */
this.DecControl.internalSampleRate = 16000;
}
}
lost_flag = data == null ? 1 : 2 * decode_fec;
decoded_samples = 0;
do
{
/* Call SILK decoder */
int first_frame = (decoded_samples == 0) ? 1 : 0;
silk_ret = DecodeAPI.silk_Decode(silk_dec, this.DecControl,
lost_flag, first_frame, dec, pcm_ptr2, pcm_ptr2_ptr, out silk_frame_size);
if (silk_ret != 0)
{
if (lost_flag != 0)
{
/* PLC failure should not be fatal */
silk_frame_size = frame_size;
Arrays.MemSetWithOffset<short>(pcm_ptr2, 0, pcm_ptr2_ptr, frame_size * this.channels);
}
else {
return OpusError.OPUS_INTERNAL_ERROR;
}
}
pcm_ptr2_ptr += (silk_frame_size * this.channels);
decoded_samples += silk_frame_size;
} while (decoded_samples < frame_size);
}
start_band = 0;
if (decode_fec == 0 && mode != OpusMode.MODE_CELT_ONLY && data != null
&& dec.tell() + 17 + 20 * (this.mode == OpusMode.MODE_HYBRID ? 1 : 0) <= 8 * len)
{
/* Check if we have a redundant 0-8 kHz band */
if (mode == OpusMode.MODE_HYBRID)
redundancy = dec.dec_bit_logp(12);
else
redundancy = 1;
if (redundancy != 0)
{
celt_to_silk = dec.dec_bit_logp(1);
/* redundancy_bytes will be at least two, in the non-hybrid
case due to the ec_tell() check above */
redundancy_bytes = mode == OpusMode.MODE_HYBRID ?
(int)dec.dec_uint(256) + 2 :
len - ((dec.tell() + 7) >> 3);
len -= redundancy_bytes;
/* This is a sanity check. It should never happen for a valid
packet, so the exact behaviour is not normative. */
if (len * 8 < dec.tell())
{
len = 0;
redundancy_bytes = 0;
redundancy = 0;
}
/* Shrink decoder because of raw bits */
dec.storage = (uint)(dec.storage - redundancy_bytes);
}
}
if (mode != OpusMode.MODE_CELT_ONLY)
start_band = 17;
{
int endband = 21;
switch (this.bandwidth)
{
case OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND:
endband = 13;
break;
case OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND:
case OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND:
endband = 17;
break;
case OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND:
endband = 19;
break;
case OpusBandwidth.OPUS_BANDWIDTH_FULLBAND:
endband = 21;
break;
}
celt_dec.SetEndBand(endband);
celt_dec.SetChannels(this.stream_channels);
}
if (redundancy != 0)
{
transition = 0;
pcm_transition_silk_size = 0;
}
pcm_transition_silk = new short[pcm_transition_silk_size];
if (transition != 0 && mode != OpusMode.MODE_CELT_ONLY)
{
pcm_transition = pcm_transition_silk;
opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0);
}
/* Only allocation memory for redundancy if/when needed */
redundant_audio_size = redundancy != 0 ? F5 * this.channels : 0;
redundant_audio = new short[redundant_audio_size];
/* 5 ms redundant frame for CELT->SILK*/
if (redundancy != 0 && celt_to_silk != 0)
{
celt_dec.SetStartBand(0);
celt_dec.celt_decode_with_ec(data, (data_ptr + len), redundancy_bytes,
redundant_audio, 0, F5, null, 0);
redundant_rng = celt_dec.GetFinalRange();
}
/* MUST be after PLC */
celt_dec.SetStartBand(start_band);
if (mode != OpusMode.MODE_SILK_ONLY)
{
int celt_frame_size = Inlines.IMIN(F20, frame_size);
/* Make sure to discard any previous CELT state */
if (mode != this.prev_mode && this.prev_mode > 0 && this.prev_redundancy == 0)
celt_dec.ResetState();
/* Decode CELT */
celt_ret = celt_dec.celt_decode_with_ec(decode_fec != 0 ? null : data, data_ptr,
len, pcm, pcm_ptr, celt_frame_size, dec, celt_accum);
}
else
{
if (celt_accum == 0)
{
for (i = pcm_ptr; i < (frame_size * this.channels) + pcm_ptr; i++)
pcm[i] = 0;
}
/* For hybrid -> SILK transitions, we let the CELT MDCT
do a fade-out by decoding a silence frame */
if (this.prev_mode == OpusMode.MODE_HYBRID && !(redundancy != 0 && celt_to_silk != 0 && this.prev_redundancy != 0))
{
celt_dec.SetStartBand(0);
celt_dec.celt_decode_with_ec(SILENCE, 0, 2, pcm, pcm_ptr, F2_5, null, celt_accum);
}
}
if (mode != OpusMode.MODE_CELT_ONLY && celt_accum == 0)
{
for (i = 0; i < frame_size * this.channels; i++)
pcm[pcm_ptr + i] = Inlines.SAT16(Inlines.ADD32(pcm[pcm_ptr + i], pcm_silk[i]));
}
window = celt_dec.GetMode().window;
/* 5 ms redundant frame for SILK->CELT */
if (redundancy != 0 && celt_to_silk == 0)
{
celt_dec.ResetState();
celt_dec.SetStartBand(0);
celt_dec.celt_decode_with_ec(data, data_ptr + len, redundancy_bytes, redundant_audio, 0, F5, null, 0);
redundant_rng = celt_dec.GetFinalRange();
CodecHelpers.smooth_fade(pcm, pcm_ptr + this.channels * (frame_size - F2_5), redundant_audio, this.channels * F2_5,
pcm, (pcm_ptr + this.channels * (frame_size - F2_5)), F2_5, this.channels, window, this.Fs);
}
if (redundancy != 0 && celt_to_silk != 0)
{
for (c = 0; c < this.channels; c++)
{
for (i = 0; i < F2_5; i++)
pcm[this.channels * i + c + pcm_ptr] = redundant_audio[this.channels * i + c];
}
CodecHelpers.smooth_fade(redundant_audio,(this.channels * F2_5), pcm,(pcm_ptr + (this.channels * F2_5)),
pcm, (pcm_ptr + (this.channels * F2_5)), F2_5, this.channels, window, this.Fs);
}
if (transition != 0)
{
if (audiosize >= F5)
{
for (i = 0; i < this.channels * F2_5; i++)
pcm[i] = pcm_transition[i];
CodecHelpers.smooth_fade(pcm_transition, (this.channels * F2_5), pcm, (pcm_ptr + (this.channels * F2_5)),
pcm, (pcm_ptr + (this.channels * F2_5)), F2_5,
this.channels, window, this.Fs);
}
else {
/* Not enough time to do a clean transition, but we do it anyway
This will not preserve amplitude perfectly and may introduce
a bit of temporal aliasing, but it shouldn't be too bad and
that's pretty much the best we can do. In any case, generating this
transition is pretty silly in the first place */
CodecHelpers.smooth_fade(pcm_transition, 0, pcm, pcm_ptr,
pcm, pcm_ptr, F2_5,
this.channels, window, this.Fs);
}
}
if (this.decode_gain != 0)
{
int gain;
gain = Inlines.celt_exp2(Inlines.MULT16_16_P15(((short)(0.5 + (6.48814081e-4f) * (((int)1) << (25))))/*Inlines.QCONST16(6.48814081e-4f, 25)*/, this.decode_gain));
for (i = pcm_ptr; i < pcm_ptr + (frame_size * this.channels); i++)
{
int x;
x = Inlines.MULT16_32_P16(pcm[i], gain);
pcm[i] = (short)Inlines.SATURATE(x, 32767);
}
}
if (len <= 1)
this.rangeFinal = 0;
else
this.rangeFinal = dec.rng ^ redundant_rng;
this.prev_mode = mode;
this.prev_redundancy = (redundancy != 0 && celt_to_silk == 0) ? 1 : 0;
return celt_ret < 0 ? celt_ret : audiosize;
}
internal int opus_decode_native(byte[] data, int data_ptr,
int len, short[] pcm_out, int pcm_out_ptr, int frame_size, int decode_fec,
int self_delimited, out int packet_offset, int soft_clip)
{
int i, nb_samples;
int count, offset;
byte toc;
int packet_frame_size, packet_stream_channels;
packet_offset = 0;
OpusBandwidth packet_bandwidth;
OpusMode packet_mode;
/* 48 x 2.5 ms = 120 ms */
// fixme: make sure these values can fit in an int16
short[] size = new short[48];
if (decode_fec < 0 || decode_fec > 1)
return OpusError.OPUS_BAD_ARG;
/* For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms */
if ((decode_fec != 0 || len == 0 || data == null) && frame_size % (this.Fs / 400) != 0)
return OpusError.OPUS_BAD_ARG;
if (len == 0 || data == null)
{
int pcm_count = 0;
do
{
int ret;
ret = opus_decode_frame(null, 0, 0, pcm_out, pcm_out_ptr + (pcm_count * this.channels), frame_size - pcm_count, 0);
if (ret < 0)
return ret;
pcm_count += ret;
} while (pcm_count < frame_size);
Inlines.OpusAssert(pcm_count == frame_size);
this.last_packet_duration = pcm_count;
return pcm_count;
}
else if (len < 0)
return OpusError.OPUS_BAD_ARG;
packet_mode = OpusPacketInfo.GetEncoderMode(data, data_ptr);
packet_bandwidth = OpusPacketInfo.GetBandwidth(data, data_ptr);
packet_frame_size = OpusPacketInfo.GetNumSamplesPerFrame(data, data_ptr, this.Fs);
packet_stream_channels = OpusPacketInfo.GetNumEncodedChannels(data, data_ptr);
count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, out toc, null, null, 0,
size, 0, out offset, out packet_offset);
if (count < 0)
return count;
data_ptr += offset;
if (decode_fec != 0)
{
int dummy;
int duration_copy;
int ret;
/* If no FEC can be present, run the PLC (recursive call) */
if (frame_size < packet_frame_size || packet_mode == OpusMode.MODE_CELT_ONLY || this.mode == OpusMode.MODE_CELT_ONLY)
return opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size, 0, 0, out dummy, soft_clip);
/* Otherwise, run the PLC on everything except the size for which we might have FEC */
duration_copy = this.last_packet_duration;
if (frame_size - packet_frame_size != 0)
{
ret = opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size - packet_frame_size, 0, 0, out dummy, soft_clip);
if (ret < 0)
{
this.last_packet_duration = duration_copy;
return ret;
}
Inlines.OpusAssert(ret == frame_size - packet_frame_size);
}
/* Complete with FEC */
this.mode = packet_mode;
this.bandwidth = packet_bandwidth;
this.frame_size = packet_frame_size;
this.stream_channels = packet_stream_channels;
ret = opus_decode_frame(data, data_ptr, size[0], pcm_out, pcm_out_ptr + (this.channels * (frame_size - packet_frame_size)),
packet_frame_size, 1);
if (ret < 0)
return ret;
else {
this.last_packet_duration = frame_size;
return frame_size;
}
}
if (count * packet_frame_size > frame_size)
return OpusError.OPUS_BUFFER_TOO_SMALL;
/* Update the state as the last step to avoid updating it on an invalid packet */
this.mode = packet_mode;
this.bandwidth = packet_bandwidth;
this.frame_size = packet_frame_size;
this.stream_channels = packet_stream_channels;
nb_samples = 0;
for (i = 0; i < count; i++)
{
int ret;
ret = opus_decode_frame(data, data_ptr, size[i], pcm_out, pcm_out_ptr + (nb_samples * this.channels), frame_size - nb_samples, 0);
if (ret < 0)
return ret;
Inlines.OpusAssert(ret == packet_frame_size);
data_ptr += size[i];
nb_samples += ret;
}
this.last_packet_duration = nb_samples;
return nb_samples;
}
/// <summary>
/// Decodes an Opus packet.
/// </summary>
/// <param name="in_data">The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled)</param>
/// <param name="in_data_offset">The offset to use when reading the input payload. Usually 0</param>
/// <param name="len">The number of bytes in the payload</param>
/// <param name="out_pcm">A buffer to put the output PCM. The output size is (# of samples) * (# of channels).
/// You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need
/// exact sizing. Otherwise, the minimum safe buffer size is 5760 samples</param>
/// <param name="out_pcm_offset">The offset to use when writing to the output buffer</param>
/// <param name="frame_size">The number of samples (per channel) of available space in the output PCM buf.
/// If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will
/// not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true),
/// then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will
/// not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must*
/// be a multiple of 10 ms.</param>
/// <param name="decode_fec">Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet
/// recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not
/// available in this packet, the decoder will simply generate a best-effort recreation of the lost packet.</param>
/// <returns>The number of decoded samples</returns>
public int Decode(byte[] in_data, int in_data_offset,
int len, short[] out_pcm, int out_pcm_offset, int frame_size, bool decode_fec = false)
{
if (frame_size <= 0)
{
throw new ArgumentException("Frame size must be > 0");
}
try
{
int dummy;
int ret = opus_decode_native(in_data, in_data_offset, len, out_pcm, out_pcm_offset, frame_size, decode_fec ? 1 : 0, 0, out dummy, 0);
if (ret < 0)
{
// An error happened; report it
if (ret == OpusError.OPUS_BAD_ARG)
throw new ArgumentException("OPUS_BAD_ARG while decoding");
throw new OpusException("An error occurred during decoding", ret);
}
return ret;
}
catch (ArgumentException e)
{
throw new OpusException("Internal error during decoding: " + e.Message);
}
}
/// <summary>
/// Decodes an Opus packet, putting the output data into a floating-point buffer.
/// </summary>
/// <param name="in_data">The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled)</param>
/// <param name="in_data_offset">The offset to use when reading the input payload. Usually 0</param>
/// <param name="len">The number of bytes in the payload</param>
/// <param name="out_pcm">A buffer to put the output PCM. The output size is (# of samples) * (# of channels).
/// You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need
/// exact sizing. Otherwise, the minimum safe buffer size is 5760 samples</param>
/// <param name="out_pcm_offset">The offset to use when writing to the output buffer</param>
/// <param name="frame_size">The number of samples (per channel) of available space in the output PCM buf.
/// If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will
/// not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true),
/// then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will
/// not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must*
/// be a multiple of 10 ms.</param>
/// <param name="decode_fec">Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet
/// recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not
/// available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. In that case,
/// the length of frame_size must be EXACTLY the length of the audio that was lost, or else the decoder will be in an inconsistent state.</param>
/// <returns>The number of decoded samples (per channel)</returns>
public int Decode(byte[] in_data, int in_data_offset,
int len, float[] out_pcm, int out_pcm_offset, int frame_size, bool decode_fec = false)
{
short[] output;
int ret, i;
int nb_samples;
if (frame_size <= 0)
{
throw new ArgumentException("Frame size must be > 0");
}
if (in_data != null && len > 0 && !decode_fec)
{
nb_samples = OpusPacketInfo.GetNumSamples(this, in_data, in_data_offset, len);
if (nb_samples > 0)
frame_size = Inlines.IMIN(frame_size, nb_samples);
else
throw new OpusException("An invalid packet was provided (unable to parse # of samples)");
}
output = new short[frame_size * this.channels];
try
{
int dummy;
ret = opus_decode_native(in_data, in_data_offset, len, output, 0, frame_size, decode_fec ? 1 : 0, 0, out dummy, 0);
if (ret < 0)
{
// An error happened; report it
if (ret == OpusError.OPUS_BAD_ARG)
throw new ArgumentException("OPUS_BAD_ARG when decoding");
throw new OpusException("An error occurred during decoding", ret);
}
if (ret > 0)
{
for (i = 0; i < ret * this.channels; i++)
out_pcm[out_pcm_offset + i] = (1.0f / 32768.0f) * (output[i]);
}
return ret;
}
catch (ArgumentException e)
{
throw new OpusException("Internal error during decoding: " + e.Message);
}
}
/// <summary>
/// Gets the encoded bandwidth of the last packet decoded. This may be lower than the actual decoding sample rate,
/// and is only an indicator of the encoded audio's quality
/// </summary>
public OpusBandwidth Bandwidth
{
get
{
return bandwidth;
}
}
public uint FinalRange
{
get
{
return rangeFinal;
}
}
/// <summary>
/// Gets the sample rate that this decoder decodes to. Always constant for the lifetime of the decoder
/// </summary>
public int SampleRate
{
get
{
return Fs;
}
}
/// <summary>
/// Gets the number of channels that this decoder decodes to. Always constant for the lifetime of the decoder.
/// </summary>
public int NumChannels
{
get
{
return channels;
}
}
/// <summary>
/// Gets the last estimated pitch value of the decoded audio
/// </summary>
public int Pitch
{
get
{
if (prev_mode == OpusMode.MODE_CELT_ONLY)
{
return Celt_Decoder.GetPitch();
}
else
return DecControl.prevPitchLag;
}
}
/// <summary>
/// Gets or sets the gain (Q8) to use in decoding
/// </summary>
public int Gain
{
get
{
return decode_gain;
}
set
{
if (value < -32768 || value > 32767)
{
throw new ArgumentException("Gain must be within the range of a signed int16");
}
decode_gain = value;
}
}
/// <summary>
/// Gets the duration of the last packet, in PCM samples per channel
/// </summary>
public int LastPacketDuration
{
get
{
return last_packet_duration;
}
}
/// <summary>
/// Resets all buffers and prepares this decoder to process a fresh (unrelated) stream
/// </summary>
public void ResetState()
{
PartialReset();
Celt_Decoder.ResetState();
DecodeAPI.silk_InitDecoder(SilkDecoder);
stream_channels = channels;
frame_size = Fs / 400;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,438 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using Concentus.Structs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus.Structs
{
public class OpusMSDecoder
{
internal ChannelLayout layout = new ChannelLayout();
internal OpusDecoder[] decoders = null;
private OpusMSDecoder(int nb_streams, int nb_coupled_streams)
{
decoders = new OpusDecoder[nb_streams];
for (int c = 0; c < nb_streams; c++)
decoders[c] = new OpusDecoder();
}
#region API functions
internal int opus_multistream_decoder_init(
int Fs,
int channels,
int streams,
int coupled_streams,
byte[] mapping
)
{
int i, ret;
int decoder_ptr = 0;
if ((channels > 255) || (channels < 1) || (coupled_streams > streams) ||
(streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams))
throw new ArgumentException("Invalid channel or coupled stream count");
this.layout.nb_channels = channels;
this.layout.nb_streams = streams;
this.layout.nb_coupled_streams = coupled_streams;
for (i = 0; i < this.layout.nb_channels; i++)
this.layout.mapping[i] = mapping[i];
if (OpusMultistream.validate_layout(this.layout) == 0)
throw new ArgumentException("Invalid surround channel layout");
for (i = 0; i < this.layout.nb_coupled_streams; i++)
{
ret = this.decoders[decoder_ptr].opus_decoder_init(Fs, 2);
if (ret != OpusError.OPUS_OK) return ret;
decoder_ptr++;
}
for (; i < this.layout.nb_streams; i++)
{
ret = this.decoders[decoder_ptr].opus_decoder_init(Fs, 1);
if (ret != OpusError.OPUS_OK) return ret;
decoder_ptr++;
}
return OpusError.OPUS_OK;
}
/// <summary>
/// Creates a new multichannel decoder
/// </summary>
/// <param name="Fs"></param>
/// <param name="channels"></param>
/// <param name="streams"></param>
/// <param name="coupled_streams"></param>
/// <param name="mapping">A mapping family (just use { 0, 1, 255 })</param>
/// <returns></returns>
public OpusMSDecoder(
int Fs,
int channels,
int streams,
int coupled_streams,
byte[] mapping) : this(streams, coupled_streams)
{
int ret;
if ((channels > 255) || (channels < 1) || (coupled_streams > streams) ||
(streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams))
{
throw new ArgumentException("Invalid channel / stream configuration");
}
ret = this.opus_multistream_decoder_init(Fs, channels, streams, coupled_streams, mapping);
if (ret != OpusError.OPUS_OK)
{
if (ret == OpusError.OPUS_BAD_ARG)
throw new ArgumentException("Bad argument while creating MS decoder");
throw new OpusException("Could not create MS decoder", ret);
}
}
internal delegate void opus_copy_channel_out_func<T>(
T[] dst,
int dst_ptr,
int dst_stride,
int dst_channel,
short[] src,
int src_ptr,
int src_stride,
int frame_size
);
internal static int opus_multistream_packet_validate(byte[] data, int data_ptr,
int len, int nb_streams, int Fs)
{
int s;
int count;
byte toc;
short[] size = new short[48];
int samples = 0;
int packet_offset;
int dummy;
for (s = 0; s < nb_streams; s++)
{
int tmp_samples;
if (len <= 0)
return OpusError.OPUS_INVALID_PACKET;
count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, (s != nb_streams - 1) ? 1 : 0, out toc, null, null, 0,
size, 0, out dummy, out packet_offset);
if (count < 0)
return count;
tmp_samples = OpusPacketInfo.GetNumSamples(data, data_ptr, packet_offset, Fs);
if (s != 0 && samples != tmp_samples)
return OpusError.OPUS_INVALID_PACKET;
samples = tmp_samples;
data_ptr += packet_offset;
len -= packet_offset;
}
return samples;
}
internal int opus_multistream_decode_native<T>(
byte[] data,
int data_ptr,
int len,
T[] pcm,
int pcm_ptr,
opus_copy_channel_out_func<T> copy_channel_out,
int frame_size,
int decode_fec,
int soft_clip
)
{
int Fs;
int s, c;
int decoder_ptr;
int do_plc = 0;
short[] buf;
/* Limit frame_size to avoid excessive stack allocations. */
Fs = this.SampleRate;
frame_size = Inlines.IMIN(frame_size, Fs / 25 * 3);
buf = new short[2 * frame_size];
decoder_ptr = 0;
if (len == 0)
do_plc = 1;
if (len < 0)
{
return OpusError.OPUS_BAD_ARG;
}
if (do_plc == 0 && len < 2 * this.layout.nb_streams - 1)
{
return OpusError.OPUS_INVALID_PACKET;
}
if (do_plc == 0)
{
int ret = opus_multistream_packet_validate(data, data_ptr, len, this.layout.nb_streams, Fs);
if (ret < 0)
{
return ret;
}
else if (ret > frame_size)
{
return OpusError.OPUS_BUFFER_TOO_SMALL;
}
}
for (s = 0; s < this.layout.nb_streams; s++)
{
OpusDecoder dec;
int ret;
dec = this.decoders[decoder_ptr++];
if (do_plc == 0 && len <= 0)
{
return OpusError.OPUS_INTERNAL_ERROR;
}
int packet_offset;
ret = dec.opus_decode_native(
data, data_ptr, len, buf, 0, frame_size, decode_fec,
(s != this.layout.nb_streams - 1) ? 1 : 0, out packet_offset, soft_clip);
data_ptr += packet_offset;
len -= packet_offset;
if (ret <= 0)
{
return ret;
}
frame_size = ret;
if (s < this.layout.nb_coupled_streams)
{
int chan, prev;
prev = -1;
/* Copy "left" audio to the channel(s) where it belongs */
while ((chan = OpusMultistream.get_left_channel(this.layout, s, prev)) != -1)
{
copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, chan,
buf, 0, 2, frame_size);
prev = chan;
}
prev = -1;
/* Copy "right" audio to the channel(s) where it belongs */
while ((chan = OpusMultistream.get_right_channel(this.layout, s, prev)) != -1)
{
copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, chan,
buf, 1, 2, frame_size);
prev = chan;
}
}
else {
int chan, prev;
prev = -1;
/* Copy audio to the channel(s) where it belongs */
while ((chan = OpusMultistream.get_mono_channel(this.layout, s, prev)) != -1)
{
copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, chan,
buf, 0, 1, frame_size);
prev = chan;
}
}
}
/* Handle muted channels */
for (c = 0; c < this.layout.nb_channels; c++)
{
if (this.layout.mapping[c] == 255)
{
copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, c,
null, 0, 0, frame_size);
}
}
return frame_size;
}
internal static void opus_copy_channel_out_float(
float[] dst,
int dst_ptr,
int dst_stride,
int dst_channel,
short[] src,
int src_ptr,
int src_stride,
int frame_size
)
{
int i;
if (src != null)
{
for (i = 0; i < frame_size; i++)
dst[i * dst_stride + dst_channel + dst_ptr] = (1 / 32768.0f) * src[i * src_stride + src_ptr];
}
else
{
for (i = 0; i < frame_size; i++)
dst[i * dst_stride + dst_channel + dst_ptr] = 0;
}
}
internal static void opus_copy_channel_out_short(
short[] dst,
int dst_ptr,
int dst_stride,
int dst_channel,
short[] src,
int src_ptr,
int src_stride,
int frame_size
)
{
int i;
if (src != null)
{
for (i = 0; i < frame_size; i++)
dst[i * dst_stride + dst_channel + dst_ptr] = src[i * src_stride + src_ptr];
}
else
{
for (i = 0; i < frame_size; i++)
dst[i * dst_stride + dst_channel + dst_ptr] = 0;
}
}
public int DecodeMultistream(
byte[] data,
int data_offset,
int len,
short[] out_pcm,
int out_pcm_offset,
int frame_size,
int decode_fec
)
{
return opus_multistream_decode_native<short>(data, data_offset, len,
out_pcm, out_pcm_offset, opus_copy_channel_out_short, frame_size, decode_fec, 0);
}
public int DecodeMultistream(byte[] data, int data_offset,
int len, float[] out_pcm, int out_pcm_offset, int frame_size, int decode_fec)
{
return opus_multistream_decode_native<float>(data, data_offset, len,
out_pcm, out_pcm_offset, opus_copy_channel_out_float, frame_size, decode_fec, 0);
}
#endregion
#region Getters and setters
public OpusBandwidth Bandwidth
{
get
{
if (decoders == null || decoders.Length == 0)
throw new InvalidOperationException("Decoder not initialized");
return decoders[0].Bandwidth;
}
}
public int SampleRate
{
get
{
if (decoders == null || decoders.Length == 0)
throw new InvalidOperationException("Decoder not initialized");
return decoders[0].SampleRate;
}
}
public int Gain
{
get
{
if (decoders == null || decoders.Length == 0)
return OpusError.OPUS_INVALID_STATE;
return decoders[0].Gain;
}
set
{
for (int s = 0; s < layout.nb_streams; s++)
{
decoders[s].Gain = value;
}
}
}
public int LastPacketDuration
{
get
{
if (decoders == null || decoders.Length == 0)
return OpusError.OPUS_INVALID_STATE;
return decoders[0].LastPacketDuration;
}
}
public uint FinalRange
{
get
{
uint value = 0;
for (int s = 0; s < layout.nb_streams; s++)
{
value ^= decoders[s].FinalRange;
}
return value;
}
}
public void ResetState()
{
for (int s = 0; s < layout.nb_streams; s++)
{
decoders[s].ResetState();
}
}
public OpusDecoder GetMultistreamDecoderState(int streamId)
{
return decoders[streamId];
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,470 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus.Structs
{
public class OpusPacketInfo
{
/// <summary>
/// The Table of Contents byte for this packet. Contains info about modes, frame length, etc.
/// </summary>
public readonly byte TOCByte;
/// <summary>
/// The list of subframes in this packet
/// </summary>
public readonly IList<byte[]> Frames;
/// <summary>
/// The index of the start of the payload within the packet
/// </summary>
public readonly int PayloadOffset;
private OpusPacketInfo(byte toc, IList<byte[]> frames, int payloadOffset)
{
TOCByte = toc;
Frames = frames;
PayloadOffset = payloadOffset;
}
/// <summary>
/// Parse an opus packet into a packetinfo object containing one or more frames.
/// Opus_decode will perform this operation internally so most applications do
/// not need to use this function.
/// </summary>
/// <param name="packet">The packet data to be parsed</param>
/// <param name="packet_offset">The index of the beginning of the packet in the data array (usually 0)</param>
/// <param name="len">The packet's length</param>
/// <returns>A parsed packet info struct</returns>
public static OpusPacketInfo ParseOpusPacket(byte[] packet, int packet_offset, int len)
{
// Find the number of frames first
int numFrames = GetNumFrames(packet, packet_offset, len);
int payload_offset;
byte out_toc;
byte[][] frames = new byte[numFrames][];
int[] frames_ptrs = new int[numFrames];
short[] size = new short[numFrames];
int packetOffset;
int error = opus_packet_parse_impl(packet, packet_offset, len, 0, out out_toc, frames, frames_ptrs, 0, size, 0, out payload_offset, out packetOffset);
if (error < 0)
{
throw new OpusException("An error occurred while parsing the packet", error);
}
IList<byte[]> copiedFrames = new List<byte[]>();
for (int c = 0; c < numFrames; c++)
{
byte[] nextFrame = new byte[size[c]];
Array.Copy(frames[c], frames_ptrs[c], nextFrame, 0, nextFrame.Length);
copiedFrames.Add(nextFrame);
}
return new OpusPacketInfo(out_toc, copiedFrames, payload_offset);
}
/// <summary>
/// Gets the number of samples per frame from an Opus packet.
/// </summary>
/// <param name="packet">Opus packet. This must contain at least one byte of data</param>
/// <param name="Fs">Sampling rate in Hz. This must be a multiple of 400, or inaccurate results will be returned.</param>
/// <returns>Number of samples per frame</returns>
public static int GetNumSamplesPerFrame(byte[] packet, int packet_offset, int Fs)
{
int audiosize;
if ((packet[packet_offset] & 0x80) != 0)
{
audiosize = ((packet[packet_offset] >> 3) & 0x3);
audiosize = (Fs << audiosize) / 400;
}
else if ((packet[packet_offset] & 0x60) == 0x60)
{
audiosize = ((packet[packet_offset] & 0x08) != 0) ? Fs / 50 : Fs / 100;
}
else
{
audiosize = ((packet[packet_offset] >> 3) & 0x3);
if (audiosize == 3)
audiosize = Fs * 60 / 1000;
else
audiosize = (Fs << audiosize) / 100;
}
return audiosize;
}
/// <summary>
/// Gets the encoded bandwidth of an Opus packet. Note that you are not forced to decode at this bandwidth
/// </summary>
/// <param name="packet">An Opus packet (must be at least 1 byte)</param>.
/// <returns>An OpusBandwidth value</returns>
public static OpusBandwidth GetBandwidth(byte[] packet, int packet_offset)
{
OpusBandwidth bandwidth;
if ((packet[packet_offset] & 0x80) != 0)
{
bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND + ((packet[packet_offset] >> 5) & 0x3);
if (bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)
bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND;
}
else if ((packet[packet_offset] & 0x60) == 0x60)
{
bandwidth = ((packet[packet_offset] & 0x10) != 0) ? OpusBandwidth.OPUS_BANDWIDTH_FULLBAND :
OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND;
}
else {
bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND + ((packet[packet_offset] >> 5) & 0x3);
}
return bandwidth;
}
/// <summary>
/// Gets the number of encoded channels of an Opus packet. Note that you are not forced to decode with this channel count.
/// </summary>
/// <param name="packet">An opus packet (must be at least 1 byte)</param>
/// <returns>The number of channels</returns>
public static int GetNumEncodedChannels(byte[] packet, int packet_offset)
{
return ((packet[packet_offset] & 0x4) != 0) ? 2 : 1;
}
/// <summary>
/// Gets the number of frames in an Opus packet.
/// </summary>
/// <param name="packet">An Opus packet</param>
/// <param name="len">The packet's length (must be at least 1)</param>
/// <returns>The number of frames in the packet</returns>
public static int GetNumFrames(byte[] packet, int packet_offset, int len)
{
int count;
if (len < 1)
return OpusError.OPUS_BAD_ARG;
count = packet[packet_offset] & 0x3;
if (count == 0)
return 1;
else if (count != 3)
return 2;
else if (len < 2)
return OpusError.OPUS_INVALID_PACKET;
else
return packet[packet_offset + 1] & 0x3F;
}
/// <summary>
/// Gets the number of samples of an Opus packet.
/// </summary>
/// <param name="packet">An Opus packet</param>
/// <param name="len">The packet's length</param>
/// <param name="Fs">The decoder's sampling rate in Hz. This must be a multiple of 400</param>
/// <returns>The size of the PCM samples that this packet will be decoded to at the specified sample rate</returns>
public static int GetNumSamples(byte[] packet, int packet_offset, int len,
int Fs)
{
int samples;
int count = GetNumFrames(packet, packet_offset, len);
if (count < 0)
return count;
samples = count * GetNumSamplesPerFrame(packet, packet_offset, Fs);
/* Can't have more than 120 ms */
if (samples * 25 > Fs * 3)
return OpusError.OPUS_INVALID_PACKET;
else
return samples;
}
/// <summary>
/// Gets the number of samples of an Opus packet.
/// </summary>
/// <param name="dec">Your current decoder state</param>
/// <param name="packet">An Opus packet</param>
/// <param name="len">The packet's length</param>
/// <returns>The size of the PCM samples that this packet will be decoded to by the specified decoder</returns>
public static int GetNumSamples(OpusDecoder dec,
byte[] packet, int packet_offset, int len)
{
return GetNumSamples(packet, packet_offset, len, dec.Fs);
}
/// <summary>
/// Gets the mode that was used to encode this packet.
/// Normally there is nothing you can really do with this, other than debugging.
/// </summary>
/// <param name="packet">An Opus packet</param>
/// <returns>The OpusMode used by the encoder</returns>
public static OpusMode GetEncoderMode(byte[] packet, int packet_offset)
{
OpusMode mode;
if ((packet[packet_offset] & 0x80) != 0)
{
mode = OpusMode.MODE_CELT_ONLY;
}
else if ((packet[packet_offset] & 0x60) == 0x60)
{
mode = OpusMode.MODE_HYBRID;
}
else {
mode = OpusMode.MODE_SILK_ONLY;
}
return mode;
}
internal static int encode_size(int size, byte[] data, int data_ptr)
{
if (size < 252)
{
data[data_ptr] = (byte)size;
return 1;
}
else {
data[data_ptr] = (byte)(252 + (size & 0x3));
data[data_ptr + 1] = (byte)((size - (int)data[data_ptr]) >> 2);
return 2;
}
}
internal static int parse_size(byte[] data, int data_ptr, int len, BoxedValueShort size)
{
if (len < 1)
{
size.Val = -1;
return -1;
}
else if (data[data_ptr] < 252)
{
size.Val = data[data_ptr];
return 1;
}
else if (len < 2)
{
size.Val = -1;
return -1;
}
else {
size.Val = (short)(4 * data[data_ptr + 1] + data[data_ptr]);
return 2;
}
}
internal static int opus_packet_parse_impl(byte[] data, int data_ptr, int len,
int self_delimited, out byte out_toc,
byte[][] frames, int[] frames_ptrs, int frames_ptr, short[] sizes, int sizes_ptr,
out int payload_offset, out int packet_offset)
{
int i, bytes;
int count;
int cbr;
byte ch, toc;
int framesize;
int last_size;
int pad = 0;
int data0 = data_ptr;
out_toc = 0;
payload_offset = 0;
packet_offset = 0;
if (sizes == null || len < 0)
return OpusError.OPUS_BAD_ARG;
if (len == 0)
return OpusError.OPUS_INVALID_PACKET;
framesize = GetNumSamplesPerFrame(data, data_ptr, 48000);
cbr = 0;
toc = data[data_ptr++];
len--;
last_size = len;
switch (toc & 0x3)
{
/* One frame */
case 0:
count = 1;
break;
/* Two CBR frames */
case 1:
count = 2;
cbr = 1;
if (self_delimited == 0)
{
if ((len & 0x1) != 0)
return OpusError.OPUS_INVALID_PACKET;
last_size = len / 2;
/* If last_size doesn't fit in size[0], we'll catch it later */
sizes[sizes_ptr] = (short)last_size;
}
break;
/* Two VBR frames */
case 2:
count = 2;
BoxedValueShort boxed_size = new BoxedValueShort(sizes[sizes_ptr]);
bytes = parse_size(data, data_ptr, len, boxed_size);
sizes[sizes_ptr] = boxed_size.Val;
len -= bytes;
if (sizes[sizes_ptr] < 0 || sizes[sizes_ptr] > len)
return OpusError.OPUS_INVALID_PACKET;
data_ptr += bytes;
last_size = len - sizes[sizes_ptr];
break;
/* Multiple CBR/VBR frames (from 0 to 120 ms) */
default: /*case 3:*/
if (len < 1)
return OpusError.OPUS_INVALID_PACKET;
/* Number of frames encoded in bits 0 to 5 */
ch = data[data_ptr++];
count = ch & 0x3F;
if (count <= 0 || framesize * count > 5760)
return OpusError.OPUS_INVALID_PACKET;
len--;
/* Padding flag is bit 6 */
if ((ch & 0x40) != 0)
{
int p;
do
{
int tmp;
if (len <= 0)
return OpusError.OPUS_INVALID_PACKET;
p = data[data_ptr++];
len--;
tmp = p == 255 ? 254 : p;
len -= tmp;
pad += tmp;
} while (p == 255);
}
if (len < 0)
return OpusError.OPUS_INVALID_PACKET;
/* VBR flag is bit 7 */
cbr = (ch & 0x80) != 0 ? 0 : 1;
if (cbr == 0)
{
/* VBR case */
last_size = len;
for (i = 0; i < count - 1; i++)
{
boxed_size = new BoxedValueShort(sizes[sizes_ptr + i]);
bytes = parse_size(data, data_ptr, len, boxed_size);
sizes[sizes_ptr + i] = boxed_size.Val;
len -= bytes;
if (sizes[sizes_ptr + i] < 0 || sizes[sizes_ptr + i] > len)
return OpusError.OPUS_INVALID_PACKET;
data_ptr += bytes;
last_size -= bytes + sizes[sizes_ptr + i];
}
if (last_size < 0)
return OpusError.OPUS_INVALID_PACKET;
}
else if (self_delimited == 0)
{
/* CBR case */
last_size = len / count;
if (last_size * count != len)
return OpusError.OPUS_INVALID_PACKET;
for (i = 0; i < count - 1; i++)
sizes[sizes_ptr + i] = (short)last_size;
}
break;
}
/* Self-delimited framing has an extra size for the last frame. */
if (self_delimited != 0)
{
BoxedValueShort boxed_size = new BoxedValueShort(sizes[sizes_ptr + count - 1]);
bytes = parse_size(data, data_ptr, len, boxed_size);
sizes[sizes_ptr + count - 1] = boxed_size.Val;
len -= bytes;
if (sizes[sizes_ptr + count - 1] < 0 || sizes[sizes_ptr + count - 1] > len)
return OpusError.OPUS_INVALID_PACKET;
data_ptr += bytes;
/* For CBR packets, apply the size to all the frames. */
if (cbr != 0)
{
if (sizes[sizes_ptr + count - 1] * count > len)
return OpusError.OPUS_INVALID_PACKET;
for (i = 0; i < count - 1; i++)
sizes[sizes_ptr + i] = sizes[sizes_ptr + count - 1];
}
else if (bytes + sizes[sizes_ptr + count - 1] > last_size)
return OpusError.OPUS_INVALID_PACKET;
}
else
{
/* Because it's not encoded explicitly, it's possible the size of the
last packet (or all the packets, for the CBR case) is larger than
1275. Reject them here.*/
if (last_size > 1275)
return OpusError.OPUS_INVALID_PACKET;
sizes[sizes_ptr + count - 1] = (short)last_size;
}
payload_offset = (int)(data_ptr - data0);
for (i = 0; i < count; i++)
{
if (frames != null)
frames[frames_ptr + i] = data;
if (frames_ptrs != null)
frames_ptrs[frames_ptr + i] = data_ptr;
data_ptr += sizes[sizes_ptr + i];
}
packet_offset = pad + (int)(data_ptr - data0);
out_toc = toc;
return count;
}
// used internally
//internal static int opus_packet_parse(byte[] data, int data_ptr, int len,
// out byte out_toc, Pointer<Pointer<byte>> frames,
// short[] size, int size_ptr, out int payload_offset)
//{
// int dummy;
// return OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, 0, out out_toc,
// frames, size, size_ptr, out payload_offset, out dummy);
//}
}
}

View File

@@ -0,0 +1,573 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus.Structs
{
public class OpusRepacketizer
{
internal byte toc = 0;
internal int nb_frames = 0;
internal readonly byte[][] frames = new byte[48][];
internal readonly int[] frames_ptrs = new int[48];
internal readonly short[] len = new short[48];
internal int framesize = 0;
/** (Re)initializes a previously allocated repacketizer state.
* The state must be at least the size returned by opus_repacketizer_get_size().
* This can be used for applications which use their own allocator instead of
* malloc().
* It must also be called to reset the queue of packets waiting to be
* repacketized, which is necessary if the maximum packet duration of 120 ms
* is reached or if you wish to submit packets with a different Opus
* configuration (coding mode, audio bandwidth, frame size, or channel count).
* Failure to do so will prevent a new packet from being added with
* opus_repacketizer_cat().
* @see opus_repacketizer_create
* @see opus_repacketizer_get_size
* @see opus_repacketizer_cat
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state to
* (re)initialize.
*/
public void Reset()
{
this.nb_frames = 0;
}
/// <summary>
/// Creates a new repacketizer
/// </summary>
public OpusRepacketizer()
{
this.Reset();
}
internal int opus_repacketizer_cat_impl(byte[] data, int data_ptr, int len, int self_delimited)
{
byte dummy_toc;
int dummy_offset;
int curr_nb_frames, ret;
/* Set of check ToC */
if (len < 1)
{
return OpusError.OPUS_INVALID_PACKET;
}
if (this.nb_frames == 0)
{
this.toc = data[data_ptr];
this.framesize = OpusPacketInfo.GetNumSamplesPerFrame(data, data_ptr, 8000);
}
else if ((this.toc & 0xFC) != (data[data_ptr] & 0xFC))
{
/*fprintf(stderr, "toc mismatch: 0x%x vs 0x%x\n", rp.toc, data[0]);*/
return OpusError.OPUS_INVALID_PACKET;
}
curr_nb_frames = OpusPacketInfo.GetNumFrames(data, data_ptr, len);
if (curr_nb_frames < 1)
return OpusError.OPUS_INVALID_PACKET;
/* Check the 120 ms maximum packet size */
if ((curr_nb_frames + this.nb_frames) * this.framesize > 960)
{
return OpusError.OPUS_INVALID_PACKET;
}
ret = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, out dummy_toc, this.frames, this.frames_ptrs, this.nb_frames, this.len, this.nb_frames, out dummy_offset, out dummy_offset);
if (ret < 1) return ret;
this.nb_frames += curr_nb_frames;
return OpusError.OPUS_OK;
}
/** opus_repacketizer_cat. Add a packet to the current repacketizer state.
* This packet must match the configuration of any packets already submitted
* for repacketization since the last call to opus_repacketizer_init().
* This means that it must have the same coding mode, audio bandwidth, frame
* size, and channel count.
* This can be checked in advance by examining the top 6 bits of the first
* byte of the packet, and ensuring they match the top 6 bits of the first
* byte of any previously submitted packet.
* The total duration of audio in the repacketizer state also must not exceed
* 120 ms, the maximum duration of a single packet, after adding this packet.
*
* The contents of the current repacketizer state can be extracted into new
* packets using opus_repacketizer_out() or opus_repacketizer_out_range().
*
* In order to add a packet with a different configuration or to add more
* audio beyond 120 ms, you must clear the repacketizer state by calling
* opus_repacketizer_init().
* If a packet is too large to add to the current repacketizer state, no part
* of it is added, even if it contains multiple frames, some of which might
* fit.
* If you wish to be able to add parts of such packets, you should first use
* another repacketizer to split the packet into pieces and add them
* individually.
* @see opus_repacketizer_out_range
* @see opus_repacketizer_out
* @see opus_repacketizer_init
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state to which to
* add the packet.
* @param[in] data <tt>const unsigned char*</tt>: The packet data.
* The application must ensure
* this pointer remains valid
* until the next call to
* opus_repacketizer_init() or
* opus_repacketizer_destroy().
* @param len <tt>opus_int32</tt>: The number of bytes in the packet data.
* @returns An error code indicating whether or not the operation succeeded.
* @retval #OPUS_OK The packet's contents have been added to the repacketizer
* state.
* @retval #OPUS_INVALID_PACKET The packet did not have a valid TOC sequence,
* the packet's TOC sequence was not compatible
* with previously submitted packets (because
* the coding mode, audio bandwidth, frame size,
* or channel count did not match), or adding
* this packet would increase the total amount of
* audio stored in the repacketizer state to more
* than 120 ms.
*/
public int AddPacket(byte[] data, int data_offset, int len)
{
return opus_repacketizer_cat_impl(data, data_offset, len, 0);
}
/** Return the total number of frames contained in packet data submitted to
* the repacketizer state so far via opus_repacketizer_cat() since the last
* call to opus_repacketizer_init() or opus_repacketizer_create().
* This defines the valid range of packets that can be extracted with
* opus_repacketizer_out_range() or opus_repacketizer_out().
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state containing the
* frames.
* @returns The total number of frames contained in the packet data submitted
* to the repacketizer state.
*/
public int GetNumFrames()
{
return this.nb_frames;
}
internal int opus_repacketizer_out_range_impl(int begin, int end,
byte[] data, int data_ptr, int maxlen, int self_delimited, int pad)
{
int i, count;
int tot_size;
int ptr;
if (begin < 0 || begin >= end || end > this.nb_frames)
{
/*fprintf(stderr, "%d %d %d\n", begin, end, rp.nb_frames);*/
return OpusError.OPUS_BAD_ARG;
}
count = end - begin;
if (self_delimited != 0)
tot_size = 1 + (this.len[count - 1] >= 252 ? 1 : 0);
else
tot_size = 0;
ptr = data_ptr;
if (count == 1)
{
/* Code 0 */
tot_size += this.len[0] + 1;
if (tot_size > maxlen)
return OpusError.OPUS_BUFFER_TOO_SMALL;
data[ptr++] = (byte)(this.toc & 0xFC);
}
else if (count == 2)
{
if (this.len[1] == this.len[0])
{
/* Code 1 */
tot_size += 2 * this.len[0] + 1;
if (tot_size > maxlen)
return OpusError.OPUS_BUFFER_TOO_SMALL;
data[ptr++] = (byte)((this.toc & 0xFC) | 0x1);
}
else {
/* Code 2 */
tot_size += this.len[0] + this.len[1] + 2 + (this.len[0] >= 252 ? 1 : 0);
if (tot_size > maxlen)
return OpusError.OPUS_BUFFER_TOO_SMALL;
data[ptr++] = (byte)((this.toc & 0xFC) | 0x2);
ptr += OpusPacketInfo.encode_size(this.len[0], data, ptr);
}
}
if (count > 2 || (pad != 0 && tot_size < maxlen))
{
/* Code 3 */
int vbr;
int pad_amount = 0;
/* Restart the process for the padding case */
ptr = data_ptr;
if (self_delimited != 0)
tot_size = 1 + (this.len[count - 1] >= 252 ? 1 : 0);
else
tot_size = 0;
vbr = 0;
for (i = 1; i < count; i++)
{
if (this.len[i] != this.len[0])
{
vbr = 1;
break;
}
}
if (vbr != 0)
{
tot_size += 2;
for (i = 0; i < count - 1; i++)
tot_size += 1 + (this.len[i] >= 252 ? 1 : 0) + this.len[i];
tot_size += this.len[count - 1];
if (tot_size > maxlen)
return OpusError.OPUS_BUFFER_TOO_SMALL;
data[ptr++] = (byte)((this.toc & 0xFC) | 0x3);
data[ptr++] = (byte)(count | 0x80);
}
else
{
tot_size += count * this.len[0] + 2;
if (tot_size > maxlen)
return OpusError.OPUS_BUFFER_TOO_SMALL;
data[ptr++] = (byte)((this.toc & 0xFC) | 0x3);
data[ptr++] = (byte)(count);
}
pad_amount = pad != 0 ? (maxlen - tot_size) : 0;
if (pad_amount != 0)
{
int nb_255s;
data[data_ptr + 1] |= 0x40;
nb_255s = (pad_amount - 1) / 255;
for (i = 0; i < nb_255s; i++)
{
data[ptr++] = 255;
}
data[ptr++] = (byte)(pad_amount - 255 * nb_255s - 1);
tot_size += pad_amount;
}
if (vbr != 0)
{
for (i = 0; i < count - 1; i++)
ptr += (OpusPacketInfo.encode_size(this.len[i], data, ptr));
}
}
if (self_delimited != 0)
{
int sdlen = OpusPacketInfo.encode_size(this.len[count - 1], data, ptr);
ptr += (sdlen);
}
/* Copy the actual data */
for (i = begin; i < count + begin; i++)
{
if (this.frames[i] == data)
{
/* Using OPUS_MOVE() instead of OPUS_COPY() in case we're doing in-place
padding from opus_packet_pad or opus_packet_unpad(). */
Arrays.MemMove<byte>(data, frames_ptrs[i], ptr, this.len[i]);
}
else
{
Array.Copy(this.frames[i], frames_ptrs[i], data, ptr, this.len[i]);
}
ptr += this.len[i];
}
if (pad != 0)
{
/* Fill padding with zeros. */
//Arrays.MemSetWithOffset<byte>(ptr.Data, 0, ptr.Offset, data.Offset + maxlen - ptr.Offset);
// FIXME why did they not just use a MemSet(0) here?
while (ptr < data_ptr + maxlen)
{
data[ptr++] = 0;
}
}
return tot_size;
}
/** Construct a new packet from data previously submitted to the repacketizer
* state via opus_repacketizer_cat().
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
* construct the new packet.
* @param begin <tt>int</tt>: The index of the first frame in the current
* repacketizer state to include in the output.
* @param end <tt>int</tt>: One past the index of the last frame in the
* current repacketizer state to include in the
* output.
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
* store the output packet.
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
* the output buffer. In order to guarantee
* success, this should be at least
* <code>1276</code> for a single frame,
* or for multiple frames,
* <code>1277*(end-begin)</code>.
* However, <code>1*(end-begin)</code> plus
* the size of all packet data submitted to
* the repacketizer since the last call to
* opus_repacketizer_init() or
* opus_repacketizer_create() is also
* sufficient, and possibly much smaller.
* @returns The total size of the output packet on success, or an error code
* on failure.
* @retval #OPUS_BAD_ARG <code>[begin,end)</code> was an invalid range of
* frames (begin &lt; 0, begin &gt;= end, or end &gt;
* opus_repacketizer_get_nb_frames()).
* @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the
* complete output packet.
*/
public int CreatePacket(int begin, int end, byte[] data, int data_offset, int maxlen)
{
return opus_repacketizer_out_range_impl(begin, end, data, data_offset, maxlen, 0, 0);
}
/** Construct a new packet from data previously submitted to the repacketizer
* state via opus_repacketizer_cat().
* This is a convenience routine that returns all the data submitted so far
* in a single packet.
* It is equivalent to calling
* @code
* opus_repacketizer_out_range(rp, 0, opus_repacketizer_get_nb_frames(rp),
* data, maxlen)
* @endcode
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
* construct the new packet.
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
* store the output packet.
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
* the output buffer. In order to guarantee
* success, this should be at least
* <code>1277*opus_repacketizer_get_nb_frames(rp)</code>.
* However,
* <code>1*opus_repacketizer_get_nb_frames(rp)</code>
* plus the size of all packet data
* submitted to the repacketizer since the
* last call to opus_repacketizer_init() or
* opus_repacketizer_create() is also
* sufficient, and possibly much smaller.
* @returns The total size of the output packet on success, or an error code
* on failure.
* @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the
* complete output packet.
*/
public int CreatePacket(byte[] data, int data_offset, int maxlen)
{
return opus_repacketizer_out_range_impl(0, this.nb_frames, data, data_offset, maxlen, 0, 0);
}
/** Pads a given Opus packet to a larger size (possibly changing the TOC sequence).
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
* packet to pad.
* @param len <tt>opus_int32</tt>: The size of the packet.
* This must be at least 1.
* @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding.
* This must be at least as large as len.
* @returns an error code
* @retval #OPUS_OK \a on success.
* @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len.
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
*/
public static int PadPacket(byte[] data, int data_offset, int len, int new_len)
{
OpusRepacketizer rp = new OpusRepacketizer();
int ret;
if (len < 1)
return OpusError.OPUS_BAD_ARG;
if (len == new_len)
return OpusError.OPUS_OK;
else if (len > new_len)
return OpusError.OPUS_BAD_ARG;
rp.Reset();
/* Moving payload to the end of the packet so we can do in-place padding */
Arrays.MemMove<byte>(data, data_offset, data_offset + new_len - len, len);
//data.MemMoveTo(data.Point(new_len - len), len);
rp.AddPacket(data, data_offset + new_len - len, len);
ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, data_offset, new_len, 0, 1);
if (ret > 0)
return OpusError.OPUS_OK;
else
return ret;
}
/** Remove all padding from a given Opus packet and rewrite the TOC sequence to
* minimize space usage.
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
* packet to strip.
* @param len <tt>opus_int32</tt>: The size of the packet.
* This must be at least 1.
* @returns The new size of the output packet on success, or an error code
* on failure.
* @retval #OPUS_BAD_ARG \a len was less than 1.
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
*/
public static int UnpadPacket(byte[] data, int data_offset, int len)
{
int ret;
if (len < 1)
return OpusError.OPUS_BAD_ARG;
OpusRepacketizer rp = new OpusRepacketizer();
rp.Reset();
ret = rp.AddPacket(data, data_offset, len);
if (ret < 0)
return ret;
ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, data_offset, len, 0, 0);
Inlines.OpusAssert(ret > 0 && ret <= len);
return ret;
}
/** Pads a given Opus multi-stream packet to a larger size (possibly changing the TOC sequence).
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
* packet to pad.
* @param len <tt>opus_int32</tt>: The size of the packet.
* This must be at least 1.
* @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding.
* This must be at least 1.
* @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet.
* This must be at least as large as len.
* @returns an error code
* @retval #OPUS_OK \a on success.
* @retval #OPUS_BAD_ARG \a len was less than 1.
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
*/
public static int PadMultistreamPacket(byte[] data, int data_offset, int len, int new_len, int nb_streams)
{
int s;
int count;
byte dummy_toc;
short[] size = new short[48];
int packet_offset;
int dummy_offset;
int amount;
if (len < 1)
return OpusError.OPUS_BAD_ARG;
if (len == new_len)
return OpusError.OPUS_OK;
else if (len > new_len)
return OpusError.OPUS_BAD_ARG;
amount = new_len - len;
/* Seek to last stream */
for (s = 0; s < nb_streams - 1; s++)
{
if (len <= 0)
return OpusError.OPUS_INVALID_PACKET;
count = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, 1, out dummy_toc, null, null, 0,
size, 0, out dummy_offset, out packet_offset);
if (count < 0)
return count;
data_offset += packet_offset;
len -= packet_offset;
}
return PadPacket(data, data_offset, len, len + amount);
}
// FIXME THIS METHOD FAILS IN TEST_OPUS_ENCODE
/** Remove all padding from a given Opus multi-stream packet and rewrite the TOC sequence to
* minimize space usage.
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
* packet to strip.
* @param len <tt>opus_int32</tt>: The size of the packet.
* This must be at least 1.
* @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet.
* This must be at least 1.
* @returns The new size of the output packet on success, or an error code
* on failure.
* @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len.
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
*/
public static int UnpadMultistreamPacket(byte[] data, int data_offset, int len, int nb_streams)
{
int s;
byte dummy_toc;
short[] size = new short[48];
int packet_offset;
int dummy_offset;
OpusRepacketizer rp = new OpusRepacketizer();
int dst;
int dst_len;
if (len < 1)
return OpusError.OPUS_BAD_ARG;
dst = data_offset;
dst_len = 0;
/* Unpad all frames */
for (s = 0; s < nb_streams; s++)
{
int ret;
int self_delimited = ((s != nb_streams) ? 1 : 0) - 1;
if (len <= 0)
return OpusError.OPUS_INVALID_PACKET;
rp.Reset();
ret = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, self_delimited, out dummy_toc, null, null, 0,
size, 0, out dummy_offset, out packet_offset);
if (ret < 0)
return ret;
ret = rp.opus_repacketizer_cat_impl(data, data_offset, packet_offset, self_delimited);
if (ret < 0)
return ret;
ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, dst, len, self_delimited, 0);
if (ret < 0)
return ret;
else
dst_len += ret;
dst += ret;
data_offset += packet_offset;
len -= packet_offset;
}
return dst_len;
}
}
}

View File

@@ -0,0 +1,61 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus.Structs
{
internal class StereoWidthState
{
internal int XX;
internal int XY;
internal int YY;
internal int smoothed_width;
internal int max_follower;
internal void Reset()
{
XX = 0;
XY = 0;
YY = 0;
smoothed_width = 0;
max_follower = 0;
}
}
}

View File

@@ -0,0 +1,140 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus.Structs
{
internal class TonalityAnalysisState
{
internal bool enabled = false;
internal readonly float[] angle = new float[240];
internal readonly float[] d_angle = new float[240];
internal readonly float[] d2_angle = new float[240];
internal readonly int[] inmem = new int[OpusConstants.ANALYSIS_BUF_SIZE];
internal int mem_fill; /* number of usable samples in the buffer */
internal readonly float[] prev_band_tonality = new float[OpusConstants.NB_TBANDS];
internal float prev_tonality;
internal readonly float[][] E = Arrays.InitTwoDimensionalArray<float>(OpusConstants.NB_FRAMES, OpusConstants.NB_TBANDS);
internal readonly float[] lowE = new float[OpusConstants.NB_TBANDS];
internal readonly float[] highE = new float[OpusConstants.NB_TBANDS];
internal readonly float[] meanE = new float[OpusConstants.NB_TOT_BANDS];
internal readonly float[] mem = new float[32];
internal readonly float[] cmean = new float[8];
internal readonly float[] std = new float[9];
internal float music_prob;
internal float Etracker;
internal float lowECount;
internal int E_count;
internal int last_music;
internal int last_transition;
internal int count;
internal readonly float[] subframe_mem = new float[3];
internal int analysis_offset;
/** Probability of having speech for time i to DETECT_SIZE-1 (and music before).
pspeech[0] is the probability that all frames in the window are speech. */
internal readonly float[] pspeech = new float[OpusConstants.DETECT_SIZE];
/** Probability of having music for time i to DETECT_SIZE-1 (and speech before).
pmusic[0] is the probability that all frames in the window are music. */
internal readonly float[] pmusic = new float[OpusConstants.DETECT_SIZE];
internal float speech_confidence;
internal float music_confidence;
internal int speech_confidence_count;
internal int music_confidence_count;
internal int write_pos;
internal int read_pos;
internal int read_subframe;
internal readonly AnalysisInfo[] info = new AnalysisInfo[OpusConstants.DETECT_SIZE];
internal TonalityAnalysisState()
{
for (int c = 0; c < OpusConstants.DETECT_SIZE; c++)
{
info[c] = new AnalysisInfo();
}
}
internal void Reset()
{
Arrays.MemSetFloat(angle,0, 240);
Arrays.MemSetFloat(d_angle,0, 240);
Arrays.MemSetFloat(d2_angle,0, 240);
Arrays.MemSetInt(inmem, 0, OpusConstants.ANALYSIS_BUF_SIZE);
mem_fill = 0;
Arrays.MemSetFloat(prev_band_tonality,0, OpusConstants.NB_TBANDS);
prev_tonality = 0;
for (int c = 0; c < OpusConstants.NB_FRAMES; c++)
{
Arrays.MemSetFloat(E[c], 0, OpusConstants.NB_TBANDS);
}
Arrays.MemSetFloat(lowE,0, OpusConstants.NB_TBANDS);
Arrays.MemSetFloat(highE,0, OpusConstants.NB_TBANDS);
Arrays.MemSetFloat(meanE,0, OpusConstants.NB_TOT_BANDS);
Arrays.MemSetFloat(mem,0, 32);
Arrays.MemSetFloat(cmean,0, 8);
Arrays.MemSetFloat(std,0, 9);
music_prob = 0;
Etracker = 0;
lowECount = 0;
E_count = 0;
last_music = 0;
last_transition = 0;
count = 0;
Arrays.MemSetFloat(subframe_mem,0, 3);
analysis_offset = 0;
Arrays.MemSetFloat(pspeech,0, OpusConstants.DETECT_SIZE);
Arrays.MemSetFloat(pmusic,0, OpusConstants.DETECT_SIZE);
speech_confidence = 0;
music_confidence = 0;
speech_confidence_count = 0;
music_confidence_count = 0;
write_pos = 0;
read_pos = 0;
read_subframe = 0;
for (int c = 0; c < OpusConstants.DETECT_SIZE; c++)
{
info[c].Reset();
}
}
}
}

View File

@@ -0,0 +1,69 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus.Structs
{
internal class VorbisLayout
{
internal VorbisLayout(int streams, int coupled_streams, byte[] map)
{
nb_streams = streams;
nb_coupled_streams = coupled_streams;
mapping = map;
}
internal int nb_streams;
internal int nb_coupled_streams;
internal byte[] mapping;
/* Index is nb_channel-1*/
internal static readonly VorbisLayout[] vorbis_mappings = {
new VorbisLayout(1, 0, new byte[] {0}), /* 1: mono */
new VorbisLayout(1, 1, new byte[] {0, 1}), /* 2: stereo */
new VorbisLayout(2, 1, new byte[] {0, 2, 1}), /* 3: 1-d surround */
new VorbisLayout(2, 2, new byte[] {0, 1, 2, 3}), /* 4: quadraphonic surround */
new VorbisLayout(3, 2, new byte[] {0, 4, 1, 2, 3}), /* 5: 5-channel surround */
new VorbisLayout(4, 2, new byte[] {0, 4, 1, 2, 3, 5}), /* 6: 5.1 surround */
new VorbisLayout(4, 3, new byte[] {0, 4, 1, 2, 3, 5, 6}), /* 7: 6.1 surround */
new VorbisLayout(5, 3, new byte[] {0, 6, 1, 2, 3, 4, 5, 7}), /* 8: 7.1 surround */
};
}
}

View File

@@ -0,0 +1,298 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Concentus.Common.CPlusPlus;
using Concentus.Structs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concentus
{
internal static class Tables
{
internal static readonly float[] dct_table = {
0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f,
0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f,
0.351851f, 0.338330f, 0.311806f, 0.273300f, 0.224292f, 0.166664f, 0.102631f, 0.034654f,
-0.034654f,-0.102631f,-0.166664f,-0.224292f,-0.273300f,-0.311806f,-0.338330f,-0.351851f,
0.346760f, 0.293969f, 0.196424f, 0.068975f,-0.068975f,-0.196424f,-0.293969f,-0.346760f,
-0.346760f,-0.293969f,-0.196424f,-0.068975f, 0.068975f, 0.196424f, 0.293969f, 0.346760f,
0.338330f, 0.224292f, 0.034654f,-0.166664f,-0.311806f,-0.351851f,-0.273300f,-0.102631f,
0.102631f, 0.273300f, 0.351851f, 0.311806f, 0.166664f,-0.034654f,-0.224292f,-0.338330f,
0.326641f, 0.135299f,-0.135299f,-0.326641f,-0.326641f,-0.135299f, 0.135299f, 0.326641f,
0.326641f, 0.135299f,-0.135299f,-0.326641f,-0.326641f,-0.135299f, 0.135299f, 0.326641f,
0.311806f, 0.034654f,-0.273300f,-0.338330f,-0.102631f, 0.224292f, 0.351851f, 0.166664f,
-0.166664f,-0.351851f,-0.224292f, 0.102631f, 0.338330f, 0.273300f,-0.034654f,-0.311806f,
0.293969f,-0.068975f,-0.346760f,-0.196424f, 0.196424f, 0.346760f, 0.068975f,-0.293969f,
-0.293969f, 0.068975f, 0.346760f, 0.196424f,-0.196424f,-0.346760f,-0.068975f, 0.293969f,
0.273300f,-0.166664f,-0.338330f, 0.034654f, 0.351851f, 0.102631f,-0.311806f,-0.224292f,
0.224292f, 0.311806f,-0.102631f,-0.351851f,-0.034654f, 0.338330f, 0.166664f,-0.273300f,
};
internal static readonly float[] analysis_window = {
0.000043f, 0.000171f, 0.000385f, 0.000685f, 0.001071f, 0.001541f, 0.002098f, 0.002739f,
0.003466f, 0.004278f, 0.005174f, 0.006156f, 0.007222f, 0.008373f, 0.009607f, 0.010926f,
0.012329f, 0.013815f, 0.015385f, 0.017037f, 0.018772f, 0.020590f, 0.022490f, 0.024472f,
0.026535f, 0.028679f, 0.030904f, 0.033210f, 0.035595f, 0.038060f, 0.040604f, 0.043227f,
0.045928f, 0.048707f, 0.051564f, 0.054497f, 0.057506f, 0.060591f, 0.063752f, 0.066987f,
0.070297f, 0.073680f, 0.077136f, 0.080665f, 0.084265f, 0.087937f, 0.091679f, 0.095492f,
0.099373f, 0.103323f, 0.107342f, 0.111427f, 0.115579f, 0.119797f, 0.124080f, 0.128428f,
0.132839f, 0.137313f, 0.141849f, 0.146447f, 0.151105f, 0.155823f, 0.160600f, 0.165435f,
0.170327f, 0.175276f, 0.180280f, 0.185340f, 0.190453f, 0.195619f, 0.200838f, 0.206107f,
0.211427f, 0.216797f, 0.222215f, 0.227680f, 0.233193f, 0.238751f, 0.244353f, 0.250000f,
0.255689f, 0.261421f, 0.267193f, 0.273005f, 0.278856f, 0.284744f, 0.290670f, 0.296632f,
0.302628f, 0.308658f, 0.314721f, 0.320816f, 0.326941f, 0.333097f, 0.339280f, 0.345492f,
0.351729f, 0.357992f, 0.364280f, 0.370590f, 0.376923f, 0.383277f, 0.389651f, 0.396044f,
0.402455f, 0.408882f, 0.415325f, 0.421783f, 0.428254f, 0.434737f, 0.441231f, 0.447736f,
0.454249f, 0.460770f, 0.467298f, 0.473832f, 0.480370f, 0.486912f, 0.493455f, 0.500000f,
0.506545f, 0.513088f, 0.519630f, 0.526168f, 0.532702f, 0.539230f, 0.545751f, 0.552264f,
0.558769f, 0.565263f, 0.571746f, 0.578217f, 0.584675f, 0.591118f, 0.597545f, 0.603956f,
0.610349f, 0.616723f, 0.623077f, 0.629410f, 0.635720f, 0.642008f, 0.648271f, 0.654508f,
0.660720f, 0.666903f, 0.673059f, 0.679184f, 0.685279f, 0.691342f, 0.697372f, 0.703368f,
0.709330f, 0.715256f, 0.721144f, 0.726995f, 0.732807f, 0.738579f, 0.744311f, 0.750000f,
0.755647f, 0.761249f, 0.766807f, 0.772320f, 0.777785f, 0.783203f, 0.788573f, 0.793893f,
0.799162f, 0.804381f, 0.809547f, 0.814660f, 0.819720f, 0.824724f, 0.829673f, 0.834565f,
0.839400f, 0.844177f, 0.848895f, 0.853553f, 0.858151f, 0.862687f, 0.867161f, 0.871572f,
0.875920f, 0.880203f, 0.884421f, 0.888573f, 0.892658f, 0.896677f, 0.900627f, 0.904508f,
0.908321f, 0.912063f, 0.915735f, 0.919335f, 0.922864f, 0.926320f, 0.929703f, 0.933013f,
0.936248f, 0.939409f, 0.942494f, 0.945503f, 0.948436f, 0.951293f, 0.954072f, 0.956773f,
0.959396f, 0.961940f, 0.964405f, 0.966790f, 0.969096f, 0.971321f, 0.973465f, 0.975528f,
0.977510f, 0.979410f, 0.981228f, 0.982963f, 0.984615f, 0.986185f, 0.987671f, 0.989074f,
0.990393f, 0.991627f, 0.992778f, 0.993844f, 0.994826f, 0.995722f, 0.996534f, 0.997261f,
0.997902f, 0.998459f, 0.998929f, 0.999315f, 0.999615f, 0.999829f, 0.999957f, 1.000000f,
};
internal static readonly int[] tbands/*[NB_TBANDS + 1]*/ = {
2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120
};
internal static readonly int[] extra_bands/*[NB_TOT_BANDS + 1]*/ = {
1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120, 160, 200
};
/*static const float tweight[NB_TBANDS+1] = {
.3, .4, .5, .6, .7, .8, .9, 1., 1., 1., 1., 1., 1., 1., .8, .7, .6, .5
};*/
/* RMS error was 0.138320, seed was 1361535663 */
internal static readonly float[] weights/*[422]*/ = {
/* hidden layer */
-0.0941125f, -0.302976f, -0.603555f, -0.19393f, -0.185983f,
-0.601617f, -0.0465317f, -0.114563f, -0.103599f, -0.618938f,
-0.317859f, -0.169949f, -0.0702885f, 0.148065f, 0.409524f,
0.548432f, 0.367649f, -0.494393f, 0.764306f, -1.83957f,
0.170849f, 12.786f, -1.08848f, -1.27284f, -16.2606f,
24.1773f, -5.57454f, -0.17276f, -0.163388f, -0.224421f,
-0.0948944f, -0.0728695f, -0.26557f, -0.100283f, -0.0515459f,
-0.146142f, -0.120674f, -0.180655f, 0.12857f, 0.442138f,
-0.493735f, 0.167767f, 0.206699f, -0.197567f, 0.417999f,
1.50364f, -0.773341f, -10.0401f, 0.401872f, 2.97966f,
15.2165f, -1.88905f, -1.19254f, 0.0285397f, -0.00405139f,
0.0707565f, 0.00825699f, -0.0927269f, -0.010393f, -0.00428882f,
-0.00489743f, -0.0709731f, -0.00255992f, 0.0395619f, 0.226424f,
0.0325231f, 0.162175f, -0.100118f, 0.485789f, 0.12697f,
0.285937f, 0.0155637f, 0.10546f, 3.05558f, 1.15059f,
-1.00904f, -1.83088f, 3.31766f, -3.42516f, -0.119135f,
-0.0405654f, 0.00690068f, 0.0179877f, -0.0382487f, 0.00597941f,
-0.0183611f, 0.00190395f, -0.144322f, -0.0435671f, 0.000990594f,
0.221087f, 0.142405f, 0.484066f, 0.404395f, 0.511955f,
-0.237255f, 0.241742f, 0.35045f, -0.699428f, 10.3993f,
2.6507f, -2.43459f, -4.18838f, 1.05928f, 1.71067f,
0.00667811f, -0.0721335f, -0.0397346f, 0.0362704f, -0.11496f,
-0.0235776f, 0.0082161f, -0.0141741f, -0.0329699f, -0.0354253f,
0.00277404f, -0.290654f, -1.14767f, -0.319157f, -0.686544f,
0.36897f, 0.478899f, 0.182579f, -0.411069f, 0.881104f,
-4.60683f, 1.4697f, 0.335845f, -1.81905f, -30.1699f,
5.55225f, 0.0019508f, -0.123576f, -0.0727332f, -0.0641597f,
-0.0534458f, -0.108166f, -0.0937368f, -0.0697883f, -0.0275475f,
-0.192309f, -0.110074f, 0.285375f, -0.405597f, 0.0926724f,
-0.287881f, -0.851193f, -0.099493f, -0.233764f, -1.2852f,
1.13611f, 3.12168f, -0.0699f, -1.86216f, 2.65292f,
-7.31036f, 2.44776f, -0.00111802f, -0.0632786f, -0.0376296f,
-0.149851f, 0.142963f, 0.184368f, 0.123433f, 0.0756158f,
0.117312f, 0.0933395f, 0.0692163f, 0.0842592f, 0.0704683f,
0.0589963f, 0.0942205f, -0.448862f, 0.0262677f, 0.270352f,
-0.262317f, 0.172586f, 2.00227f, -0.159216f, 0.038422f,
10.2073f, 4.15536f, -2.3407f, -0.0550265f, 0.00964792f,
-0.141336f, 0.0274501f, 0.0343921f, -0.0487428f, 0.0950172f,
-0.00775017f, -0.0372492f, -0.00548121f, -0.0663695f, 0.0960506f,
-0.200008f, -0.0412827f, 0.58728f, 0.0515787f, 0.337254f,
0.855024f, 0.668371f, -0.114904f, -3.62962f, -0.467477f,
-0.215472f, 2.61537f, 0.406117f, -1.36373f, 0.0425394f,
0.12208f, 0.0934502f, 0.123055f, 0.0340935f, -0.142466f,
0.035037f, -0.0490666f, 0.0733208f, 0.0576672f, 0.123984f,
-0.0517194f, -0.253018f, 0.590565f, 0.145849f, 0.315185f,
0.221534f, -0.149081f, 0.216161f, -0.349575f, 24.5664f,
-0.994196f, 0.614289f, -18.7905f, -2.83277f, -0.716801f,
-0.347201f, 0.479515f, -0.246027f, 0.0758683f, 0.137293f,
-0.17781f, 0.118751f, -0.00108329f, -0.237334f, 0.355732f,
-0.12991f, -0.0547627f, -0.318576f, -0.325524f, 0.180494f,
-0.0625604f, 0.141219f, 0.344064f, 0.37658f, -0.591772f,
5.8427f, -0.38075f, 0.221894f, -1.41934f, -1.87943e+06f,
1.34114f, 0.0283355f, -0.0447856f, -0.0211466f, -0.0256927f,
0.0139618f, 0.0207934f, -0.0107666f, 0.0110969f, 0.0586069f,
-0.0253545f, -0.0328433f, 0.11872f, -0.216943f, 0.145748f,
0.119808f, -0.0915211f, -0.120647f, -0.0787719f, -0.143644f,
-0.595116f, -1.152f, -1.25335f, -1.17092f, 4.34023f,
-975268.0f, -1.37033f, -0.0401123f, 0.210602f, -0.136656f,
0.135962f, -0.0523293f, 0.0444604f, 0.0143928f, 0.00412666f,
-0.0193003f, 0.218452f, -0.110204f, -2.02563f, 0.918238f,
-2.45362f, 1.19542f, -0.061362f, -1.92243f, 0.308111f,
0.49764f, 0.912356f, 0.209272f, -2.34525f, 2.19326f,
-6.47121f, 1.69771f, -0.725123f, 0.0118929f, 0.0377944f,
0.0554003f, 0.0226452f, -0.0704421f, -0.0300309f, 0.0122978f,
-0.0041782f, -0.0686612f, 0.0313115f, 0.039111f, 0.364111f,
-0.0945548f, 0.0229876f, -0.17414f, 0.329795f, 0.114714f,
0.30022f, 0.106997f, 0.132355f, 5.79932f, 0.908058f,
-0.905324f, -3.3561f, 0.190647f, 0.184211f, -0.673648f,
0.231807f, -0.0586222f, 0.230752f, -0.438277f, 0.245857f,
-0.17215f, 0.0876383f, -0.720512f, 0.162515f, 0.0170571f,
0.101781f, 0.388477f, 1.32931f, 1.08548f, -0.936301f,
-2.36958f, -6.71988f, -3.44376f, 2.13818f, 14.2318f,
4.91459f, -3.09052f, -9.69191f, -0.768234f, 1.79604f,
0.0549653f, 0.163399f, 0.0797025f, 0.0343933f, -0.0555876f,
-0.00505673f, 0.0187258f, 0.0326628f, 0.0231486f, 0.15573f,
0.0476223f, -0.254824f, 1.60155f, -0.801221f, 2.55496f,
0.737629f, -1.36249f, -0.695463f, -2.44301f, -1.73188f,
3.95279f, 1.89068f, 0.486087f, -11.3343f, 3.9416e+06f,
/* output layer */
-0.381439f, 0.12115f, -0.906927f, 2.93878f, 1.6388f,
0.882811f, 0.874344f, 1.21726f, -0.874545f, 0.321706f,
0.785055f, 0.946558f, -0.575066f, -3.46553f, 0.884905f,
0.0924047f, -9.90712f, 0.391338f, 0.160103f, -2.04954f,
4.1455f, 0.0684029f, -0.144761f, -0.285282f, 0.379244f,
-1.1584f, -0.0277241f, -9.85f, -4.82386f, 3.71333f,
3.87308f, 3.52558f};
internal static readonly int[] topo = { 25, 15, 2 };
// fixme: move this into an MLP class singleton or something?
internal static readonly MLP net = new MLP()
{
layers = 3,
topo = topo,
weights = weights
};
internal static readonly float[] tansig_table/*[201]*/ = {
0.000000f, 0.039979f, 0.079830f, 0.119427f, 0.158649f,
0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f,
0.379949f, 0.413644f, 0.446244f, 0.477700f, 0.507977f,
0.537050f, 0.564900f, 0.591519f, 0.616909f, 0.641077f,
0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f,
0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.821040f,
0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f,
0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.915420f,
0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f,
0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.961090f,
0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f,
0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f,
0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f,
0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.992020f,
0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f,
0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f,
0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.997590f,
0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f,
0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f,
0.999000f, 0.999076f, 0.999147f, 0.999213f, 0.999273f,
0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f,
0.999550f, 0.999585f, 0.999617f, 0.999646f, 0.999673f,
0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f,
0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f,
0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f,
0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f,
0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f,
0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.999970f,
0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.999980f,
0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f,
0.999988f, 0.999989f, 0.999990f, 0.999990f, 0.999991f,
0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f,
0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f,
0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f,
0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f,
0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f,
0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f,
0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f,
1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f,
1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f,
1.000000f,
};
// from opus_encoder.c
/* Transition tables for the voice and music. First column is the
middle (memoriless) threshold. The second column is the hysteresis
(difference with the middle) */
internal static readonly int[] mono_voice_bandwidth_thresholds = {
11000, 1000, /* NB<->MB */
14000, 1000, /* MB<->WB */
17000, 1000, /* WB<->SWB */
21000, 2000, /* SWB<->FB */
};
internal static readonly int[] mono_music_bandwidth_thresholds = {
12000, 1000, /* NB<->MB */
15000, 1000, /* MB<->WB */
18000, 2000, /* WB<->SWB */
22000, 2000, /* SWB<->FB */
};
internal static readonly int[] stereo_voice_bandwidth_thresholds = {
11000, 1000, /* NB<->MB */
14000, 1000, /* MB<->WB */
21000, 2000, /* WB<->SWB */
28000, 2000, /* SWB<->FB */
};
internal static readonly int[] stereo_music_bandwidth_thresholds = {
12000, 1000, /* NB<->MB */
18000, 2000, /* MB<->WB */
21000, 2000, /* WB<->SWB */
30000, 2000, /* SWB<->FB */
};
/* Threshold bit-rates for switching between mono and stereo */
public const int stereo_voice_threshold = 30000;
public const int stereo_music_threshold = 30000;
/* Threshold bit-rate for switching between SILK/hybrid and CELT-only */
internal static readonly int[][] mode_thresholds = {
/* voice */ /* music */
new int[]{ 64000, 16000}, /* mono */
new int[]{ 36000, 16000}, /* stereo */
};
}
}

View File

@@ -0,0 +1,52 @@
Hi there, welcome to the Concentus package. You're about to ask me for sample code, so I'll get straight to it:
If you're already using something like P/Opus then your code probably looks like this:
[DllImport(OPUS_TARGET_DLL, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr opus_encoder_create(int Fs, int channels, int application, out IntPtr error);
[DllImport(OPUS_TARGET_DLL, CallingConvention = CallingConvention.Cdecl)]
private static extern int opus_encode(IntPtr st, byte[] pcm, int frame_size, IntPtr data, int max_data_bytes);
// Initialize
IntPtr error;
IntPtr _encoder = opus_encoder_create(48000, 1, OPUS_APPLICATION_AUDIO, out error);
opus_encoder_ctl(_encoder, OPUS_SET_BITRATE_REQUEST, 12000);
// Encoding loop
byte[] inputAudioSamplesInterleaved; // 16-bit pcm data interleaved into a byte array
byte[] outputBuffer[1000];
int frameSize = 960;
unsafe
{
fixed (byte* benc = outputBuffer)
{
IntPtr encodedPtr = new IntPtr(benc);
int thisPacketSizeOrSometimesAnErrorCode = opus_encode(_encoder, inputAudioSamplesInterleaved, frameSize, encodedPtr, outputBuffer.Length);
}
}
Here is what you can replace it with:
// Initialize
OpusEncoder encoder = OpusEncoder.Create(48000, 1, OpusApplication.OPUS_APPLICATION_AUDIO);
encoder.Bitrate = 12000;
// Encoding loop
short[] inputAudioSamples
byte[] outputBuffer[1000];
int frameSize = 960;
int thisPacketSize = encoder.Encode(inputAudioSamples, 0, frameSize, outputBuffer, 0, outputBuffer.Length); // this throws OpusException on a failure, rather than returning a negative number
And here is the decoder path:
OpusDecoder decoder = OpusDecoder.Create(48000, 1);
// Decoding loop
byte[] compressedPacket;
int frameSize = 960; // must be same as framesize used in input, you can use OpusPacketInfo.GetNumSamples() to determine this dynamically
short[] outputBuffer = new short[frameSize];
int thisFrameSize = _decoder.Decode(compressedPacket, 0, compressedPacket.Length, outputBuffer, 0, frameSize, false);

View File

@@ -0,0 +1,118 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
internal static class ApplySineWindow
{
/* Apply sine window to signal vector. */
/* Window types: */
/* 1 . sine window from 0 to pi/2 */
/* 2 . sine window from pi/2 to pi */
/* Every other sample is linearly interpolated, for speed. */
/* Window length must be between 16 and 120 (incl) and a multiple of 4. */
/* Matlab code for table:
for k=16:9*4:16+2*9*4, fprintf(' %7.d,', -round(65536*pi ./ (k:4:k+8*4))); fprintf('\n'); end
*/
private static readonly short[] freq_table_Q16 = {
12111, 9804, 8235, 7100, 6239, 5565, 5022, 4575, 4202,
3885, 3612, 3375, 3167, 2984, 2820, 2674, 2542, 2422,
2313, 2214, 2123, 2038, 1961, 1889, 1822, 1760, 1702,
};
internal static void silk_apply_sine_window(
short[] px_win, /* O Pointer to windowed signal */
int px_win_ptr,
short[] px, /* I Pointer to input signal */
int px_ptr,
int win_type, /* I Selects a window type */
int length /* I Window length, multiple of 4 */
)
{
int k, f_Q16, c_Q16;
int S0_Q16, S1_Q16;
Inlines.OpusAssert(win_type == 1 || win_type == 2);
/* Length must be in a range from 16 to 120 and a multiple of 4 */
Inlines.OpusAssert(length >= 16 && length <= 120);
Inlines.OpusAssert((length & 3) == 0);
/* Frequency */
k = (length >> 2) - 4;
Inlines.OpusAssert(k >= 0 && k <= 26);
f_Q16 = (int)freq_table_Q16[k];
/* Factor used for cosine approximation */
c_Q16 = Inlines.silk_SMULWB((int)f_Q16, -f_Q16);
Inlines.OpusAssert(c_Q16 >= -32768);
/* initialize state */
if (win_type == 1)
{
/* start from 0 */
S0_Q16 = 0;
/* approximation of sin(f) */
S1_Q16 = f_Q16 + Inlines.silk_RSHIFT(length, 3);
}
else {
/* start from 1 */
S0_Q16 = ((int)1 << 16);
/* approximation of cos(f) */
S1_Q16 = ((int)1 << 16) + Inlines.silk_RSHIFT(c_Q16, 1) + Inlines.silk_RSHIFT(length, 4);
}
/* Uses the recursive equation: sin(n*f) = 2 * cos(f) * sin((n-1)*f) - sin((n-2)*f) */
/* 4 samples at a time */
for (k = 0; k < length; k += 4)
{
int pxwk = px_win_ptr + k;
int pxk = px_ptr + k;
px_win[pxwk] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk]);
px_win[pxwk + 1] = (short)Inlines.silk_SMULWB(S1_Q16, px[pxk + 1]);
S0_Q16 = Inlines.silk_SMULWB(S1_Q16, c_Q16) + Inlines.silk_LSHIFT(S1_Q16, 1) - S0_Q16 + 1;
S0_Q16 = Inlines.silk_min(S0_Q16, ((int)1 << 16));
px_win[pxwk + 2] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk + 2]);
px_win[pxwk + 3] = (short)Inlines.silk_SMULWB(S0_Q16, px[pxk + 3]);
S1_Q16 = Inlines.silk_SMULWB(S0_Q16, c_Q16) + Inlines.silk_LSHIFT(S0_Q16, 1) - S1_Q16;
S1_Q16 = Inlines.silk_min(S1_Q16, ((int)1 << 16));
}
}
}
}

View File

@@ -0,0 +1,90 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class BWExpander
{
/// <summary>
/// Chirp (bw expand) LP AR filter (Fixed point implementation)
/// </summary>
/// <param name="ar">I/O AR filter to be expanded (without leading 1)</param>
/// <param name="d">I length of ar</param>
/// <param name="chirp_Q16">I chirp factor (typically in range (0..1) )</param>
internal static void silk_bwexpander_32(
int[] ar, /* I/O AR filter to be expanded (without leading 1) */
int d, /* I Length of ar */
int chirp_Q16 /* I Chirp factor in Q16 */
)
{
int i;
int chirp_minus_one_Q16 = chirp_Q16 - 65536;
for (i = 0; i < d - 1; i++)
{
ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]);
chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16);
}
ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]);
}
/// <summary>
/// Chirp (bw expand) LP AR filter (Fixed point implementation)
/// </summary>
/// <param name="ar">I/O AR filter to be expanded (without leading 1)</param>
/// <param name="d">I length of ar</param>
/// <param name="chirp_Q16">I chirp factor (typically in range (0..1) )</param>
internal static void silk_bwexpander(
short[] ar,
int d,
int chirp_Q16)
{
int i;
int chirp_minus_one_Q16 = chirp_Q16 - 65536;
/* NB: Dont use silk_SMULWB, instead of silk_RSHIFT_ROUND( silk_MUL(), 16 ), below. */
/* Bias in silk_SMULWB can lead to unstable filters */
for (i = 0; i < d - 1; i++)
{
ar[i] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[i]), 16);
chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16);
}
ar[d - 1] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[d - 1]), 16);
}
}
}

View File

@@ -0,0 +1,326 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if !UNSAFE
namespace Concentus.Silk
{
using Concentus.Celt;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class BurgModified
{
/* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */
private const int MAX_FRAME_SIZE = 384;
private const int QA = 25;
private const int N_BITS_HEAD_ROOM = 2;
private const int MIN_RSHIFTS = -16;
private const int MAX_RSHIFTS = (32 - QA);
/* Compute reflection coefficients from input signal */
internal static void silk_burg_modified(
BoxedValueInt res_nrg, /* O Residual energy */
BoxedValueInt res_nrg_Q, /* O Residual energy Q value */
int[] A_Q16, /* O Prediction coefficients (length order) */
short[] x, /* I Input signal, length: nb_subfr * ( D + subfr_length ) */
int x_ptr,
int minInvGain_Q30, /* I Inverse of max prediction gain */
int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */
int nb_subfr, /* I Number of subframes stacked in x */
int D /* I Order */
)
{
int k, n, s, lz, rshifts, reached_max_gain;
int C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2;
int x_offset;
int[] C_first_row = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] C_last_row = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] Af_QA = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] CAf = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1];
int[] CAb = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1];
int[] xcorr = new int[SilkConstants.SILK_MAX_ORDER_LPC];
long C0_64;
Inlines.OpusAssert(subfr_length * nb_subfr <= MAX_FRAME_SIZE);
/* Compute autocorrelations, added over subframes */
C0_64 = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr);
lz = Inlines.silk_CLZ64(C0_64);
rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz;
if (rshifts > MAX_RSHIFTS) rshifts = MAX_RSHIFTS;
if (rshifts < MIN_RSHIFTS) rshifts = MIN_RSHIFTS;
if (rshifts > 0)
{
C0 = (int)Inlines.silk_RSHIFT64(C0_64, rshifts);
}
else {
C0 = Inlines.silk_LSHIFT32((int)C0_64, -rshifts);
}
CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */
Arrays.MemSetInt(C_first_row, 0, SilkConstants.SILK_MAX_ORDER_LPC);
if (rshifts > 0)
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
for (n = 1; n < D + 1; n++)
{
C_first_row[n - 1] += (int)Inlines.silk_RSHIFT64(
Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset + n, subfr_length - n), rshifts);
}
}
}
else {
for (s = 0; s < nb_subfr; s++)
{
int i;
int d;
x_offset = x_ptr + s * subfr_length;
CeltPitchXCorr.pitch_xcorr(x, x_offset, x, x_offset + 1, xcorr, subfr_length - D, D);
for (n = 1; n < D + 1; n++)
{
for (i = n + subfr_length - D, d = 0; i < subfr_length; i++)
d = Inlines.MAC16_16(d, x[x_offset + i], x[x_offset + i - n]);
xcorr[n - 1] += d;
}
for (n = 1; n < D + 1; n++)
{
C_first_row[n - 1] += Inlines.silk_LSHIFT32(xcorr[n - 1], -rshifts);
}
}
}
Array.Copy(C_first_row, C_last_row, SilkConstants.SILK_MAX_ORDER_LPC);
/* Initialize */
CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */
invGain_Q30 = (int)1 << 30;
reached_max_gain = 0;
for (n = 0; n < D; n++)
{
/* Update first row of correlation matrix (without first element) */
/* Update last row of correlation matrix (without last element, stored in reversed order) */
/* Update C * Af */
/* Update C * flipud(Af) (stored in reversed order) */
if (rshifts > -2)
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
x1 = -Inlines.silk_LSHIFT32((int)x[x_offset + n], 16 - rshifts); /* Q(16-rshifts) */
x2 = -Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], 16 - rshifts); /* Q(16-rshifts) */
tmp1 = Inlines.silk_LSHIFT32((int)x[x_offset + n], QA - 16); /* Q(QA-16) */
tmp2 = Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], QA - 16); /* Q(QA-16) */
for (k = 0; k < n; k++)
{
C_first_row[k] = Inlines.silk_SMLAWB(C_first_row[k], x1, x[x_offset + n - k - 1]); /* Q( -rshifts ) */
C_last_row[k] = Inlines.silk_SMLAWB(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); /* Q( -rshifts ) */
Atmp_QA = Af_QA[k];
tmp1 = Inlines.silk_SMLAWB(tmp1, Atmp_QA, x[x_offset + n - k - 1]); /* Q(QA-16) */
tmp2 = Inlines.silk_SMLAWB(tmp2, Atmp_QA, x[x_offset + subfr_length - n + k]); /* Q(QA-16) */
}
tmp1 = Inlines.silk_LSHIFT32(-tmp1, 32 - QA - rshifts); /* Q(16-rshifts) */
tmp2 = Inlines.silk_LSHIFT32(-tmp2, 32 - QA - rshifts); /* Q(16-rshifts) */
for (k = 0; k <= n; k++)
{
CAf[k] = Inlines.silk_SMLAWB(CAf[k], tmp1, x[x_offset + n - k]); /* Q( -rshift ) */
CAb[k] = Inlines.silk_SMLAWB(CAb[k], tmp2, x[x_offset + subfr_length - n + k - 1]); /* Q( -rshift ) */
}
}
}
else {
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
x1 = -Inlines.silk_LSHIFT32((int)x[x_offset + n], -rshifts); /* Q( -rshifts ) */
x2 = -Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], -rshifts); /* Q( -rshifts ) */
tmp1 = Inlines.silk_LSHIFT32((int)x[x_offset + n], 17); /* Q17 */
tmp2 = Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], 17); /* Q17 */
for (k = 0; k < n; k++)
{
C_first_row[k] = Inlines.silk_MLA(C_first_row[k], x1, x[x_offset + n - k - 1]); /* Q( -rshifts ) */
C_last_row[k] = Inlines.silk_MLA(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); /* Q( -rshifts ) */
Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 17); /* Q17 */
tmp1 = Inlines.silk_MLA(tmp1, x[x_offset + n - k - 1], Atmp1); /* Q17 */
tmp2 = Inlines.silk_MLA(tmp2, x[x_offset + subfr_length - n + k], Atmp1); /* Q17 */
}
tmp1 = -tmp1; /* Q17 */
tmp2 = -tmp2; /* Q17 */
for (k = 0; k <= n; k++)
{
CAf[k] = Inlines.silk_SMLAWW(CAf[k], tmp1,
Inlines.silk_LSHIFT32((int)x[x_offset + n - k], -rshifts - 1)); /* Q( -rshift ) */
CAb[k] = Inlines.silk_SMLAWW(CAb[k], tmp2,
Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n + k - 1], -rshifts - 1)); /* Q( -rshift ) */
}
}
}
/* Calculate nominator and denominator for the next order reflection (parcor) coefficient */
tmp1 = C_first_row[n]; /* Q( -rshifts ) */
tmp2 = C_last_row[n]; /* Q( -rshifts ) */
num = 0; /* Q( -rshifts ) */
nrg = Inlines.silk_ADD32(CAb[0], CAf[0]); /* Q( 1-rshifts ) */
for (k = 0; k < n; k++)
{
Atmp_QA = Af_QA[k];
lz = Inlines.silk_CLZ32(Inlines.silk_abs(Atmp_QA)) - 1;
lz = Inlines.silk_min(32 - QA, lz);
Atmp1 = Inlines.silk_LSHIFT32(Atmp_QA, lz); /* Q( QA + lz ) */
tmp1 = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(C_last_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
tmp2 = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(C_first_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
num = Inlines.silk_ADD_LSHIFT32(num, Inlines.silk_SMMUL(CAb[n - k], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
nrg = Inlines.silk_ADD_LSHIFT32(nrg, Inlines.silk_SMMUL(Inlines.silk_ADD32(CAb[k + 1], CAf[k + 1]),
Atmp1), 32 - QA - lz); /* Q( 1-rshifts ) */
}
CAf[n + 1] = tmp1; /* Q( -rshifts ) */
CAb[n + 1] = tmp2; /* Q( -rshifts ) */
num = Inlines.silk_ADD32(num, tmp2); /* Q( -rshifts ) */
num = Inlines.silk_LSHIFT32(-num, 1); /* Q( 1-rshifts ) */
/* Calculate the next order reflection (parcor) coefficient */
if (Inlines.silk_abs(num) < nrg)
{
rc_Q31 = Inlines.silk_DIV32_varQ(num, nrg, 31);
}
else {
rc_Q31 = (num > 0) ? int.MaxValue : int.MinValue;
}
/* Update inverse prediction gain */
tmp1 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
tmp1 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, tmp1), 2);
if (tmp1 <= minInvGain_Q30)
{
/* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */
tmp2 = ((int)1 << 30) - Inlines.silk_DIV32_varQ(minInvGain_Q30, invGain_Q30, 30); /* Q30 */
rc_Q31 = Inlines.silk_SQRT_APPROX(tmp2); /* Q15 */
/* Newton-Raphson iteration */
rc_Q31 = Inlines.silk_RSHIFT32(rc_Q31 + Inlines.silk_DIV32(tmp2, rc_Q31), 1); /* Q15 */
rc_Q31 = Inlines.silk_LSHIFT32(rc_Q31, 16); /* Q31 */
if (num < 0)
{
/* Ensure adjusted reflection coefficients has the original sign */
rc_Q31 = -rc_Q31;
}
invGain_Q30 = minInvGain_Q30;
reached_max_gain = 1;
}
else {
invGain_Q30 = tmp1;
}
/* Update the AR coefficients */
for (k = 0; k < (n + 1) >> 1; k++)
{
tmp1 = Af_QA[k]; /* QA */
tmp2 = Af_QA[n - k - 1]; /* QA */
Af_QA[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* QA */
Af_QA[n - k - 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* QA */
}
Af_QA[n] = Inlines.silk_RSHIFT32(rc_Q31, 31 - QA); /* QA */
if (reached_max_gain != 0)
{
/* Reached max prediction gain; set remaining coefficients to zero and exit loop */
for (k = n + 1; k < D; k++)
{
Af_QA[k] = 0;
}
break;
}
/* Update C * Af and C * Ab */
for (k = 0; k <= n + 1; k++)
{
tmp1 = CAf[k]; /* Q( -rshifts ) */
tmp2 = CAb[n - k + 1]; /* Q( -rshifts ) */
CAf[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* Q( -rshifts ) */
CAb[n - k + 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* Q( -rshifts ) */
}
}
if (reached_max_gain != 0)
{
for (k = 0; k < D; k++)
{
/* Scale coefficients */
A_Q16[k] = -Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16);
}
/* Subtract energy of preceding samples from C0 */
if (rshifts > 0)
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
C0 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset, D), rshifts);
}
}
else {
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
C0 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, x_offset, D), -rshifts);
}
}
/* Approximate residual energy */
res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, C0), 2);
res_nrg_Q.Val = 0 - rshifts;
}
else {
/* Return residual energy */
nrg = CAf[0]; /* Q( -rshifts ) */
tmp1 = (int)1 << 16; /* Q16 */
for (k = 0; k < D; k++)
{
Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); /* Q16 */
nrg = Inlines.silk_SMLAWW(nrg, CAf[k + 1], Atmp1); /* Q( -rshifts ) */
tmp1 = Inlines.silk_SMLAWW(tmp1, Atmp1, Atmp1); /* Q16 */
A_Q16[k] = -Atmp1;
}
res_nrg.Val = Inlines.silk_SMLAWW(nrg, Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0), -tmp1);/* Q( -rshifts ) */
res_nrg_Q.Val = -rshifts;
}
}
}
}
#endif

View File

@@ -0,0 +1,336 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if UNSAFE
namespace Concentus.Silk
{
using Concentus.Celt;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class BurgModified
{
/* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */
private const int MAX_FRAME_SIZE = 384;
private const int QA = 25;
private const int N_BITS_HEAD_ROOM = 2;
private const int MIN_RSHIFTS = -16;
private const int MAX_RSHIFTS = (32 - QA);
/* Compute reflection coefficients from input signal */
internal static unsafe void silk_burg_modified(
BoxedValueInt res_nrg, /* O Residual energy */
BoxedValueInt res_nrg_Q, /* O Residual energy Q value */
int[] A_Q16, /* O Prediction coefficients (length order) */
short[] x, /* I Input signal, length: nb_subfr * ( D + subfr_length ) */
int x_ptr,
int minInvGain_Q30, /* I Inverse of max prediction gain */
int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */
int nb_subfr, /* I Number of subframes stacked in x */
int D /* I Order */
)
{
int k, n, s, lz, rshifts, reached_max_gain;
int C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2;
int x_offset;
int[] C_first_row = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] C_last_row = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] Af_QA = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] CAf = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1];
int[] CAb = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1];
int[] xcorr = new int[SilkConstants.SILK_MAX_ORDER_LPC];
long C0_64;
Inlines.OpusAssert(subfr_length * nb_subfr <= MAX_FRAME_SIZE);
fixed (short* px_base = x)
{
short* px = px_base + x_ptr;
/* Compute autocorrelations, added over subframes */
C0_64 = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr);
lz = Inlines.silk_CLZ64(C0_64);
rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz;
if (rshifts > MAX_RSHIFTS) rshifts = MAX_RSHIFTS;
if (rshifts < MIN_RSHIFTS) rshifts = MIN_RSHIFTS;
if (rshifts > 0)
{
C0 = (int)Inlines.silk_RSHIFT64(C0_64, rshifts);
}
else
{
C0 = Inlines.silk_LSHIFT32((int)C0_64, -rshifts);
}
CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */
Arrays.MemSetInt(C_first_row, 0, SilkConstants.SILK_MAX_ORDER_LPC);
if (rshifts > 0)
{
for (s = 0; s < nb_subfr; s++)
{
short* px2 = px + s * subfr_length;
for (n = 1; n < D + 1; n++)
{
C_first_row[n - 1] += (int)Inlines.silk_RSHIFT64(
Inlines.silk_inner_prod16_aligned_64(px2, px2 + n, subfr_length - n), rshifts);
}
}
}
else
{
for (s = 0; s < nb_subfr; s++)
{
int i;
int d;
x_offset = x_ptr + s * subfr_length;
CeltPitchXCorr.pitch_xcorr(x, x_offset, x, x_offset + 1, xcorr, subfr_length - D, D);
for (n = 1; n < D + 1; n++)
{
for (i = n + subfr_length - D, d = 0; i < subfr_length; i++)
d = Inlines.MAC16_16(d, x[x_offset + i], x[x_offset + i - n]);
xcorr[n - 1] += d;
}
for (n = 1; n < D + 1; n++)
{
C_first_row[n - 1] += Inlines.silk_LSHIFT32(xcorr[n - 1], -rshifts);
}
}
}
Array.Copy(C_first_row, C_last_row, SilkConstants.SILK_MAX_ORDER_LPC);
/* Initialize */
CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */
invGain_Q30 = (int)1 << 30;
reached_max_gain = 0;
for (n = 0; n < D; n++)
{
/* Update first row of correlation matrix (without first element) */
/* Update last row of correlation matrix (without last element, stored in reversed order) */
/* Update C * Af */
/* Update C * flipud(Af) (stored in reversed order) */
if (rshifts > -2)
{
for (s = 0; s < nb_subfr; s++)
{
short* px2 = px + s * subfr_length;
x1 = -Inlines.silk_LSHIFT32((int)px2[n], 16 - rshifts); /* Q(16-rshifts) */
x2 = -Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], 16 - rshifts); /* Q(16-rshifts) */
tmp1 = Inlines.silk_LSHIFT32((int)px2[n], QA - 16); /* Q(QA-16) */
tmp2 = Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], QA - 16); /* Q(QA-16) */
for (k = 0; k < n; k++)
{
C_first_row[k] = Inlines.silk_SMLAWB(C_first_row[k], x1, px2[n - k - 1]); /* Q( -rshifts ) */
C_last_row[k] = Inlines.silk_SMLAWB(C_last_row[k], x2, px2[subfr_length - n + k]); /* Q( -rshifts ) */
Atmp_QA = Af_QA[k];
tmp1 = Inlines.silk_SMLAWB(tmp1, Atmp_QA, px2[n - k - 1]); /* Q(QA-16) */
tmp2 = Inlines.silk_SMLAWB(tmp2, Atmp_QA, px2[subfr_length - n + k]); /* Q(QA-16) */
}
tmp1 = Inlines.silk_LSHIFT32(-tmp1, 32 - QA - rshifts); /* Q(16-rshifts) */
tmp2 = Inlines.silk_LSHIFT32(-tmp2, 32 - QA - rshifts); /* Q(16-rshifts) */
for (k = 0; k <= n; k++)
{
CAf[k] = Inlines.silk_SMLAWB(CAf[k], tmp1, px2[n - k]); /* Q( -rshift ) */
CAb[k] = Inlines.silk_SMLAWB(CAb[k], tmp2, px2[subfr_length - n + k - 1]); /* Q( -rshift ) */
}
}
}
else
{
for (s = 0; s < nb_subfr; s++)
{
short* px2 = px + s * subfr_length;
x1 = -Inlines.silk_LSHIFT32((int)px2[n], -rshifts); /* Q( -rshifts ) */
x2 = -Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], -rshifts); /* Q( -rshifts ) */
tmp1 = Inlines.silk_LSHIFT32((int)px2[n], 17); /* Q17 */
tmp2 = Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], 17); /* Q17 */
for (k = 0; k < n; k++)
{
C_first_row[k] = Inlines.silk_MLA(C_first_row[k], x1, px2[n - k - 1]); /* Q( -rshifts ) */
C_last_row[k] = Inlines.silk_MLA(C_last_row[k], x2, px2[subfr_length - n + k]); /* Q( -rshifts ) */
Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 17); /* Q17 */
tmp1 = Inlines.silk_MLA(tmp1, px2[n - k - 1], Atmp1); /* Q17 */
tmp2 = Inlines.silk_MLA(tmp2, px2[subfr_length - n + k], Atmp1); /* Q17 */
}
tmp1 = -tmp1; /* Q17 */
tmp2 = -tmp2; /* Q17 */
for (k = 0; k <= n; k++)
{
CAf[k] = Inlines.silk_SMLAWW(CAf[k], tmp1,
Inlines.silk_LSHIFT32((int)px2[n - k], -rshifts - 1)); /* Q( -rshift ) */
CAb[k] = Inlines.silk_SMLAWW(CAb[k], tmp2,
Inlines.silk_LSHIFT32((int)px2[subfr_length - n + k - 1], -rshifts - 1)); /* Q( -rshift ) */
}
}
}
/* Calculate nominator and denominator for the next order reflection (parcor) coefficient */
tmp1 = C_first_row[n]; /* Q( -rshifts ) */
tmp2 = C_last_row[n]; /* Q( -rshifts ) */
num = 0; /* Q( -rshifts ) */
nrg = Inlines.silk_ADD32(CAb[0], CAf[0]); /* Q( 1-rshifts ) */
for (k = 0; k < n; k++)
{
Atmp_QA = Af_QA[k];
lz = Inlines.silk_CLZ32(Inlines.silk_abs(Atmp_QA)) - 1;
lz = Inlines.silk_min(32 - QA, lz);
Atmp1 = Inlines.silk_LSHIFT32(Atmp_QA, lz); /* Q( QA + lz ) */
tmp1 = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(C_last_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
tmp2 = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(C_first_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
num = Inlines.silk_ADD_LSHIFT32(num, Inlines.silk_SMMUL(CAb[n - k], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
nrg = Inlines.silk_ADD_LSHIFT32(nrg, Inlines.silk_SMMUL(Inlines.silk_ADD32(CAb[k + 1], CAf[k + 1]),
Atmp1), 32 - QA - lz); /* Q( 1-rshifts ) */
}
CAf[n + 1] = tmp1; /* Q( -rshifts ) */
CAb[n + 1] = tmp2; /* Q( -rshifts ) */
num = Inlines.silk_ADD32(num, tmp2); /* Q( -rshifts ) */
num = Inlines.silk_LSHIFT32(-num, 1); /* Q( 1-rshifts ) */
/* Calculate the next order reflection (parcor) coefficient */
if (Inlines.silk_abs(num) < nrg)
{
rc_Q31 = Inlines.silk_DIV32_varQ(num, nrg, 31);
}
else
{
rc_Q31 = (num > 0) ? int.MaxValue : int.MinValue;
}
/* Update inverse prediction gain */
tmp1 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
tmp1 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, tmp1), 2);
if (tmp1 <= minInvGain_Q30)
{
/* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */
tmp2 = ((int)1 << 30) - Inlines.silk_DIV32_varQ(minInvGain_Q30, invGain_Q30, 30); /* Q30 */
rc_Q31 = Inlines.silk_SQRT_APPROX(tmp2); /* Q15 */
/* Newton-Raphson iteration */
rc_Q31 = Inlines.silk_RSHIFT32(rc_Q31 + Inlines.silk_DIV32(tmp2, rc_Q31), 1); /* Q15 */
rc_Q31 = Inlines.silk_LSHIFT32(rc_Q31, 16); /* Q31 */
if (num < 0)
{
/* Ensure adjusted reflection coefficients has the original sign */
rc_Q31 = -rc_Q31;
}
invGain_Q30 = minInvGain_Q30;
reached_max_gain = 1;
}
else
{
invGain_Q30 = tmp1;
}
/* Update the AR coefficients */
for (k = 0; k < (n + 1) >> 1; k++)
{
tmp1 = Af_QA[k]; /* QA */
tmp2 = Af_QA[n - k - 1]; /* QA */
Af_QA[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* QA */
Af_QA[n - k - 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* QA */
}
Af_QA[n] = Inlines.silk_RSHIFT32(rc_Q31, 31 - QA); /* QA */
if (reached_max_gain != 0)
{
/* Reached max prediction gain; set remaining coefficients to zero and exit loop */
for (k = n + 1; k < D; k++)
{
Af_QA[k] = 0;
}
break;
}
/* Update C * Af and C * Ab */
for (k = 0; k <= n + 1; k++)
{
tmp1 = CAf[k]; /* Q( -rshifts ) */
tmp2 = CAb[n - k + 1]; /* Q( -rshifts ) */
CAf[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* Q( -rshifts ) */
CAb[n - k + 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* Q( -rshifts ) */
}
}
if (reached_max_gain != 0)
{
for (k = 0; k < D; k++)
{
/* Scale coefficients */
A_Q16[k] = -Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16);
}
/* Subtract energy of preceding samples from C0 */
if (rshifts > 0)
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
C0 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset, D), rshifts);
}
}
else
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
C0 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, x_offset, D), -rshifts);
}
}
/* Approximate residual energy */
res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, C0), 2);
res_nrg_Q.Val = 0 - rshifts;
}
else
{
/* Return residual energy */
nrg = CAf[0]; /* Q( -rshifts ) */
tmp1 = (int)1 << 16; /* Q16 */
for (k = 0; k < D; k++)
{
Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); /* Q16 */
nrg = Inlines.silk_SMLAWW(nrg, CAf[k + 1], Atmp1); /* Q( -rshifts ) */
tmp1 = Inlines.silk_SMLAWW(tmp1, Atmp1, Atmp1); /* Q16 */
A_Q16[k] = -Atmp1;
}
res_nrg.Val = Inlines.silk_SMLAWW(nrg, Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0), -tmp1);/* Q( -rshifts ) */
res_nrg_Q.Val = -rshifts;
}
}
}
}
}
#endif

View File

@@ -0,0 +1,232 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
/// <summary>
/// Comfort noise generation and estimation
/// </summary>
internal static class CNG
{
/// <summary>
/// Generates excitation for CNG LPC synthesis
/// </summary>
/// <param name="exc_Q10">O CNG excitation signal Q10</param>
/// <param name="exc_buf_Q14">I Random samples buffer Q10</param>
/// <param name="Gain_Q16">I Gain to apply</param>
/// <param name="length">I Length</param>
/// <param name="rand_seed">I/O Seed to random index generator</param>
internal static void silk_CNG_exc(
int[] exc_Q10,
int exc_Q10_ptr,
int[] exc_buf_Q14,
int Gain_Q16,
int length,
ref int rand_seed)
{
int seed;
int i, idx, exc_mask;
exc_mask = SilkConstants.CNG_BUF_MASK_MAX;
while (exc_mask > length)
{
exc_mask = Inlines.silk_RSHIFT(exc_mask, 1);
}
seed = rand_seed;
for (i = exc_Q10_ptr; i < exc_Q10_ptr + length; i++)
{
seed = Inlines.silk_RAND(seed);
idx = (int)(Inlines.silk_RSHIFT(seed, 24) & exc_mask);
Inlines.OpusAssert(idx >= 0);
Inlines.OpusAssert(idx <= SilkConstants.CNG_BUF_MASK_MAX);
exc_Q10[i] = (short)Inlines.silk_SAT16(Inlines.silk_SMULWW(exc_buf_Q14[idx], Gain_Q16 >> 4));
}
rand_seed = seed;
}
/// <summary>
/// Resets CNG state
/// </summary>
/// <param name="psDec">I/O Decoder state</param>
internal static void silk_CNG_Reset(SilkChannelDecoder psDec)
{
int i, NLSF_step_Q15, NLSF_acc_Q15;
NLSF_step_Q15 = Inlines.silk_DIV32_16(short.MaxValue, (short)(psDec.LPC_order + 1));
NLSF_acc_Q15 = 0;
for (i = 0; i < psDec.LPC_order; i++)
{
NLSF_acc_Q15 += NLSF_step_Q15;
psDec.sCNG.CNG_smth_NLSF_Q15[i] = (short)(NLSF_acc_Q15);
}
psDec.sCNG.CNG_smth_Gain_Q16 = 0;
psDec.sCNG.rand_seed = 3176576;
}
/// <summary>
/// Updates CNG estimate, and applies the CNG when packet was lost
/// </summary>
/// <param name="psDec">I/O Decoder state</param>
/// <param name="psDecCtrl">I/O Decoder control</param>
/// <param name="frame">I/O Signal</param>
/// <param name="length">I Length of residual</param>
internal static void silk_CNG(
SilkChannelDecoder psDec,
SilkDecoderControl psDecCtrl,
short[] frame,
int frame_ptr,
int length)
{
int i, subfr;
int sum_Q6, max_Gain_Q16, gain_Q16;
short[] A_Q12 = new short[psDec.LPC_order];
CNGState psCNG = psDec.sCNG;
if (psDec.fs_kHz != psCNG.fs_kHz)
{
/* Reset state */
silk_CNG_Reset(psDec);
psCNG.fs_kHz = psDec.fs_kHz;
}
if (psDec.lossCnt == 0 && psDec.prevSignalType == SilkConstants.TYPE_NO_VOICE_ACTIVITY)
{
/* Update CNG parameters */
/* Smoothing of LSF's */
for (i = 0; i < psDec.LPC_order; i++)
{
psCNG.CNG_smth_NLSF_Q15[i] += (short)(Inlines.silk_SMULWB((int)psDec.prevNLSF_Q15[i] - (int)psCNG.CNG_smth_NLSF_Q15[i], SilkConstants.CNG_NLSF_SMTH_Q16));
}
/* Find the subframe with the highest gain */
max_Gain_Q16 = 0;
subfr = 0;
for (i = 0; i < psDec.nb_subfr; i++)
{
if (psDecCtrl.Gains_Q16[i] > max_Gain_Q16)
{
max_Gain_Q16 = psDecCtrl.Gains_Q16[i];
subfr = i;
}
}
/* Update CNG excitation buffer with excitation from this subframe */
Arrays.MemMoveInt(psCNG.CNG_exc_buf_Q14, 0, psDec.subfr_length, (psDec.nb_subfr - 1) * psDec.subfr_length);
/* Smooth gains */
for (i = 0; i < psDec.nb_subfr; i++)
{
psCNG.CNG_smth_Gain_Q16 += Inlines.silk_SMULWB(psDecCtrl.Gains_Q16[i] - psCNG.CNG_smth_Gain_Q16, SilkConstants.CNG_GAIN_SMTH_Q16);
}
}
/* Add CNG when packet is lost or during DTX */
if (psDec.lossCnt != 0)
{
int[] CNG_sig_Q10 = new int[length + SilkConstants.MAX_LPC_ORDER];
/* Generate CNG excitation */
gain_Q16 = Inlines.silk_SMULWW(psDec.sPLC.randScale_Q14, psDec.sPLC.prevGain_Q16[1]);
if (gain_Q16 >= (1 << 21) || psCNG.CNG_smth_Gain_Q16 > (1 << 23))
{
gain_Q16 = Inlines.silk_SMULTT(gain_Q16, gain_Q16);
gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULTT(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5);
gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 16);
}
else
{
gain_Q16 = Inlines.silk_SMULWW(gain_Q16, gain_Q16);
gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULWW(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5);
gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 8);
}
silk_CNG_exc(CNG_sig_Q10, SilkConstants.MAX_LPC_ORDER, psCNG.CNG_exc_buf_Q14, gain_Q16, length, ref psCNG.rand_seed);
/* Convert CNG NLSF to filter representation */
NLSF.silk_NLSF2A(A_Q12, psCNG.CNG_smth_NLSF_Q15, psDec.LPC_order);
/* Generate CNG signal, by synthesis filtering */
Array.Copy(psCNG.CNG_synth_state, CNG_sig_Q10, SilkConstants.MAX_LPC_ORDER);
for (i = 0; i < length; i++)
{
int lpci = SilkConstants.MAX_LPC_ORDER + i;
Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16);
/* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
sum_Q6 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 1], A_Q12[0]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 2], A_Q12[1]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 3], A_Q12[2]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 4], A_Q12[3]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 5], A_Q12[4]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 6], A_Q12[5]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 7], A_Q12[6]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 8], A_Q12[7]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 9], A_Q12[8]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 10], A_Q12[9]);
if (psDec.LPC_order == 16)
{
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 11], A_Q12[10]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 12], A_Q12[11]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 13], A_Q12[12]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 14], A_Q12[13]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 15], A_Q12[14]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 16], A_Q12[15]);
}
/* Update states */
CNG_sig_Q10[lpci] = Inlines.silk_ADD_LSHIFT(CNG_sig_Q10[lpci], sum_Q6, 4);
frame[frame_ptr + i] = Inlines.silk_ADD_SAT16(frame[frame_ptr + i], (short)(Inlines.silk_RSHIFT_ROUND(CNG_sig_Q10[lpci], 10)));
}
Array.Copy(CNG_sig_Q10, length, psCNG.CNG_synth_state, 0, SilkConstants.MAX_LPC_ORDER);
}
else
{
Arrays.MemSetInt(psCNG.CNG_synth_state, 0, psDec.LPC_order);
}
}
}
}

View File

@@ -0,0 +1,150 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class CodeSigns
{
private static int silk_enc_map(int a)
{
return (Inlines.silk_RSHIFT((a), 15) + 1);
}
private static int silk_dec_map(int a)
{
return (Inlines.silk_LSHIFT((a), 1) - 1);
}
/// <summary>
/// Encodes signs of excitation
/// </summary>
/// <param name="psRangeEnc">I/O Compressor data structure</param>
/// <param name="pulses">I pulse signal</param>
/// <param name="length">I length of input</param>
/// <param name="signalType">I Signal type</param>
/// <param name="quantOffsetType">I Quantization offset type</param>
/// <param name="sum_pulses">I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS]</param>
internal static void silk_encode_signs(
EntropyCoder psRangeEnc,
sbyte[] pulses,
int length,
int signalType,
int quantOffsetType,
int[] sum_pulses)
{
int i, j, p;
byte[] icdf = new byte[2];
int q_ptr;
byte[] sign_icdf = Tables.silk_sign_iCDF;
int icdf_ptr;
icdf[1] = 0;
q_ptr = 0;
i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1));
icdf_ptr = i;
length = Inlines.silk_RSHIFT(length + (SilkConstants.SHELL_CODEC_FRAME_LENGTH / 2), SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH);
for (i = 0; i < length; i++)
{
p = sum_pulses[i];
if (p > 0)
{
icdf[0] = sign_icdf[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)];
for (j = q_ptr; j < q_ptr + SilkConstants.SHELL_CODEC_FRAME_LENGTH; j++)
{
if (pulses[j] != 0)
{
psRangeEnc.enc_icdf( silk_enc_map(pulses[j]), icdf, 8);
}
}
}
q_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH;
}
}
/// <summary>
/// Decodes signs of excitation
/// </summary>
/// <param name="psRangeDec">I/O Compressor data structure</param>
/// <param name="pulses">I/O pulse signal</param>
/// <param name="length">I length of input</param>
/// <param name="signalType">I Signal type</param>
/// <param name="quantOffsetType">I Quantization offset type</param>
/// <param name="sum_pulses">I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS]</param>
internal static void silk_decode_signs(
EntropyCoder psRangeDec,
short[] pulses,
int length,
int signalType,
int quantOffsetType,
int[] sum_pulses)
{
int i, j, p;
byte[] icdf = new byte[2];
int q_ptr;
byte[] icdf_table = Tables.silk_sign_iCDF;
int icdf_ptr;
icdf[1] = 0;
q_ptr = 0;
i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1));
icdf_ptr = i;
length = Inlines.silk_RSHIFT(length + SilkConstants.SHELL_CODEC_FRAME_LENGTH / 2, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH);
for (i = 0; i < length; i++)
{
p = sum_pulses[i];
if (p > 0)
{
icdf[0] = icdf_table[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)];
for (j = 0; j < SilkConstants.SHELL_CODEC_FRAME_LENGTH; j++)
{
if (pulses[q_ptr + j] > 0)
{
/* attach sign */
pulses[q_ptr + j] *= (short)(silk_dec_map(psRangeDec.dec_icdf(icdf, 8)));
}
}
}
q_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH;
}
}
}
}

View File

@@ -0,0 +1,186 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
/**********************************************************************
* Correlation Matrix Computations for LS estimate.
**********************************************************************/
internal static class CorrelateMatrix
{
/* Calculates correlation vector X'*t */
internal static void silk_corrVector(
short[] x, /* I x vector [L + order - 1] used to form data matrix X */
int x_ptr,
short[] t, /* I Target vector [L] */
int t_ptr,
int L, /* I Length of vectors */
int order, /* I Max lag for correlation */
int[] Xt, /* O Pointer to X'*t correlation vector [order] */
int rshifts /* I Right shifts of correlations */
)
{
int lag, i;
int ptr1;
int ptr2;
int inner_prod;
ptr1 = x_ptr + order - 1; /* Points to first sample of column 0 of X: X[:,0] */
ptr2 = t_ptr;
/* Calculate X'*t */
if (rshifts > 0)
{
/* Right shifting used */
for (lag = 0; lag < order; lag++)
{
inner_prod = 0;
for (i = 0; i < L; i++)
{
inner_prod += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], t[ptr2 + i]), rshifts);
}
Xt[lag] = inner_prod; /* X[:,lag]'*t */
ptr1--; /* Go to next column of X */
}
}
else {
Inlines.OpusAssert(rshifts == 0);
for (lag = 0; lag < order; lag++)
{
Xt[lag] = Inlines.silk_inner_prod(x, ptr1, t, ptr2, L); /* X[:,lag]'*t */
ptr1--; /* Go to next column of X */
}
}
}
/* Calculates correlation matrix X'*X */
internal static void silk_corrMatrix(
short[] x, /* I x vector [L + order - 1] used to form data matrix X */
int x_ptr,
int L, /* I Length of vectors */
int order, /* I Max lag for correlation */
int head_room, /* I Desired headroom */
int[] XX, /* O Pointer to X'*X correlation matrix [ order x order ] */
int XX_ptr,
BoxedValueInt rshifts /* I/O Right shifts of correlations */
)
{
int i, j, lag, head_room_rshifts;
int energy, rshifts_local;
int ptr1, ptr2;
/* Calculate energy to find shift used to fit in 32 bits */
SumSqrShift.silk_sum_sqr_shift(out energy, out rshifts_local, x, x_ptr, L + order - 1);
/* Add shifts to get the desired head room */
head_room_rshifts = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0);
energy = Inlines.silk_RSHIFT32(energy, head_room_rshifts);
rshifts_local += head_room_rshifts;
/* Calculate energy of first column (0) of X: X[:,0]'*X[:,0] */
/* Remove contribution of first order - 1 samples */
for (i = x_ptr; i < x_ptr + order - 1; i++)
{
energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), rshifts_local);
}
if (rshifts_local < rshifts.Val)
{
/* Adjust energy */
energy = Inlines.silk_RSHIFT32(energy, rshifts.Val - rshifts_local);
rshifts_local = rshifts.Val;
}
/* Calculate energy of remaining columns of X: X[:,j]'*X[:,j] */
/* Fill out the diagonal of the correlation matrix */
Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy);
ptr1 = x_ptr + order - 1; /* First sample of column 0 of X */
for (j = 1; j < order; j++)
{
energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr1 + L - j]), rshifts_local));
energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr1 - j]), rshifts_local));
Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy);
}
ptr2 = x_ptr + order - 2; /* First sample of column 1 of X */
/* Calculate the remaining elements of the correlation matrix */
if (rshifts_local > 0)
{
/* Right shifting used */
for (lag = 1; lag < order; lag++)
{
/* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */
energy = 0;
for (i = 0; i < L; i++)
{
energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], x[ptr2 + i]), rshifts_local);
}
/* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */
Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy);
Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy);
for (j = 1; j < (order - lag); j++)
{
energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]), rshifts_local));
energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr2 - j]), rshifts_local));
Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy);
Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy);
}
ptr2--; /* Update pointer to first sample of next column (lag) in X */
}
}
else {
for (lag = 1; lag < order; lag++)
{
/* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */
energy = Inlines.silk_inner_prod(x, ptr1, x, ptr2, L);
Inlines.MatrixSet(XX, XX_ptr, lag, 0, order,energy);
Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy);
/* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */
for (j = 1; j < (order - lag); j++)
{
energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]));
energy = Inlines.silk_SMLABB(energy, x[ptr1 - j], x[ptr2 - j]);
Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy);
Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy);
}
ptr2--;/* Update pointer to first sample of next column (lag) in X */
}
}
rshifts.Val = rshifts_local;
}
}
}

View File

@@ -0,0 +1,461 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class DecodeAPI
{
/// <summary>
/// Reset decoder state
/// </summary>
/// <param name="decState">I/O Stat</param>
/// <returns>Returns error code</returns>
internal static int silk_InitDecoder(SilkDecoder decState)
{
/* Reset decoder */
decState.Reset();
int n, ret = SilkError.SILK_NO_ERROR;
SilkChannelDecoder[] channel_states = decState.channel_state;
for (n = 0; n < SilkConstants.DECODER_NUM_CHANNELS; n++)
{
ret = channel_states[n].silk_init_decoder();
}
decState.sStereo.Reset();
/* Not strictly needed, but it's cleaner that way */
decState.prev_decode_only_middle = 0;
return ret;
}
/* Decode a frame */
internal static int silk_Decode( /* O Returns error code */
SilkDecoder psDec, /* I/O State */
DecControlState decControl, /* I/O Control Structure */
int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */
int newPacketFlag, /* I Indicates first decoder call for this packet */
EntropyCoder psRangeDec, /* I/O Compressor data structure */
short[] samplesOut, /* O Decoded output speech vector */
int samplesOut_ptr,
out int nSamplesOut /* O Number of samples decoded */
)
{
int i, n, decode_only_middle = 0, ret = SilkError.SILK_NO_ERROR;
int LBRR_symbol;
BoxedValueInt nSamplesOutDec = new BoxedValueInt();
short[] samplesOut_tmp;
int[] samplesOut_tmp_ptrs = new int[2];
short[] samplesOut1_tmp_storage1;
short[] samplesOut1_tmp_storage2;
short[] samplesOut2_tmp;
int[] MS_pred_Q13 = new int[] { 0, 0 };
short[] resample_out;
int resample_out_ptr;
SilkChannelDecoder[] channel_state = psDec.channel_state;
int has_side;
int stereo_to_mono;
int delay_stack_alloc;
nSamplesOut = 0;
Inlines.OpusAssert(decControl.nChannelsInternal == 1 || decControl.nChannelsInternal == 2);
/**********************************/
/* Test if first frame in payload */
/**********************************/
if (newPacketFlag != 0)
{
for (n = 0; n < decControl.nChannelsInternal; n++)
{
channel_state[n].nFramesDecoded = 0; /* Used to count frames in packet */
}
}
/* If Mono . Stereo transition in bitstream: init state of second channel */
if (decControl.nChannelsInternal > psDec.nChannelsInternal)
{
ret += channel_state[1].silk_init_decoder();
}
stereo_to_mono = (decControl.nChannelsInternal == 1 && psDec.nChannelsInternal == 2 &&
(decControl.internalSampleRate == 1000 * channel_state[0].fs_kHz)) ? 1 : 0;
if (channel_state[0].nFramesDecoded == 0)
{
for (n = 0; n < decControl.nChannelsInternal; n++)
{
int fs_kHz_dec;
if (decControl.payloadSize_ms == 0)
{
/* Assuming packet loss, use 10 ms */
channel_state[n].nFramesPerPacket = 1;
channel_state[n].nb_subfr = 2;
}
else if (decControl.payloadSize_ms == 10)
{
channel_state[n].nFramesPerPacket = 1;
channel_state[n].nb_subfr = 2;
}
else if (decControl.payloadSize_ms == 20)
{
channel_state[n].nFramesPerPacket = 1;
channel_state[n].nb_subfr = 4;
}
else if (decControl.payloadSize_ms == 40)
{
channel_state[n].nFramesPerPacket = 2;
channel_state[n].nb_subfr = 4;
}
else if (decControl.payloadSize_ms == 60)
{
channel_state[n].nFramesPerPacket = 3;
channel_state[n].nb_subfr = 4;
}
else {
Inlines.OpusAssert(false);
return SilkError.SILK_DEC_INVALID_FRAME_SIZE;
}
fs_kHz_dec = (decControl.internalSampleRate >> 10) + 1;
if (fs_kHz_dec != 8 && fs_kHz_dec != 12 && fs_kHz_dec != 16)
{
Inlines.OpusAssert(false);
return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY;
}
ret += channel_state[n].silk_decoder_set_fs(fs_kHz_dec, decControl.API_sampleRate);
}
}
if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2 && (psDec.nChannelsAPI == 1 || psDec.nChannelsInternal == 1))
{
Arrays.MemSetShort(psDec.sStereo.pred_prev_Q13, 0, 2);
Arrays.MemSetShort(psDec.sStereo.sSide, 0, 2);
channel_state[1].resampler_state.Assign(channel_state[0].resampler_state);
}
psDec.nChannelsAPI = decControl.nChannelsAPI;
psDec.nChannelsInternal = decControl.nChannelsInternal;
if (decControl.API_sampleRate > (int)SilkConstants.MAX_API_FS_KHZ * 1000 || decControl.API_sampleRate < 8000)
{
ret = SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY;
return (ret);
}
if (lostFlag != DecoderAPIFlag.FLAG_PACKET_LOST && channel_state[0].nFramesDecoded == 0)
{
/* First decoder call for this payload */
/* Decode VAD flags and LBRR flag */
for (n = 0; n < decControl.nChannelsInternal; n++)
{
for (i = 0; i < channel_state[n].nFramesPerPacket; i++)
{
channel_state[n].VAD_flags[i] = psRangeDec.dec_bit_logp(1);
}
channel_state[n].LBRR_flag = psRangeDec.dec_bit_logp(1);
}
/* Decode LBRR flags */
for (n = 0; n < decControl.nChannelsInternal; n++)
{
Arrays.MemSetInt(channel_state[n].LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET);
if (channel_state[n].LBRR_flag != 0)
{
if (channel_state[n].nFramesPerPacket == 1)
{
channel_state[n].LBRR_flags[0] = 1;
}
else {
LBRR_symbol = psRangeDec.dec_icdf(Tables.silk_LBRR_flags_iCDF_ptr[channel_state[n].nFramesPerPacket - 2], 8) + 1;
for (i = 0; i < channel_state[n].nFramesPerPacket; i++)
{
channel_state[n].LBRR_flags[i] = Inlines.silk_RSHIFT(LBRR_symbol, i) & 1;
}
}
}
}
if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL)
{
/* Regular decoding: skip all LBRR data */
for (i = 0; i < channel_state[0].nFramesPerPacket; i++)
{
for (n = 0; n < decControl.nChannelsInternal; n++)
{
if (channel_state[n].LBRR_flags[i] != 0)
{
short[] pulses = new short[SilkConstants.MAX_FRAME_LENGTH];
int condCoding;
if (decControl.nChannelsInternal == 2 && n == 0)
{
Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13);
if (channel_state[1].LBRR_flags[i] == 0)
{
BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle);
Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed);
decode_only_middle = decodeOnlyMiddleBoxed.Val;
}
}
/* Use conditional coding if previous frame available */
if (i > 0 && (channel_state[n].LBRR_flags[i - 1] != 0))
{
condCoding = SilkConstants.CODE_CONDITIONALLY;
}
else
{
condCoding = SilkConstants.CODE_INDEPENDENTLY;
}
DecodeIndices.silk_decode_indices(channel_state[n], psRangeDec, i, 1, condCoding);
DecodePulses.silk_decode_pulses(psRangeDec, pulses, channel_state[n].indices.signalType,
channel_state[n].indices.quantOffsetType, channel_state[n].frame_length);
}
}
}
}
}
/* Get MS predictor index */
if (decControl.nChannelsInternal == 2)
{
if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL ||
(lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && channel_state[0].LBRR_flags[channel_state[0].nFramesDecoded] == 1))
{
Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13);
/* For LBRR data, decode mid-only flag only if side-channel's LBRR flag is false */
if ((lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL && channel_state[1].VAD_flags[channel_state[0].nFramesDecoded] == 0) ||
(lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && channel_state[1].LBRR_flags[channel_state[0].nFramesDecoded] == 0))
{
BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle);
Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed);
decode_only_middle = decodeOnlyMiddleBoxed.Val;
}
else
{
decode_only_middle = 0;
}
}
else
{
for (n = 0; n < 2; n++)
{
MS_pred_Q13[n] = psDec.sStereo.pred_prev_Q13[n];
}
}
}
/* Reset side channel decoder prediction memory for first frame with side coding */
if (decControl.nChannelsInternal == 2 && decode_only_middle == 0 && psDec.prev_decode_only_middle == 1)
{
Arrays.MemSetShort(psDec.channel_state[1].outBuf, 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH);
Arrays.MemSetInt(psDec.channel_state[1].sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER);
psDec.channel_state[1].lagPrev = 100;
psDec.channel_state[1].LastGainIndex = 10;
psDec.channel_state[1].prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY;
psDec.channel_state[1].first_frame_after_reset = 1;
}
/* Check if the temp buffer fits into the output PCM buffer. If it fits,
we can delay allocating the temp buffer until after the SILK peak stack
usage. We need to use a < and not a <= because of the two extra samples. */
delay_stack_alloc = (decControl.internalSampleRate * decControl.nChannelsInternal
< decControl.API_sampleRate * decControl.nChannelsAPI) ? 1 : 0;
if (delay_stack_alloc != 0)
{
samplesOut_tmp = samplesOut;
samplesOut_tmp_ptrs[0] = samplesOut_ptr;
samplesOut_tmp_ptrs[1] = samplesOut_ptr + channel_state[0].frame_length + 2;
}
else
{
samplesOut1_tmp_storage1 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)];
samplesOut_tmp = samplesOut1_tmp_storage1;
samplesOut_tmp_ptrs[0] = 0;
samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2;
}
if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL)
{
has_side = (decode_only_middle == 0) ? 1 : 0;
}
else
{
has_side = (psDec.prev_decode_only_middle == 0
|| (decControl.nChannelsInternal == 2 &&
lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR &&
channel_state[1].LBRR_flags[channel_state[1].nFramesDecoded] == 1)) ? 1 : 0;
}
/* Call decoder for one frame */
for (n = 0; n < decControl.nChannelsInternal; n++)
{
if (n == 0 || (has_side != 0))
{
int FrameIndex;
int condCoding;
FrameIndex = channel_state[0].nFramesDecoded - n;
/* Use independent coding if no previous frame available */
if (FrameIndex <= 0)
{
condCoding = SilkConstants.CODE_INDEPENDENTLY;
}
else if (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR)
{
condCoding = (channel_state[n].LBRR_flags[FrameIndex - 1] != 0) ? SilkConstants.CODE_CONDITIONALLY : SilkConstants.CODE_INDEPENDENTLY;
}
else if (n > 0 && (psDec.prev_decode_only_middle != 0))
{
/* If we skipped a side frame in this packet, we don't
need LTP scaling; the LTP state is well-defined. */
condCoding = SilkConstants.CODE_INDEPENDENTLY_NO_LTP_SCALING;
}
else
{
condCoding = SilkConstants.CODE_CONDITIONALLY;
}
ret += channel_state[n].silk_decode_frame(psRangeDec, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec, lostFlag, condCoding);
}
else
{
Arrays.MemSetWithOffset<short>(samplesOut_tmp, 0, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec.Val);
}
channel_state[n].nFramesDecoded++;
}
if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2)
{
/* Convert Mid/Side to Left/Right */
Stereo.silk_stereo_MS_to_LR(psDec.sStereo, samplesOut_tmp, samplesOut_tmp_ptrs[0], samplesOut_tmp, samplesOut_tmp_ptrs[1], MS_pred_Q13, channel_state[0].fs_kHz, nSamplesOutDec.Val);
}
else
{
/* Buffering */
Array.Copy(psDec.sStereo.sMid, 0, samplesOut_tmp, samplesOut_tmp_ptrs[0], 2);
Array.Copy(samplesOut_tmp, samplesOut_tmp_ptrs[0] + nSamplesOutDec.Val, psDec.sStereo.sMid, 0, 2);
}
/* Number of output samples */
nSamplesOut = Inlines.silk_DIV32(nSamplesOutDec.Val * decControl.API_sampleRate, Inlines.silk_SMULBB(channel_state[0].fs_kHz, 1000));
/* Set up pointers to temp buffers */
if (decControl.nChannelsAPI == 2)
{
samplesOut2_tmp = new short[nSamplesOut];
resample_out = samplesOut2_tmp;
resample_out_ptr = 0;
}
else {
resample_out = samplesOut;
resample_out_ptr = samplesOut_ptr;
}
if (delay_stack_alloc != 0)
{
samplesOut1_tmp_storage2 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)];
Array.Copy(samplesOut, samplesOut_ptr, samplesOut1_tmp_storage2, 0, decControl.nChannelsInternal * (channel_state[0].frame_length + 2));
samplesOut_tmp = samplesOut1_tmp_storage2;
samplesOut_tmp_ptrs[0] = 0;
samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2;
}
for (n = 0; n < Inlines.silk_min(decControl.nChannelsAPI, decControl.nChannelsInternal); n++)
{
/* Resample decoded signal to API_sampleRate */
ret += Resampler.silk_resampler(channel_state[n].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 1, nSamplesOutDec.Val);
/* Interleave if stereo output and stereo stream */
if (decControl.nChannelsAPI == 2)
{
int nptr = samplesOut_ptr + n;
for (i = 0; i < nSamplesOut; i++)
{
samplesOut[nptr + 2 * i] = resample_out[resample_out_ptr + i];
}
}
}
/* Create two channel output from mono stream */
if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 1)
{
if (stereo_to_mono != 0)
{
/* Resample right channel for newly collapsed stereo just in case
we weren't doing collapsing when switching to mono */
ret += Resampler.silk_resampler(channel_state[1].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[0] + 1, nSamplesOutDec.Val);
for (i = 0; i < nSamplesOut; i++)
{
samplesOut[samplesOut_ptr + 1 + 2 * i] = resample_out[resample_out_ptr + i];
}
}
else {
for (i = 0; i < nSamplesOut; i++)
{
samplesOut[samplesOut_ptr + 1 + 2 * i] = samplesOut[samplesOut_ptr + 2 * i];
}
}
}
/* Export pitch lag, measured at 48 kHz sampling rate */
if (channel_state[0].prevSignalType == SilkConstants.TYPE_VOICED)
{
int[] mult_tab = { 6, 4, 3 };
decControl.prevPitchLag = channel_state[0].lagPrev * mult_tab[(channel_state[0].fs_kHz - 8) >> 2];
}
else
{
decControl.prevPitchLag = 0;
}
if (lostFlag == DecoderAPIFlag.FLAG_PACKET_LOST)
{
/* On packet loss, remove the gain clamping to prevent having the energy "bounce back"
if we lose packets when the energy is going down */
for (i = 0; i < psDec.nChannelsInternal; i++)
psDec.channel_state[i].LastGainIndex = 10;
}
else
{
psDec.prev_decode_only_middle = decode_only_middle;
}
return ret;
}
}
}

View File

@@ -0,0 +1,278 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class DecodeCore
{
/**********************************************************/
/* Core decoder. Performs inverse NSQ operation LTP + LPC */
/**********************************************************/
internal static void silk_decode_core(
SilkChannelDecoder psDec, /* I/O Decoder state */
SilkDecoderControl psDecCtrl, /* I Decoder control */
short[] xq, /* O Decoded speech */
int xq_ptr,
short[] pulses /* I Pulse signal [MAX_FRAME_LENGTH] */
)
{
int i, k, lag = 0, start_idx, sLTP_buf_idx, NLSF_interpolation_flag, signalType;
short[] A_Q12;
short[] B_Q14 = psDecCtrl.LTPCoef_Q14;
int B_Q14_ptr;
int pxq;
short[] sLTP;
int[] sLTP_Q15;
int LTP_pred_Q13, LPC_pred_Q10, Gain_Q10, inv_gain_Q31, gain_adj_Q16, rand_seed, offset_Q10;
int pred_lag_ptr;
int pexc_Q14;
int[] pres_Q14;
int pres_Q14_ptr;
int[] res_Q14;
int[] sLPC_Q14;
Inlines.OpusAssert(psDec.prev_gain_Q16 != 0);
sLTP= new short[psDec.ltp_mem_length];
sLTP_Q15 = new int[psDec.ltp_mem_length + psDec.frame_length];
res_Q14 = new int[psDec.subfr_length];
sLPC_Q14 = new int[psDec.subfr_length + SilkConstants.MAX_LPC_ORDER];
offset_Q10 = Tables.silk_Quantization_Offsets_Q10[psDec.indices.signalType >> 1][psDec.indices.quantOffsetType];
if (psDec.indices.NLSFInterpCoef_Q2 < 1 << 2)
{
NLSF_interpolation_flag = 1;
}
else {
NLSF_interpolation_flag = 0;
}
/* Decode excitation */
rand_seed = psDec.indices.Seed;
for (i = 0; i < psDec.frame_length; i++)
{
rand_seed = Inlines.silk_RAND(rand_seed);
psDec.exc_Q14[i] = Inlines.silk_LSHIFT((int)pulses[i], 14);
if (psDec.exc_Q14[i] > 0)
{
psDec.exc_Q14[i] -= SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4;
}
else
if (psDec.exc_Q14[i] < 0)
{
psDec.exc_Q14[i] += SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4;
}
psDec.exc_Q14[i] += offset_Q10 << 4;
if (rand_seed < 0)
{
psDec.exc_Q14[i] = -psDec.exc_Q14[i];
}
rand_seed = Inlines.silk_ADD32_ovflw(rand_seed, pulses[i]);
}
/* Copy LPC state */
Array.Copy(psDec.sLPC_Q14_buf, sLPC_Q14, SilkConstants.MAX_LPC_ORDER);
pexc_Q14 = 0;
pxq = xq_ptr;
sLTP_buf_idx = psDec.ltp_mem_length;
/* Loop over subframes */
for (k = 0; k < psDec.nb_subfr; k++)
{
pres_Q14 = res_Q14;
pres_Q14_ptr = 0;
A_Q12 = psDecCtrl.PredCoef_Q12[k >> 1];
B_Q14_ptr = k * SilkConstants.LTP_ORDER;
signalType = psDec.indices.signalType;
Gain_Q10 = Inlines.silk_RSHIFT(psDecCtrl.Gains_Q16[k], 6);
inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(psDecCtrl.Gains_Q16[k], 47);
/* Calculate gain adjustment factor */
if (psDecCtrl.Gains_Q16[k] != psDec.prev_gain_Q16)
{
gain_adj_Q16 = Inlines.silk_DIV32_varQ(psDec.prev_gain_Q16, psDecCtrl.Gains_Q16[k], 16);
/* Scale short term state */
for (i = 0; i < SilkConstants.MAX_LPC_ORDER; i++)
{
sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLPC_Q14[i]);
}
}
else {
gain_adj_Q16 = (int)1 << 16;
}
/* Save inv_gain */
Inlines.OpusAssert(inv_gain_Q31 != 0);
psDec.prev_gain_Q16 = psDecCtrl.Gains_Q16[k];
/* Avoid abrupt transition from voiced PLC to unvoiced normal decoding */
if (psDec.lossCnt != 0 && psDec.prevSignalType == SilkConstants.TYPE_VOICED &&
psDec.indices.signalType != SilkConstants.TYPE_VOICED && k < SilkConstants.MAX_NB_SUBFR / 2)
{
Arrays.MemSetWithOffset<short>(B_Q14, 0, B_Q14_ptr, SilkConstants.LTP_ORDER);
B_Q14[B_Q14_ptr + (SilkConstants.LTP_ORDER / 2)] = (short)(((int)((0.25f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.25f, 14)*/);
signalType = SilkConstants.TYPE_VOICED;
psDecCtrl.pitchL[k] = psDec.lagPrev;
}
if (signalType == SilkConstants.TYPE_VOICED)
{
/* Voiced */
lag = psDecCtrl.pitchL[k];
/* Re-whitening */
if (k == 0 || (k == 2 && (NLSF_interpolation_flag != 0)))
{
/* Rewhiten with new A coefs */
start_idx = psDec.ltp_mem_length - lag - psDec.LPC_order - SilkConstants.LTP_ORDER / 2;
Inlines.OpusAssert(start_idx > 0);
if (k == 2)
{
Array.Copy(xq, xq_ptr, psDec.outBuf, psDec.ltp_mem_length, 2 * psDec.subfr_length);
}
Filters.silk_LPC_analysis_filter(sLTP, start_idx, psDec.outBuf, (start_idx + k * psDec.subfr_length),
A_Q12, 0, psDec.ltp_mem_length - start_idx, psDec.LPC_order);
/* After rewhitening the LTP state is unscaled */
if (k == 0)
{
/* Do LTP downscaling to reduce inter-packet dependency */
inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, psDecCtrl.LTP_scale_Q14), 2);
}
for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++)
{
sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[psDec.ltp_mem_length - i - 1]);
}
}
else {
/* Update LTP state when Gain changes */
if (gain_adj_Q16 != (int)1 << 16)
{
for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++)
{
sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[sLTP_buf_idx - i - 1]);
}
}
}
}
/* Long-term prediction */
if (signalType == SilkConstants.TYPE_VOICED)
{
/* Set up pointer */
pred_lag_ptr = sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2;
for (i = 0; i < psDec.subfr_length; i++)
{
/* Unrolled loop */
/* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
LTP_pred_Q13 = 2;
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr], B_Q14[B_Q14_ptr]);
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 1], B_Q14[B_Q14_ptr + 1]);
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 2], B_Q14[B_Q14_ptr + 2]);
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 3], B_Q14[B_Q14_ptr + 3]);
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 4], B_Q14[B_Q14_ptr + 4]);
pred_lag_ptr += 1;
/* Generate LPC excitation */
pres_Q14[pres_Q14_ptr + i] = Inlines.silk_ADD_LSHIFT32(psDec.exc_Q14[pexc_Q14 + i], LTP_pred_Q13, 1);
/* Update states */
sLTP_Q15[sLTP_buf_idx] = Inlines.silk_LSHIFT(pres_Q14[pres_Q14_ptr + i], 1);
sLTP_buf_idx++;
}
}
else {
pres_Q14 = psDec.exc_Q14;
pres_Q14_ptr = pexc_Q14;
}
for (i = 0; i < psDec.subfr_length; i++)
{
/* Short-term prediction */
Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16);
/* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 1], A_Q12[0]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 2], A_Q12[1]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 3], A_Q12[2]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 4], A_Q12[3]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 5], A_Q12[4]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 6], A_Q12[5]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 7], A_Q12[6]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 8], A_Q12[7]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 9], A_Q12[8]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 10], A_Q12[9]);
if (psDec.LPC_order == 16)
{
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 11], A_Q12[10]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 12], A_Q12[11]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 13], A_Q12[12]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 14], A_Q12[13]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 15], A_Q12[14]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 16], A_Q12[15]);
}
/* Add prediction to LPC excitation */
sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i] = Inlines.silk_ADD_LSHIFT32(pres_Q14[pres_Q14_ptr + i], LPC_pred_Q10, 4);
/* Scale with gain */
xq[pxq + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i], Gain_Q10), 8));
}
/* DEBUG_STORE_DATA( dec.pcm, pxq, psDec.subfr_length * sizeof( short ) ) */
/* Update LPC filter state */
Array.Copy(sLPC_Q14, psDec.subfr_length, sLPC_Q14, 0, SilkConstants.MAX_LPC_ORDER);
pexc_Q14 += psDec.subfr_length;
pxq += psDec.subfr_length;
}
/* Save LPC state */
Array.Copy(sLPC_Q14, 0, psDec.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER);
}
}
}

View File

@@ -0,0 +1,179 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class DecodeIndices
{
/* Decode side-information parameters from payload */
internal static void silk_decode_indices(
SilkChannelDecoder psDec, /* I/O State */
EntropyCoder psRangeDec, /* I/O Compressor data structure */
int FrameIndex, /* I Frame number */
int decode_LBRR, /* I Flag indicating LBRR data is being decoded */
int condCoding /* I The type of conditional coding to use */
)
{
int i, k, Ix;
int decode_absolute_lagIndex, delta_lagIndex;
short[] ec_ix = new short[psDec.LPC_order];
byte[] pred_Q8 = new byte[psDec.LPC_order];
/*******************************************/
/* Decode signal type and quantizer offset */
/*******************************************/
if (decode_LBRR != 0 || psDec.VAD_flags[FrameIndex] != 0)
{
Ix = psRangeDec.dec_icdf(Tables.silk_type_offset_VAD_iCDF, 8) + 2;
}
else {
Ix = psRangeDec.dec_icdf(Tables.silk_type_offset_no_VAD_iCDF, 8);
}
psDec.indices.signalType = (sbyte)Inlines.silk_RSHIFT(Ix, 1);
psDec.indices.quantOffsetType = (sbyte)(Ix & 1);
/****************/
/* Decode gains */
/****************/
/* First subframe */
if (condCoding == SilkConstants.CODE_CONDITIONALLY)
{
/* Conditional coding */
psDec.indices.GainsIndices[0] = (sbyte)psRangeDec.dec_icdf(Tables.silk_delta_gain_iCDF, 8);
}
else {
/* Independent coding, in two stages: MSB bits followed by 3 LSBs */
psDec.indices.GainsIndices[0] = (sbyte)Inlines.silk_LSHIFT(psRangeDec.dec_icdf(Tables.silk_gain_iCDF[psDec.indices.signalType], 8), 3);
psDec.indices.GainsIndices[0] += (sbyte)psRangeDec.dec_icdf(Tables.silk_uniform8_iCDF, 8);
}
/* Remaining subframes */
for (i = 1; i < psDec.nb_subfr; i++)
{
psDec.indices.GainsIndices[i] = (sbyte)psRangeDec.dec_icdf(Tables.silk_delta_gain_iCDF, 8);
}
/**********************/
/* Decode LSF Indices */
/**********************/
psDec.indices.NLSFIndices[0] = (sbyte)psRangeDec.dec_icdf(psDec.psNLSF_CB.CB1_iCDF, (psDec.indices.signalType >> 1) * psDec.psNLSF_CB.nVectors, 8);
NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psDec.psNLSF_CB, psDec.indices.NLSFIndices[0]);
Inlines.OpusAssert(psDec.psNLSF_CB.order == psDec.LPC_order);
for (i = 0; i < psDec.psNLSF_CB.order; i++)
{
Ix = psRangeDec.dec_icdf(psDec.psNLSF_CB.ec_iCDF, (ec_ix[i]), 8);
if (Ix == 0)
{
Ix -= psRangeDec.dec_icdf(Tables.silk_NLSF_EXT_iCDF, 8);
}
else if (Ix == 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE)
{
Ix += psRangeDec.dec_icdf(Tables.silk_NLSF_EXT_iCDF, 8);
}
psDec.indices.NLSFIndices[i + 1] = (sbyte)(Ix - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE);
}
/* Decode LSF interpolation factor */
if (psDec.nb_subfr == SilkConstants.MAX_NB_SUBFR)
{
psDec.indices.NLSFInterpCoef_Q2 = (sbyte)psRangeDec.dec_icdf(Tables.silk_NLSF_interpolation_factor_iCDF, 8);
}
else {
psDec.indices.NLSFInterpCoef_Q2 = 4;
}
if (psDec.indices.signalType == SilkConstants.TYPE_VOICED)
{
/*********************/
/* Decode pitch lags */
/*********************/
/* Get lag index */
decode_absolute_lagIndex = 1;
if (condCoding == SilkConstants.CODE_CONDITIONALLY && psDec.ec_prevSignalType == SilkConstants.TYPE_VOICED)
{
/* Decode Delta index */
delta_lagIndex = (short)psRangeDec.dec_icdf(Tables.silk_pitch_delta_iCDF, 8);
if (delta_lagIndex > 0)
{
delta_lagIndex = delta_lagIndex - 9;
psDec.indices.lagIndex = (short)(psDec.ec_prevLagIndex + delta_lagIndex);
decode_absolute_lagIndex = 0;
}
}
if (decode_absolute_lagIndex != 0)
{
/* Absolute decoding */
psDec.indices.lagIndex = (short)(psRangeDec.dec_icdf(Tables.silk_pitch_lag_iCDF, 8) * Inlines.silk_RSHIFT(psDec.fs_kHz, 1));
psDec.indices.lagIndex += (short)psRangeDec.dec_icdf(psDec.pitch_lag_low_bits_iCDF, 8);
}
psDec.ec_prevLagIndex = psDec.indices.lagIndex;
/* Get countour index */
psDec.indices.contourIndex = (sbyte)psRangeDec.dec_icdf(psDec.pitch_contour_iCDF, 8);
/********************/
/* Decode LTP gains */
/********************/
/* Decode PERIndex value */
psDec.indices.PERIndex = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTP_per_index_iCDF, 8);
for (k = 0; k < psDec.nb_subfr; k++)
{
psDec.indices.LTPIndex[k] = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTP_gain_iCDF_ptrs[psDec.indices.PERIndex], 8);
}
/**********************/
/* Decode LTP scaling */
/**********************/
if (condCoding == SilkConstants.CODE_INDEPENDENTLY)
{
psDec.indices.LTP_scaleIndex = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTPscale_iCDF, 8);
}
else {
psDec.indices.LTP_scaleIndex = 0;
}
}
psDec.ec_prevSignalType = psDec.indices.signalType;
/***************/
/* Decode seed */
/***************/
psDec.indices.Seed = (sbyte)psRangeDec.dec_icdf(Tables.silk_uniform4_iCDF, 8);
}
}
}

View File

@@ -0,0 +1,141 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal class DecodeParameters
{
/* Decode parameters from payload */
internal static void silk_decode_parameters(
SilkChannelDecoder psDec, /* I/O State */
SilkDecoderControl psDecCtrl, /* I/O Decoder control */
int condCoding /* I The type of conditional coding to use */
)
{
int i, k, Ix;
short[] pNLSF_Q15 = new short[psDec.LPC_order];
short[] pNLSF0_Q15 = new short[psDec.LPC_order];
sbyte[][] cbk_ptr_Q7;
/* Dequant Gains */
BoxedValueSbyte boxedLastGainIndex = new BoxedValueSbyte(psDec.LastGainIndex);
GainQuantization.silk_gains_dequant(psDecCtrl.Gains_Q16, psDec.indices.GainsIndices,
boxedLastGainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, psDec.nb_subfr);
psDec.LastGainIndex = boxedLastGainIndex.Val;
/****************/
/* Decode NLSFs */
/****************/
NLSF.silk_NLSF_decode(pNLSF_Q15, psDec.indices.NLSFIndices, psDec.psNLSF_CB);
/* Convert NLSF parameters to AR prediction filter coefficients */
NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[1], pNLSF_Q15, psDec.LPC_order);
/* If just reset, e.g., because internal Fs changed, do not allow interpolation */
/* improves the case of packet loss in the first frame after a switch */
if (psDec.first_frame_after_reset == 1)
{
psDec.indices.NLSFInterpCoef_Q2 = 4;
}
if (psDec.indices.NLSFInterpCoef_Q2 < 4)
{
/* Calculation of the interpolated NLSF0 vector from the interpolation factor, */
/* the previous NLSF1, and the current NLSF1 */
for (i = 0; i < psDec.LPC_order; i++)
{
pNLSF0_Q15[i] = (short)(psDec.prevNLSF_Q15[i] + Inlines.silk_RSHIFT(Inlines.silk_MUL(psDec.indices.NLSFInterpCoef_Q2,
pNLSF_Q15[i] - psDec.prevNLSF_Q15[i]), 2));
}
/* Convert NLSF parameters to AR prediction filter coefficients */
NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[0], pNLSF0_Q15, psDec.LPC_order);
}
else
{
/* Copy LPC coefficients for first half from second half */
Array.Copy(psDecCtrl.PredCoef_Q12[1], psDecCtrl.PredCoef_Q12[0], psDec.LPC_order);
}
Array.Copy(pNLSF_Q15, psDec.prevNLSF_Q15, psDec.LPC_order);
/* After a packet loss do BWE of LPC coefs */
if (psDec.lossCnt != 0)
{
BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[0], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16);
BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[1], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16);
}
if (psDec.indices.signalType == SilkConstants.TYPE_VOICED)
{
/*********************/
/* Decode pitch lags */
/*********************/
/* Decode pitch values */
DecodePitch.silk_decode_pitch(psDec.indices.lagIndex, psDec.indices.contourIndex, psDecCtrl.pitchL, psDec.fs_kHz, psDec.nb_subfr);
/* Decode Codebook Index */
cbk_ptr_Q7 = Tables.silk_LTP_vq_ptrs_Q7[psDec.indices.PERIndex]; /* set pointer to start of codebook */
for (k = 0; k < psDec.nb_subfr; k++)
{
Ix = psDec.indices.LTPIndex[k];
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
psDecCtrl.LTPCoef_Q14[k * SilkConstants.LTP_ORDER + i] = (short)(Inlines.silk_LSHIFT(cbk_ptr_Q7[Ix][i], 7));
}
}
/**********************/
/* Decode LTP scaling */
/**********************/
Ix = psDec.indices.LTP_scaleIndex;
psDecCtrl.LTP_scale_Q14 = Tables.silk_LTPScales_table_Q14[Ix];
}
else
{
Arrays.MemSetInt(psDecCtrl.pitchL, 0, psDec.nb_subfr);
Arrays.MemSetShort(psDecCtrl.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER * psDec.nb_subfr);
psDec.indices.PERIndex = 0;
psDecCtrl.LTP_scale_Q14 = 0;
}
}
}
}

View File

@@ -0,0 +1,90 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class DecodePitch
{
internal static void silk_decode_pitch(
short lagIndex, /* I */
sbyte contourIndex, /* O */
int[] pitch_lags, /* O 4 pitch values */
int Fs_kHz, /* I sampling frequency (kHz) */
int nb_subfr /* I number of sub frames */
)
{
int lag, k, min_lag, max_lag;
sbyte[][] Lag_CB_ptr;
if (Fs_kHz == 8)
{
if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
{
Lag_CB_ptr = Tables.silk_CB_lags_stage2;
}
else
{
Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1);
Lag_CB_ptr = Tables.silk_CB_lags_stage2_10_ms;
}
}
else
{
if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
{
Lag_CB_ptr = Tables.silk_CB_lags_stage3;
}
else
{
Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1);
Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms;
}
}
min_lag = Inlines.silk_SMULBB(SilkConstants.PE_MIN_LAG_MS, Fs_kHz);
max_lag = Inlines.silk_SMULBB(SilkConstants.PE_MAX_LAG_MS, Fs_kHz);
lag = min_lag + lagIndex;
for (k = 0; k < nb_subfr; k++)
{
pitch_lags[k] = lag + Lag_CB_ptr[k][contourIndex];
pitch_lags[k] = Inlines.silk_LIMIT(pitch_lags[k], min_lag, max_lag);
}
}
}
}

View File

@@ -0,0 +1,136 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class DecodePulses
{
/*********************************************/
/* Decode quantization indices of excitation */
/*********************************************/
internal static void silk_decode_pulses(
EntropyCoder psRangeDec, /* I/O Compressor data structure */
short[] pulses, /* O Excitation signal */
int signalType, /* I Sigtype */
int quantOffsetType, /* I quantOffsetType */
int frame_length /* I Frame length */
)
{
int i, j, k, iter, abs_q, nLS, RateLevelIndex;
int[] sum_pulses = new int[SilkConstants.MAX_NB_SHELL_BLOCKS];
int[] nLshifts = new int[SilkConstants.MAX_NB_SHELL_BLOCKS];
int pulses_ptr;
/*********************/
/* Decode rate level */
/*********************/
RateLevelIndex = psRangeDec.dec_icdf(Tables.silk_rate_levels_iCDF[signalType >> 1], 8);
/* Calculate number of shell blocks */
Inlines.OpusAssert(1 << SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH == SilkConstants.SHELL_CODEC_FRAME_LENGTH);
iter = Inlines.silk_RSHIFT(frame_length, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH);
if (iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH < frame_length)
{
Inlines.OpusAssert(frame_length == 12 * 10); /* Make sure only happens for 10 ms @ 12 kHz */
iter++;
}
/***************************************************/
/* Sum-Weighted-Pulses Decoding */
/***************************************************/
for (i = 0; i < iter; i++)
{
nLshifts[i] = 0;
sum_pulses[i] = psRangeDec.dec_icdf(Tables.silk_pulses_per_block_iCDF[RateLevelIndex], 8);
/* LSB indication */
while (sum_pulses[i] == SilkConstants.SILK_MAX_PULSES + 1)
{
nLshifts[i]++;
/* When we've already got 10 LSBs, we shift the table to not allow (SILK_MAX_PULSES + 1) */
sum_pulses[i] = psRangeDec.dec_icdf(
Tables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], (nLshifts[i] == 10 ? 1 : 0), 8);
}
}
/***************************************************/
/* Shell decoding */
/***************************************************/
for (i = 0; i < iter; i++)
{
if (sum_pulses[i] > 0)
{
ShellCoder.silk_shell_decoder(pulses, Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH), psRangeDec, sum_pulses[i]);
}
else
{
Arrays.MemSetWithOffset<short>(pulses, 0, Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH), SilkConstants.SHELL_CODEC_FRAME_LENGTH);
}
}
/***************************************************/
/* LSB Decoding */
/***************************************************/
for (i = 0; i < iter; i++)
{
if (nLshifts[i] > 0)
{
nLS = nLshifts[i];
pulses_ptr = Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH);
for (k = 0; k < SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++)
{
abs_q = pulses[pulses_ptr + k];
for (j = 0; j < nLS; j++)
{
abs_q = Inlines.silk_LSHIFT(abs_q, 1);
abs_q += psRangeDec.dec_icdf(Tables.silk_lsb_iCDF, 8);
}
pulses[pulses_ptr + k] = (short)(abs_q);
}
/* Mark the number of pulses non-zero for sign decoding. */
sum_pulses[i] |= nLS << 5;
}
}
/****************************************/
/* Decode and add signs to pulse signal */
/****************************************/
CodeSigns.silk_decode_signs(psRangeDec, pulses, frame_length, signalType, quantOffsetType, sum_pulses);
}
}
}

View File

@@ -0,0 +1,740 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class EncodeAPI
{
/// <summary>
/// Init or Reset encoder
/// </summary>
/// <param name="encState">I/O State</param>
/// <param name="encStatus">O Encoder Status</param>
/// <returns>O Returns error code</returns>
internal static int silk_InitEncoder(SilkEncoder encState, EncControlState encStatus)
{
int ret = SilkError.SILK_NO_ERROR;
/* Reset encoder */
encState.Reset();
for (int n = 0; n < SilkConstants.ENCODER_NUM_CHANNELS; n++)
{
ret += SilkEncoder.silk_init_encoder(encState.state_Fxx[n]);
Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR);
}
encState.nChannelsAPI = 1;
encState.nChannelsInternal = 1;
/* Read control structure */
ret += silk_QueryEncoder(encState, encStatus);
Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR);
return ret;
}
/// <summary>
/// Read control structure from encode
/// </summary>
/// <param name="encState">I State</param>
/// <param name="encStatus">O Encoder Status</param>
/// <returns>Returns error code</returns>
internal static int silk_QueryEncoder(SilkEncoder encState, EncControlState encStatus)
{
int ret = SilkError.SILK_NO_ERROR;
SilkChannelEncoder state_Fxx = encState.state_Fxx[0];
encStatus.Reset();
encStatus.nChannelsAPI = encState.nChannelsAPI;
encStatus.nChannelsInternal = encState.nChannelsInternal;
encStatus.API_sampleRate = state_Fxx.API_fs_Hz;
encStatus.maxInternalSampleRate = state_Fxx.maxInternal_fs_Hz;
encStatus.minInternalSampleRate = state_Fxx.minInternal_fs_Hz;
encStatus.desiredInternalSampleRate = state_Fxx.desiredInternal_fs_Hz;
encStatus.payloadSize_ms = state_Fxx.PacketSize_ms;
encStatus.bitRate = state_Fxx.TargetRate_bps;
encStatus.packetLossPercentage = state_Fxx.PacketLoss_perc;
encStatus.complexity = state_Fxx.Complexity;
encStatus.useInBandFEC = state_Fxx.useInBandFEC;
encStatus.useDTX = state_Fxx.useDTX;
encStatus.useCBR = state_Fxx.useCBR;
encStatus.internalSampleRate = Inlines.silk_SMULBB(state_Fxx.fs_kHz, 1000);
encStatus.allowBandwidthSwitch = state_Fxx.allow_bandwidth_switch;
encStatus.inWBmodeWithoutVariableLP = (state_Fxx.fs_kHz == 16 && state_Fxx.sLP.mode == 0) ? 1 : 0;
return ret;
}
/// <summary>
/// Encode frame with Silk
/// Note: if prefillFlag is set, the input must contain 10 ms of audio, irrespective of what
/// encControl.payloadSize_ms is set to
/// </summary>
/// <param name="psEnc">I/O State</param>
/// <param name="encControl">I Control status</param>
/// <param name="samplesIn">I Speech sample input vector</param>
/// <param name="nSamplesIn">I Number of samples in input vector</param>
/// <param name="psRangeEnc">I/O Compressor data structure</param>
/// <param name="nBytesOut">I/O Number of bytes in payload (input: Max bytes)</param>
/// <param name="prefillFlag">I Flag to indicate prefilling buffers no coding</param>
/// <returns>error code</returns>
internal static int silk_Encode(
SilkEncoder psEnc,
EncControlState encControl,
short[] samplesIn,
int nSamplesIn,
EntropyCoder psRangeEnc,
BoxedValueInt nBytesOut,
int prefillFlag)
{
int ret = SilkError.SILK_NO_ERROR;
int n, i, nBits, flags, tmp_payloadSize_ms = 0, tmp_complexity = 0;
int nSamplesToBuffer, nSamplesToBufferMax, nBlocksOf10ms;
int nSamplesFromInput = 0, nSamplesFromInputMax;
int speech_act_thr_for_switch_Q8;
int TargetRate_bps, channelRate_bps, LBRR_symbol, sum;
int[] MStargetRates_bps = new int[2];
short[] buf;
int transition, curr_block, tot_blocks;
nBytesOut.Val = 0;
if (encControl.reducedDependency != 0)
{
psEnc.state_Fxx[0].first_frame_after_reset = 1;
psEnc.state_Fxx[1].first_frame_after_reset = 1;
}
psEnc.state_Fxx[0].nFramesEncoded = psEnc.state_Fxx[1].nFramesEncoded = 0;
/* Check values in encoder control structure */
ret += encControl.check_control_input();
if (ret != SilkError.SILK_NO_ERROR)
{
Inlines.OpusAssert(false);
return ret;
}
encControl.switchReady = 0;
if (encControl.nChannelsInternal > psEnc.nChannelsInternal)
{
/* Mono . Stereo transition: init state of second channel and stereo state */
ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[1]);
Arrays.MemSetShort(psEnc.sStereo.pred_prev_Q13, 0, 2);
Arrays.MemSetShort(psEnc.sStereo.sSide, 0, 2);
psEnc.sStereo.mid_side_amp_Q0[0] = 0;
psEnc.sStereo.mid_side_amp_Q0[1] = 1;
psEnc.sStereo.mid_side_amp_Q0[2] = 0;
psEnc.sStereo.mid_side_amp_Q0[3] = 1;
psEnc.sStereo.width_prev_Q14 = 0;
psEnc.sStereo.smth_width_Q14 = (short)(((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/);
if (psEnc.nChannelsAPI == 2)
{
psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state);
Array.Copy(psEnc.state_Fxx[0].In_HP_State, psEnc.state_Fxx[1].In_HP_State, 2);
}
}
transition = ((encControl.payloadSize_ms != psEnc.state_Fxx[0].PacketSize_ms) || (psEnc.nChannelsInternal != encControl.nChannelsInternal)) ? 1 : 0;
psEnc.nChannelsAPI = encControl.nChannelsAPI;
psEnc.nChannelsInternal = encControl.nChannelsInternal;
nBlocksOf10ms = Inlines.silk_DIV32(100 * nSamplesIn, encControl.API_sampleRate);
tot_blocks = (nBlocksOf10ms > 1) ? nBlocksOf10ms >> 1 : 1;
curr_block = 0;
if (prefillFlag != 0)
{
/* Only accept input length of 10 ms */
if (nBlocksOf10ms != 1)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES;
}
/* Reset Encoder */
for (n = 0; n < encControl.nChannelsInternal; n++)
{
ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[n]);
Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR);
}
tmp_payloadSize_ms = encControl.payloadSize_ms;
encControl.payloadSize_ms = 10;
tmp_complexity = encControl.complexity;
encControl.complexity = 0;
for (n = 0; n < encControl.nChannelsInternal; n++)
{
psEnc.state_Fxx[n].controlled_since_last_payload = 0;
psEnc.state_Fxx[n].prefillFlag = 1;
}
}
else
{
/* Only accept input lengths that are a multiple of 10 ms */
if (nBlocksOf10ms * encControl.API_sampleRate != 100 * nSamplesIn || nSamplesIn < 0)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES;
}
/* Make sure no more than one packet can be produced */
if (1000 * (int)nSamplesIn > encControl.payloadSize_ms * encControl.API_sampleRate)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES;
}
}
TargetRate_bps = Inlines.silk_RSHIFT32(encControl.bitRate, encControl.nChannelsInternal - 1);
for (n = 0; n < encControl.nChannelsInternal; n++)
{
/* Force the side channel to the same rate as the mid */
int force_fs_kHz = (n == 1) ? psEnc.state_Fxx[0].fs_kHz : 0;
ret += psEnc.state_Fxx[n].silk_control_encoder(encControl, TargetRate_bps, psEnc.allowBandwidthSwitch, n, force_fs_kHz);
if (ret != SilkError.SILK_NO_ERROR)
{
Inlines.OpusAssert(false);
return ret;
}
if (psEnc.state_Fxx[n].first_frame_after_reset != 0 || transition != 0)
{
for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; i++)
{
psEnc.state_Fxx[n].LBRR_flags[i] = 0;
}
}
psEnc.state_Fxx[n].inDTX = psEnc.state_Fxx[n].useDTX;
}
Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[0].fs_kHz == psEnc.state_Fxx[1].fs_kHz);
/* Input buffering/resampling and encoding */
nSamplesToBufferMax = 10 * nBlocksOf10ms * psEnc.state_Fxx[0].fs_kHz;
nSamplesFromInputMax =
Inlines.silk_DIV32_16(nSamplesToBufferMax *
psEnc.state_Fxx[0].API_fs_Hz,
(short)(psEnc.state_Fxx[0].fs_kHz * 1000));
buf = new short[nSamplesFromInputMax];
int samplesIn_ptr = 0;
while (true)
{
nSamplesToBuffer = psEnc.state_Fxx[0].frame_length - psEnc.state_Fxx[0].inputBufIx;
nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, nSamplesToBufferMax);
nSamplesFromInput = Inlines.silk_DIV32_16(nSamplesToBuffer * psEnc.state_Fxx[0].API_fs_Hz, psEnc.state_Fxx[0].fs_kHz * 1000);
/* Resample and write to buffer */
if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 2)
{
int id = psEnc.state_Fxx[0].nFramesEncoded;
for (n = 0; n < nSamplesFromInput; n++)
{
buf[n] = samplesIn[samplesIn_ptr + (2 * n)];
}
/* Making sure to start both resamplers from the same state when switching from mono to stereo */
if (psEnc.nPrevChannelsInternal == 1 && id == 0)
{
//silk_memcpy(&psEnc.state_Fxx[1].resampler_state, &psEnc.state_Fxx[0].resampler_state, sizeof(psEnc.state_Fxx[1].resampler_state));
psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state);
}
ret += Resampler.silk_resampler(
psEnc.state_Fxx[0].resampler_state,
psEnc.state_Fxx[0].inputBuf,
psEnc.state_Fxx[0].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer;
nSamplesToBuffer = psEnc.state_Fxx[1].frame_length - psEnc.state_Fxx[1].inputBufIx;
nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, 10 * nBlocksOf10ms * psEnc.state_Fxx[1].fs_kHz);
for (n = 0; n < nSamplesFromInput; n++)
{
buf[n] = samplesIn[samplesIn_ptr + (2 * n) + 1];
}
ret += Resampler.silk_resampler(
psEnc.state_Fxx[1].resampler_state,
psEnc.state_Fxx[1].inputBuf,
psEnc.state_Fxx[1].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
psEnc.state_Fxx[1].inputBufIx += nSamplesToBuffer;
}
else if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 1)
{
/* Combine left and right channels before resampling */
for (n = 0; n < nSamplesFromInput; n++)
{
sum = samplesIn[samplesIn_ptr + (2 * n)] + samplesIn[samplesIn_ptr + (2 * n) + 1];
buf[n] = (short)Inlines.silk_RSHIFT_ROUND(sum, 1);
}
ret += Resampler.silk_resampler(
psEnc.state_Fxx[0].resampler_state,
psEnc.state_Fxx[0].inputBuf,
psEnc.state_Fxx[0].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
/* On the first mono frame, average the results for the two resampler states */
if (psEnc.nPrevChannelsInternal == 2 && psEnc.state_Fxx[0].nFramesEncoded == 0)
{
ret += Resampler.silk_resampler(
psEnc.state_Fxx[1].resampler_state,
psEnc.state_Fxx[1].inputBuf,
psEnc.state_Fxx[1].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
for (n = 0; n < psEnc.state_Fxx[0].frame_length; n++)
{
psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2] =
(short)(Inlines.silk_RSHIFT(psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2]
+ psEnc.state_Fxx[1].inputBuf[psEnc.state_Fxx[1].inputBufIx + n + 2], 1));
}
}
psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer;
}
else
{
Inlines.OpusAssert(encControl.nChannelsAPI == 1 && encControl.nChannelsInternal == 1);
Array.Copy(samplesIn, samplesIn_ptr, buf, 0, nSamplesFromInput);
ret += Resampler.silk_resampler(
psEnc.state_Fxx[0].resampler_state,
psEnc.state_Fxx[0].inputBuf,
psEnc.state_Fxx[0].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer;
}
samplesIn_ptr += (nSamplesFromInput * encControl.nChannelsAPI);
nSamplesIn -= nSamplesFromInput;
/* Default */
psEnc.allowBandwidthSwitch = 0;
/* Silk encoder */
if (psEnc.state_Fxx[0].inputBufIx >= psEnc.state_Fxx[0].frame_length)
{
/* Enough data in input buffer, so encode */
Inlines.OpusAssert(psEnc.state_Fxx[0].inputBufIx == psEnc.state_Fxx[0].frame_length);
Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inputBufIx == psEnc.state_Fxx[1].frame_length);
/* Deal with LBRR data */
if (psEnc.state_Fxx[0].nFramesEncoded == 0 && prefillFlag == 0)
{
/* Create space at start of payload for VAD and FEC flags */
byte[] iCDF = { 0, 0 };
iCDF[0] = (byte)(256 - Inlines.silk_RSHIFT(256, (psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal));
psRangeEnc.enc_icdf(0, iCDF, 8);
/* Encode any LBRR data from previous packet */
/* Encode LBRR flags */
for (n = 0; n < encControl.nChannelsInternal; n++)
{
LBRR_symbol = 0;
for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; i++)
{
LBRR_symbol |= Inlines.silk_LSHIFT(psEnc.state_Fxx[n].LBRR_flags[i], i);
}
psEnc.state_Fxx[n].LBRR_flag = (sbyte)(LBRR_symbol > 0 ? 1 : 0);
if (LBRR_symbol != 0 && psEnc.state_Fxx[n].nFramesPerPacket > 1)
{
psRangeEnc.enc_icdf( LBRR_symbol - 1, Tables.silk_LBRR_flags_iCDF_ptr[psEnc.state_Fxx[n].nFramesPerPacket - 2], 8);
}
}
/* Code LBRR indices and excitation signals */
for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; i++)
{
for (n = 0; n < encControl.nChannelsInternal; n++)
{
if (psEnc.state_Fxx[n].LBRR_flags[i] != 0)
{
int condCoding;
if (encControl.nChannelsInternal == 2 && n == 0)
{
Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[i]);
/* For LBRR data there's no need to code the mid-only flag if the side-channel LBRR flag is set */
if (psEnc.state_Fxx[1].LBRR_flags[i] == 0)
{
Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[i]);
}
}
/* Use conditional coding if previous frame available */
if (i > 0 && psEnc.state_Fxx[n].LBRR_flags[i - 1] != 0)
{
condCoding = SilkConstants.CODE_CONDITIONALLY;
}
else
{
condCoding = SilkConstants.CODE_INDEPENDENTLY;
}
EncodeIndices.silk_encode_indices(psEnc.state_Fxx[n], psRangeEnc, i, 1, condCoding);
EncodePulses.silk_encode_pulses(psRangeEnc, psEnc.state_Fxx[n].indices_LBRR[i].signalType, psEnc.state_Fxx[n].indices_LBRR[i].quantOffsetType,
psEnc.state_Fxx[n].pulses_LBRR[i], psEnc.state_Fxx[n].frame_length);
}
}
}
/* Reset LBRR flags */
for (n = 0; n < encControl.nChannelsInternal; n++)
{
Arrays.MemSetInt(psEnc.state_Fxx[n].LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET);
}
psEnc.nBitsUsedLBRR = psRangeEnc.tell();
}
HPVariableCutoff.silk_HP_variable_cutoff(psEnc.state_Fxx);
/* Total target bits for packet */
nBits = Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000);
/* Subtract bits used for LBRR */
if (prefillFlag == 0)
{
nBits -= psEnc.nBitsUsedLBRR;
}
/* Divide by number of uncoded frames left in packet */
nBits = Inlines.silk_DIV32_16(nBits, psEnc.state_Fxx[0].nFramesPerPacket);
/* Convert to bits/second */
if (encControl.payloadSize_ms == 10)
{
TargetRate_bps = Inlines.silk_SMULBB(nBits, 100);
}
else
{
TargetRate_bps = Inlines.silk_SMULBB(nBits, 50);
}
/* Subtract fraction of bits in excess of target in previous frames and packets */
TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(psEnc.nBitsExceeded, 1000), TuningParameters.BITRESERVOIR_DECAY_TIME_MS);
if (prefillFlag == 0 && psEnc.state_Fxx[0].nFramesEncoded > 0)
{
/* Compare actual vs target bits so far in this packet */
int bitsBalance = psRangeEnc.tell() - psEnc.nBitsUsedLBRR - nBits * psEnc.state_Fxx[0].nFramesEncoded;
TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(bitsBalance, 1000), TuningParameters.BITRESERVOIR_DECAY_TIME_MS);
}
/* Never exceed input bitrate */
TargetRate_bps = Inlines.silk_LIMIT(TargetRate_bps, encControl.bitRate, 5000);
/* Convert Left/Right to Mid/Side */
if (encControl.nChannelsInternal == 2)
{
BoxedValueSbyte midOnlyFlagBoxed = new BoxedValueSbyte(psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]);
Stereo.silk_stereo_LR_to_MS(psEnc.sStereo,
psEnc.state_Fxx[0].inputBuf,
2,
psEnc.state_Fxx[1].inputBuf,
2,
psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded],
midOnlyFlagBoxed,
MStargetRates_bps,
TargetRate_bps,
psEnc.state_Fxx[0].speech_activity_Q8,
encControl.toMono,
psEnc.state_Fxx[0].fs_kHz,
psEnc.state_Fxx[0].frame_length);
psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded] = midOnlyFlagBoxed.Val;
if (midOnlyFlagBoxed.Val == 0)
{
/* Reset side channel encoder memory for first frame with side coding */
if (psEnc.prev_decode_only_middle == 1)
{
psEnc.state_Fxx[1].sShape.Reset();
psEnc.state_Fxx[1].sPrefilt.Reset();
psEnc.state_Fxx[1].sNSQ.Reset();
Arrays.MemSetShort(psEnc.state_Fxx[1].prev_NLSFq_Q15, 0, SilkConstants.MAX_LPC_ORDER);
Arrays.MemSetInt(psEnc.state_Fxx[1].sLP.In_LP_State, 0, 2);
psEnc.state_Fxx[1].prevLag = 100;
psEnc.state_Fxx[1].sNSQ.lagPrev = 100;
psEnc.state_Fxx[1].sShape.LastGainIndex = 10;
psEnc.state_Fxx[1].prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY;
psEnc.state_Fxx[1].sNSQ.prev_gain_Q16 = 65536;
psEnc.state_Fxx[1].first_frame_after_reset = 1;
}
psEnc.state_Fxx[1].silk_encode_do_VAD();
}
else
{
psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] = 0;
}
if (prefillFlag == 0)
{
Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded]);
if (psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] == 0)
{
Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]);
}
}
}
else
{
/* Buffering */
Array.Copy(psEnc.sStereo.sMid, psEnc.state_Fxx[0].inputBuf, 2);
Array.Copy(psEnc.state_Fxx[0].inputBuf, psEnc.state_Fxx[0].frame_length, psEnc.sStereo.sMid, 0, 2);
}
psEnc.state_Fxx[0].silk_encode_do_VAD();
/* Encode */
for (n = 0; n < encControl.nChannelsInternal; n++)
{
int maxBits, useCBR;
/* Handling rate constraints */
maxBits = encControl.maxBits;
if (tot_blocks == 2 && curr_block == 0)
{
maxBits = maxBits * 3 / 5;
}
else if (tot_blocks == 3)
{
if (curr_block == 0)
{
maxBits = maxBits * 2 / 5;
}
else if (curr_block == 1)
{
maxBits = maxBits * 3 / 4;
}
}
useCBR = (encControl.useCBR != 0 && curr_block == tot_blocks - 1) ? 1 : 0;
if (encControl.nChannelsInternal == 1)
{
channelRate_bps = TargetRate_bps;
}
else
{
channelRate_bps = MStargetRates_bps[n];
if (n == 0 && MStargetRates_bps[1] > 0)
{
useCBR = 0;
/* Give mid up to 1/2 of the max bits for that frame */
maxBits -= encControl.maxBits / (tot_blocks * 2);
}
}
if (channelRate_bps > 0)
{
int condCoding;
psEnc.state_Fxx[n].silk_control_SNR(channelRate_bps);
/* Use independent coding if no previous frame available */
if (psEnc.state_Fxx[0].nFramesEncoded - n <= 0)
{
condCoding = SilkConstants.CODE_INDEPENDENTLY;
}
else if (n > 0 && psEnc.prev_decode_only_middle != 0)
{
/* If we skipped a side frame in this packet, we don't
need LTP scaling; the LTP state is well-defined. */
condCoding = SilkConstants.CODE_INDEPENDENTLY_NO_LTP_SCALING;
}
else
{
condCoding = SilkConstants.CODE_CONDITIONALLY;
}
ret += psEnc.state_Fxx[n].silk_encode_frame(nBytesOut, psRangeEnc, condCoding, maxBits, useCBR);
Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR);
}
psEnc.state_Fxx[n].controlled_since_last_payload = 0;
psEnc.state_Fxx[n].inputBufIx = 0;
psEnc.state_Fxx[n].nFramesEncoded++;
}
psEnc.prev_decode_only_middle = psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded - 1];
/* Insert VAD and FEC flags at beginning of bitstream */
if (nBytesOut.Val > 0 && psEnc.state_Fxx[0].nFramesEncoded == psEnc.state_Fxx[0].nFramesPerPacket)
{
flags = 0;
for (n = 0; n < encControl.nChannelsInternal; n++)
{
for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; i++)
{
flags = Inlines.silk_LSHIFT(flags, 1);
flags |= (int)psEnc.state_Fxx[n].VAD_flags[i];
}
flags = Inlines.silk_LSHIFT(flags, 1);
flags |= (int)psEnc.state_Fxx[n].LBRR_flag;
}
if (prefillFlag == 0)
{
psRangeEnc.enc_patch_initial_bits((uint)flags, (uint)((psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal));
}
/* Return zero bytes if all channels DTXed */
if (psEnc.state_Fxx[0].inDTX != 0 && (encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inDTX != 0))
{
nBytesOut.Val = 0;
}
psEnc.nBitsExceeded += nBytesOut.Val * 8;
psEnc.nBitsExceeded -= Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000);
psEnc.nBitsExceeded = Inlines.silk_LIMIT(psEnc.nBitsExceeded, 0, 10000);
/* Update flag indicating if bandwidth switching is allowed */
speech_act_thr_for_switch_Q8 = Inlines.silk_SMLAWB(((int)((TuningParameters.SPEECH_ACTIVITY_DTX_THRES) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPEECH_ACTIVITY_DTX_THRES, 8)*/,
((int)(((1 - TuningParameters.SPEECH_ACTIVITY_DTX_THRES) / TuningParameters.MAX_BANDWIDTH_SWITCH_DELAY_MS) * ((long)1 << (16 + 8)) + 0.5))/*Inlines.SILK_CONST((1 - TuningParameters.SPEECH_ACTIVITY_DTX_THRES) / TuningParameters.MAX_BANDWIDTH_SWITCH_DELAY_MS, 16 + 8)*/,
psEnc.timeSinceSwitchAllowed_ms);
if (psEnc.state_Fxx[0].speech_activity_Q8 < speech_act_thr_for_switch_Q8)
{
psEnc.allowBandwidthSwitch = 1;
psEnc.timeSinceSwitchAllowed_ms = 0;
}
else
{
psEnc.allowBandwidthSwitch = 0;
psEnc.timeSinceSwitchAllowed_ms += encControl.payloadSize_ms;
}
}
if (nSamplesIn == 0)
{
break;
}
}
else
{
break;
}
curr_block++;
}
psEnc.nPrevChannelsInternal = encControl.nChannelsInternal;
encControl.allowBandwidthSwitch = psEnc.allowBandwidthSwitch;
encControl.inWBmodeWithoutVariableLP = (psEnc.state_Fxx[0].fs_kHz == 16 && psEnc.state_Fxx[0].sLP.mode == 0) ? 1 : 0;
encControl.internalSampleRate = Inlines.silk_SMULBB(psEnc.state_Fxx[0].fs_kHz, 1000);
encControl.stereoWidth_Q14 = encControl.toMono != 0 ? 0 : psEnc.sStereo.smth_width_Q14;
if (prefillFlag != 0)
{
encControl.payloadSize_ms = tmp_payloadSize_ms;
encControl.complexity = tmp_complexity;
for (n = 0; n < encControl.nChannelsInternal; n++)
{
psEnc.state_Fxx[n].controlled_since_last_payload = 0;
psEnc.state_Fxx[n].prefillFlag = 0;
}
}
return ret;
}
}
}
//────────────────────▄▄▄▄▄▄▄▄▄▄────────────────────
//─────────────▄▄▄▄████▓▓▓▓▓▓▓▓▓██▄▄────────────────
//─────────▄▄██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒░▀▀▄─────────────
//────────▀▀▀▀▀█████▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░▀▄───────────
//────────────▄▄█▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░▀▄─────────
//─────▄▀▀▄─▄█▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░█▄▀▀▄─────
//────█░░░▄█▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░█░░░░░█────
//───█░░░█▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░▄▀░░░▄░░█───
//──█░░░█▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░█░░░░░█░░█──
//──█░░█▓▒▒▄▄▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░██░░░░░░█░█──
//─█░░█▓▄▄▀█▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░▄▄▀█░░░░▄▀█░░░░░░█░░█─
//─█░██▀░▄▀▒▒▒▒▒▒▒▒▒▒▒▒▄▄▄▄▀▀▀░▄▀░░░▄▀░░░░░░░░░█░░█─
//─██▀░░█▒▒▒▒▒▒▒▒▒▄▄▀▀▀░░░░░░▄▀░░▄▄▀░░░░░░░░░░░░░░█─
//──█░░█▒▒▒▒▒▒▒▄▀▀░░░░░░░░░░▀▀▀▀▀░░░░░░░░░░▄▄░░░░░█─
//───██▒▒▒▒▒▒▄▀██▄▄░░░░░█░░░░░░░░░░░░░▄▄▄█▀░░░░░░█──
//────█▒▒▒▒▒█─▓██████▄▄█░░░█░░░░▄▄▄████▓─█▀▀░░░░█───
//───█▒▒▒▒▄█─▓█──████▓─█░░░░████████──█▓─█▄░░░▄▀────
//───█▒▒▄▀█──▓█▒▓████▓─█░░░░█─▓█████▓▒█▓─█░░█▀──────
//──█▒▄▀█░█──▓███─███▓─█░░░░█─▓████─██▓──█░█────────
//──█▀──█░█──▓▓██████▓─█░░░░█─▓██████▓▓──█░█────────
//──────█░░█──▓▓████▓─█░░░░░░█─▓████▓▓──█░░█────────
//──────█░░▀▄───▓▓▓▓──█░░░░░░█──▓▓▓▓───▄▀░░█────────
//───────█░░▀▄───────█░░░░░░░░█───────▄▀░░█─────────
//───────█░░░░▀▄▄▄▄▄▀░░▄▀▀▀▀▄░░▀▄▄▄▄▄▀░░░░█─────────
//────────█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█──────────
//─────────█░░░░░░░░░░▀▄░░░░▄▀░░░░░░░░░░█───────────
//──────────▀▄░░░░░░░░░░░░░░░░░░░░░░░░▄▀────────────
//────────────▀▄░░░░░░▀▄░░░░▄▀░░░░░░▄▀──────────────
//──────────────▀▄▄░░░░░▀▀▀▀░░░░░▄▄▀────────────────
//─────────────────▀▀▄▄▄▄▄▄▄▄▄▄▀▀───────────────────
//──────────────────────────────────────────────────
//───────█───█───█─████──███──███──█───█─████─█─────
//──────█─█──█───█─█────█────█───█─██─██─█────█─────
//─────█▄▄▄█─█─█─█─██────██──█───█─█─█─█─██───█─────
//─────█───█──█─█──█───────█─█───█─█───█─█──────────
//─────█───█──█─█──████─███───███──█───█─████─█─────
//──────────────────────────────────────────────────
//──────────────────────────────────────────────────

View File

@@ -0,0 +1,223 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class EncodeIndices
{
/// <summary>
/// Encode side-information parameters to payload
/// </summary>
/// <param name="psEncC">I/O Encoder state</param>
/// <param name="psRangeEnc">I/O Compressor data structure</param>
/// <param name="FrameIndex">I Frame number</param>
/// <param name="encode_LBRR">I Flag indicating LBRR data is being encoded</param>
/// <param name="condCoding">I The type of conditional coding to use</param>
internal static void silk_encode_indices(
SilkChannelEncoder psEncC,
EntropyCoder psRangeEnc,
int FrameIndex,
int encode_LBRR,
int condCoding)
{
int i, k, typeOffset;
int encode_absolute_lagIndex, delta_lagIndex;
short[] ec_ix = new short[SilkConstants.MAX_LPC_ORDER];
byte[] pred_Q8 = new byte[SilkConstants.MAX_LPC_ORDER];
SideInfoIndices psIndices;
if (encode_LBRR != 0)
{
psIndices = psEncC.indices_LBRR[FrameIndex];
}
else {
psIndices = psEncC.indices;
}
/*******************************************/
/* Encode signal type and quantizer offset */
/*******************************************/
typeOffset = 2 * psIndices.signalType + psIndices.quantOffsetType;
Inlines.OpusAssert(typeOffset >= 0 && typeOffset < 6);
Inlines.OpusAssert(encode_LBRR == 0 || typeOffset >= 2);
if (encode_LBRR != 0 || typeOffset >= 2)
{
psRangeEnc.enc_icdf( typeOffset - 2, Tables.silk_type_offset_VAD_iCDF, 8);
}
else
{
psRangeEnc.enc_icdf( typeOffset, Tables.silk_type_offset_no_VAD_iCDF, 8);
}
/****************/
/* Encode gains */
/****************/
/* first subframe */
if (condCoding == SilkConstants.CODE_CONDITIONALLY)
{
/* conditional coding */
Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.MIN_DELTA_GAIN_QUANT + 1);
psRangeEnc.enc_icdf( psIndices.GainsIndices[0], Tables.silk_delta_gain_iCDF, 8);
}
else
{
/* independent coding, in two stages: MSB bits followed by 3 LSBs */
Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < SilkConstants.N_LEVELS_QGAIN);
psRangeEnc.enc_icdf( Inlines.silk_RSHIFT(psIndices.GainsIndices[0], 3), Tables.silk_gain_iCDF[psIndices.signalType], 8);
psRangeEnc.enc_icdf( psIndices.GainsIndices[0] & 7, Tables.silk_uniform8_iCDF, 8);
}
/* remaining subframes */
for (i = 1; i < psEncC.nb_subfr; i++)
{
Inlines.OpusAssert(psIndices.GainsIndices[i] >= 0 && psIndices.GainsIndices[i] < SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.MIN_DELTA_GAIN_QUANT + 1);
psRangeEnc.enc_icdf( psIndices.GainsIndices[i], Tables.silk_delta_gain_iCDF, 8);
}
/****************/
/* Encode NLSFs */
/****************/
psRangeEnc.enc_icdf( psIndices.NLSFIndices[0], psEncC.psNLSF_CB.CB1_iCDF, ((psIndices.signalType >> 1) * psEncC.psNLSF_CB.nVectors), 8);
NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psEncC.psNLSF_CB, psIndices.NLSFIndices[0]);
Inlines.OpusAssert(psEncC.psNLSF_CB.order == psEncC.predictLPCOrder);
for (i = 0; i < psEncC.psNLSF_CB.order; i++)
{
if (psIndices.NLSFIndices[i + 1] >= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE)
{
psRangeEnc.enc_icdf( 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, psEncC.psNLSF_CB.ec_iCDF, (ec_ix[i]), 8);
psRangeEnc.enc_icdf( psIndices.NLSFIndices[i + 1] - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, Tables.silk_NLSF_EXT_iCDF, 8);
}
else if (psIndices.NLSFIndices[i + 1] <= 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE)
{
psRangeEnc.enc_icdf( 0, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8);
psRangeEnc.enc_icdf( -psIndices.NLSFIndices[i + 1] - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, Tables.silk_NLSF_EXT_iCDF, 8);
}
else
{
psRangeEnc.enc_icdf( psIndices.NLSFIndices[i + 1] + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8);
}
}
/* Encode NLSF interpolation factor */
if (psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR)
{
Inlines.OpusAssert(psIndices.NLSFInterpCoef_Q2 >= 0 && psIndices.NLSFInterpCoef_Q2 < 5);
psRangeEnc.enc_icdf( psIndices.NLSFInterpCoef_Q2, Tables.silk_NLSF_interpolation_factor_iCDF, 8);
}
if (psIndices.signalType == SilkConstants.TYPE_VOICED)
{
/*********************/
/* Encode pitch lags */
/*********************/
/* lag index */
encode_absolute_lagIndex = 1;
if (condCoding == SilkConstants.CODE_CONDITIONALLY && psEncC.ec_prevSignalType == SilkConstants.TYPE_VOICED)
{
/* Delta Encoding */
delta_lagIndex = psIndices.lagIndex - psEncC.ec_prevLagIndex;
if (delta_lagIndex < -8 || delta_lagIndex > 11)
{
delta_lagIndex = 0;
}
else
{
delta_lagIndex = delta_lagIndex + 9;
encode_absolute_lagIndex = 0; /* Only use delta */
}
Inlines.OpusAssert(delta_lagIndex >= 0 && delta_lagIndex < 21);
psRangeEnc.enc_icdf( delta_lagIndex, Tables.silk_pitch_delta_iCDF, 8);
}
if (encode_absolute_lagIndex != 0)
{
/* Absolute encoding */
int pitch_high_bits, pitch_low_bits;
pitch_high_bits = Inlines.silk_DIV32_16(psIndices.lagIndex, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1));
pitch_low_bits = psIndices.lagIndex - Inlines.silk_SMULBB(pitch_high_bits, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1));
Inlines.OpusAssert(pitch_low_bits < psEncC.fs_kHz / 2);
Inlines.OpusAssert(pitch_high_bits < 32);
psRangeEnc.enc_icdf( pitch_high_bits, Tables.silk_pitch_lag_iCDF, 8);
psRangeEnc.enc_icdf( pitch_low_bits, psEncC.pitch_lag_low_bits_iCDF, 8);
}
psEncC.ec_prevLagIndex = psIndices.lagIndex;
/* Countour index */
Inlines.OpusAssert(psIndices.contourIndex >= 0);
Inlines.OpusAssert((psIndices.contourIndex < 34 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 4) || (psIndices.contourIndex < 11 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 4) || (psIndices.contourIndex < 12 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 2) || (psIndices.contourIndex < 3 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 2));
psRangeEnc.enc_icdf( psIndices.contourIndex, psEncC.pitch_contour_iCDF, 8);
/********************/
/* Encode LTP gains */
/********************/
/* PERIndex value */
Inlines.OpusAssert(psIndices.PERIndex >= 0 && psIndices.PERIndex < 3);
psRangeEnc.enc_icdf( psIndices.PERIndex, Tables.silk_LTP_per_index_iCDF, 8);
/* Codebook Indices */
for (k = 0; k < psEncC.nb_subfr; k++)
{
Inlines.OpusAssert(psIndices.LTPIndex[k] >= 0 && psIndices.LTPIndex[k] < (8 << psIndices.PERIndex));
psRangeEnc.enc_icdf( psIndices.LTPIndex[k], Tables.silk_LTP_gain_iCDF_ptrs[psIndices.PERIndex], 8);
}
/**********************/
/* Encode LTP scaling */
/**********************/
if (condCoding == SilkConstants.CODE_INDEPENDENTLY)
{
Inlines.OpusAssert(psIndices.LTP_scaleIndex >= 0 && psIndices.LTP_scaleIndex < 3);
psRangeEnc.enc_icdf( psIndices.LTP_scaleIndex, Tables.silk_LTPscale_iCDF, 8);
}
Inlines.OpusAssert(condCoding == 0 || psIndices.LTP_scaleIndex == 0);
}
psEncC.ec_prevSignalType = psIndices.signalType;
/***************/
/* Encode seed */
/***************/
Inlines.OpusAssert(psIndices.Seed >= 0 && psIndices.Seed < 4);
psRangeEnc.enc_icdf( psIndices.Seed, Tables.silk_uniform4_iCDF, 8);
}
}
}

View File

@@ -0,0 +1,278 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class EncodePulses
{
/// <summary>
///
/// </summary>
/// <param name="pulses_comb">(O)</param>
/// <param name="pulses_in">(I)</param>
/// <param name="max_pulses"> I max value for sum of pulses</param>
/// <param name="len">I number of output values</param>
/// <returns>return ok</returns>
internal static int combine_and_check(
int[] pulses_comb,
int pulses_comb_ptr,
int[] pulses_in,
int pulses_in_ptr,
int max_pulses,
int len)
{
for (int k = 0; k < len; k++)
{
int k2p = 2 * k + pulses_in_ptr;
int sum = pulses_in[k2p] + pulses_in[k2p + 1];
if (sum > max_pulses)
{
return 1;
}
pulses_comb[pulses_comb_ptr + k] = sum;
}
return 0;
}
/// <summary>
///
/// </summary>
/// <param name="pulses_comb">(O)</param>
/// <param name="pulses_in">(I)</param>
/// <param name="max_pulses"> I max value for sum of pulses</param>
/// <param name="len">I number of output values</param>
/// <returns>return ok</returns>
internal static int combine_and_check(
int[] pulses_comb,
int[] pulses_in,
int max_pulses,
int len)
{
for (int k = 0; k < len; k++)
{
int sum = pulses_in[2 * k] + pulses_in[2 * k + 1];
if (sum > max_pulses)
{
return 1;
}
pulses_comb[k] = sum;
}
return 0;
}
/// <summary>
/// Encode quantization indices of excitation
/// </summary>
/// <param name="psRangeEnc">I/O compressor data structure</param>
/// <param name="signalType">I Signal type</param>
/// <param name="quantOffsetType">I quantOffsetType</param>
/// <param name="pulses">I quantization indices</param>
/// <param name="frame_length">I Frame length</param>
internal static void silk_encode_pulses(
EntropyCoder psRangeEnc,
int signalType,
int quantOffsetType,
sbyte[] pulses,
int frame_length)
{
int i, k, j, iter, bit, nLS, scale_down, RateLevelIndex = 0;
int abs_q, minSumBits_Q5, sumBits_Q5;
int[] abs_pulses;
int[] sum_pulses;
int[] nRshifts;
int[] pulses_comb = new int[8];
int abs_pulses_ptr;
int pulses_ptr;
byte[] nBits_ptr;
Arrays.MemSetInt(pulses_comb, 0, 8);
/****************************/
/* Prepare for shell coding */
/****************************/
/* Calculate number of shell blocks */
Inlines.OpusAssert(1 << SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH == SilkConstants.SHELL_CODEC_FRAME_LENGTH);
iter = Inlines.silk_RSHIFT(frame_length, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH);
if (iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH < frame_length)
{
Inlines.OpusAssert(frame_length == 12 * 10); /* Make sure only happens for 10 ms @ 12 kHz */
iter++;
Arrays.MemSetWithOffset<sbyte>(pulses, 0, frame_length, SilkConstants.SHELL_CODEC_FRAME_LENGTH);
}
/* Take the absolute value of the pulses */
abs_pulses = new int[iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH];
Inlines.OpusAssert((SilkConstants.SHELL_CODEC_FRAME_LENGTH & 3) == 0);
// unrolled loop
for (i = 0; i < iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH; i += 4)
{
abs_pulses[i + 0] = (int)Inlines.silk_abs(pulses[i + 0]);
abs_pulses[i + 1] = (int)Inlines.silk_abs(pulses[i + 1]);
abs_pulses[i + 2] = (int)Inlines.silk_abs(pulses[i + 2]);
abs_pulses[i + 3] = (int)Inlines.silk_abs(pulses[i + 3]);
}
/* Calc sum pulses per shell code frame */
sum_pulses = new int[iter];
nRshifts = new int[iter];
abs_pulses_ptr = 0;
for (i = 0; i < iter; i++)
{
nRshifts[i] = 0;
while (true)
{
/* 1+1 . 2 */
scale_down = combine_and_check(pulses_comb, 0, abs_pulses, abs_pulses_ptr, Tables.silk_max_pulses_table[0], 8);
/* 2+2 . 4 */
scale_down += combine_and_check(pulses_comb, pulses_comb, Tables.silk_max_pulses_table[1], 4);
/* 4+4 . 8 */
scale_down += combine_and_check(pulses_comb, pulses_comb, Tables.silk_max_pulses_table[2], 2);
/* 8+8 . 16 */
scale_down += combine_and_check(sum_pulses, i, pulses_comb, 0, Tables.silk_max_pulses_table[3], 1);
if (scale_down != 0)
{
/* We need to downscale the quantization signal */
nRshifts[i]++;
for (k = abs_pulses_ptr; k < abs_pulses_ptr + SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++)
{
abs_pulses[k] = Inlines.silk_RSHIFT(abs_pulses[k], 1);
}
}
else
{
/* Jump out of while(1) loop and go to next shell coding frame */
break;
}
}
abs_pulses_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH;
}
/**************/
/* Rate level */
/**************/
/* find rate level that leads to fewest bits for coding of pulses per block info */
minSumBits_Q5 = int.MaxValue;
for (k = 0; k < SilkConstants.N_RATE_LEVELS - 1; k++)
{
nBits_ptr = Tables.silk_pulses_per_block_BITS_Q5[k];
sumBits_Q5 = Tables.silk_rate_levels_BITS_Q5[signalType >> 1][k];
for (i = 0; i < iter; i++)
{
if (nRshifts[i] > 0)
{
sumBits_Q5 += nBits_ptr[SilkConstants.SILK_MAX_PULSES + 1];
}
else {
sumBits_Q5 += nBits_ptr[sum_pulses[i]];
}
}
if (sumBits_Q5 < minSumBits_Q5)
{
minSumBits_Q5 = sumBits_Q5;
RateLevelIndex = k;
}
}
psRangeEnc.enc_icdf( RateLevelIndex, Tables.silk_rate_levels_iCDF[signalType >> 1], 8);
/***************************************************/
/* Sum-Weighted-Pulses Encoding */
/***************************************************/
for (i = 0; i < iter; i++)
{
if (nRshifts[i] == 0)
{
psRangeEnc.enc_icdf( sum_pulses[i], Tables.silk_pulses_per_block_iCDF[RateLevelIndex], 8);
}
else
{
psRangeEnc.enc_icdf( SilkConstants.SILK_MAX_PULSES + 1, Tables.silk_pulses_per_block_iCDF[RateLevelIndex], 8);
for (k = 0; k < nRshifts[i] - 1; k++)
{
psRangeEnc.enc_icdf( SilkConstants.SILK_MAX_PULSES + 1, Tables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], 8);
}
psRangeEnc.enc_icdf( sum_pulses[i], Tables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], 8);
}
}
/******************/
/* Shell Encoding */
/******************/
for (i = 0; i < iter; i++)
{
if (sum_pulses[i] > 0)
{
ShellCoder.silk_shell_encoder(psRangeEnc, abs_pulses, i * SilkConstants.SHELL_CODEC_FRAME_LENGTH);
}
}
/****************/
/* LSB Encoding */
/****************/
for (i = 0; i < iter; i++)
{
if (nRshifts[i] > 0)
{
pulses_ptr = i * SilkConstants.SHELL_CODEC_FRAME_LENGTH;
nLS = nRshifts[i] - 1;
for (k = 0; k < SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++)
{
abs_q = (sbyte)Inlines.silk_abs(pulses[pulses_ptr + k]);
for (j = nLS; j > 0; j--)
{
bit = Inlines.silk_RSHIFT(abs_q, j) & 1;
psRangeEnc.enc_icdf( bit, Tables.silk_lsb_iCDF, 8);
}
bit = abs_q & 1;
psRangeEnc.enc_icdf( bit, Tables.silk_lsb_iCDF, 8);
}
}
}
/****************/
/* Encode signs */
/****************/
CodeSigns.silk_encode_signs(psRangeEnc, pulses, frame_length, signalType, quantOffsetType, sum_pulses);
}
}
}

View File

@@ -0,0 +1,41 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk.Enums
{
internal static class DecoderAPIFlag
{
public const int FLAG_DECODE_NORMAL = 0;
public const int FLAG_PACKET_LOST = 1;
public const int FLAG_DECODE_LBRR = 2;
}
}

View File

@@ -0,0 +1,91 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk.Enums
{
/// <summary>
/// Represents error messages from a silk encoder/decoder
/// </summary>
internal static class SilkError
{
internal static int SILK_NO_ERROR = 0;
// Encoder error messages
/* Input length is not a multiple of 10 ms, or length is longer than the packet length */
internal static int SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES = -101;
/* Sampling frequency not 8000, 12000 or 16000 Hertz */
internal static int SILK_ENC_FS_NOT_SUPPORTED = -102;
/* Packet size not 10, 20, 40, or 60 ms */
internal static int SILK_ENC_PACKET_SIZE_NOT_SUPPORTED = -103;
/* Allocated payload buffer too short */
internal static int SILK_ENC_PAYLOAD_BUF_TOO_SHORT = -104;
/* Loss rate not between 0 and 100 percent */
internal static int SILK_ENC_INVALID_LOSS_RATE = -105;
/* Complexity setting not valid, use 0...10 */
internal static int SILK_ENC_INVALID_COMPLEXITY_SETTING = -106;
/* Inband FEC setting not valid, use 0 or 1 */
internal static int SILK_ENC_INVALID_INBAND_FEC_SETTING = -107;
/* DTX setting not valid, use 0 or 1 */
internal static int SILK_ENC_INVALID_DTX_SETTING = -108;
/* CBR setting not valid, use 0 or 1 */
internal static int SILK_ENC_INVALID_CBR_SETTING = -109;
/* Internal encoder error */
internal static int SILK_ENC_INTERNAL_ERROR = -110;
/* Internal encoder error */
internal static int SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR = -111;
// Decoder error messages
/* Output sampling frequency lower than internal decoded sampling frequency */
internal static int SILK_DEC_INVALID_SAMPLING_FREQUENCY = -200;
/* Payload size exceeded the maximum allowed 1024 bytes */
internal static int SILK_DEC_PAYLOAD_TOO_LARGE = -201;
/* Payload has bit errors */
internal static int SILK_DEC_PAYLOAD_ERROR = -202;
/* Payload has bit errors */
internal static int SILK_DEC_INVALID_FRAME_SIZE = -203;
}
}

View File

@@ -0,0 +1,714 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Celt;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class Filters
{
internal static void silk_warped_LPC_analysis_filter(
int[] state, /* I/O State [order + 1] */
int[] res_Q2, /* O Residual signal [length] */
short[] coef_Q13, /* I Coefficients [order] */
int coef_Q13_ptr,
short[] input, /* I Input signal [length] */
int input_ptr,
short lambda_Q16, /* I Warping factor */
int length, /* I Length of input signal */
int order /* I Filter order (even) */
)
{
int n, i;
int acc_Q11, tmp1, tmp2;
/* Order must be even */
Inlines.OpusAssert((order & 1) == 0);
for (n = 0; n < length; n++)
{
/* Output of lowpass section */
tmp2 = Inlines.silk_SMLAWB(state[0], state[1], lambda_Q16);
state[0] = Inlines.silk_LSHIFT(input[input_ptr + n], 14);
/* Output of allpass section */
tmp1 = Inlines.silk_SMLAWB(state[1], state[2] - tmp2, lambda_Q16);
state[1] = tmp2;
acc_Q11 = Inlines.silk_RSHIFT(order, 1);
acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr]);
/* Loop over allpass sections */
for (i = 2; i < order; i += 2)
{
/* Output of allpass section */
tmp2 = Inlines.silk_SMLAWB(state[i], state[i + 1] - tmp1, lambda_Q16);
state[i] = tmp1;
acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + i - 1]);
/* Output of allpass section */
tmp1 = Inlines.silk_SMLAWB(state[i + 1], state[i + 2] - tmp2, lambda_Q16);
state[i + 1] = tmp2;
acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr + i]);
}
state[order] = tmp1;
acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + order - 1]);
res_Q2[n] = Inlines.silk_LSHIFT((int)input[input_ptr + n], 2) - Inlines.silk_RSHIFT_ROUND(acc_Q11, 9);
}
}
internal static void silk_prefilter(
SilkChannelEncoder psEnc, /* I/O Encoder state */
SilkEncoderControl psEncCtrl, /* I Encoder control */
int[] xw_Q3, /* O Weighted signal */
short[] x, /* I Speech signal */
int x_ptr
)
{
SilkPrefilterState P = psEnc.sPrefilt;
int j, k, lag;
int tmp_32;
int AR1_shp_Q13;
int px;
int pxw_Q3;
int HarmShapeGain_Q12, Tilt_Q14;
int HarmShapeFIRPacked_Q12, LF_shp_Q14;
int[] x_filt_Q12;
int[] st_res_Q2;
short[] B_Q10 = new short[2];
/* Set up pointers */
px = x_ptr;
pxw_Q3 = 0;
lag = P.lagPrev;
x_filt_Q12 = new int[psEnc.subfr_length];
st_res_Q2 = new int[psEnc.subfr_length];
for (k = 0; k < psEnc.nb_subfr; k++)
{
/* Update Variables that change per sub frame */
if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
lag = psEncCtrl.pitchL[k];
}
/* Noise shape parameters */
HarmShapeGain_Q12 = Inlines.silk_SMULWB((int)psEncCtrl.HarmShapeGain_Q14[k], 16384 - psEncCtrl.HarmBoost_Q14[k]);
Inlines.OpusAssert(HarmShapeGain_Q12 >= 0);
HarmShapeFIRPacked_Q12 = Inlines.silk_RSHIFT(HarmShapeGain_Q12, 2);
HarmShapeFIRPacked_Q12 |= Inlines.silk_LSHIFT((int)Inlines.silk_RSHIFT(HarmShapeGain_Q12, 1), 16);
Tilt_Q14 = psEncCtrl.Tilt_Q14[k];
LF_shp_Q14 = psEncCtrl.LF_shp_Q14[k];
AR1_shp_Q13 = k * SilkConstants.MAX_SHAPE_LPC_ORDER;
/* Short term FIR filtering*/
silk_warped_LPC_analysis_filter(P.sAR_shp, st_res_Q2, psEncCtrl.AR1_Q13, AR1_shp_Q13, x, px,
(short)(psEnc.warping_Q16), psEnc.subfr_length, psEnc.shapingLPCOrder);
/* Reduce (mainly) low frequencies during harmonic emphasis */
B_Q10[0] = (short)(Inlines.silk_RSHIFT_ROUND(psEncCtrl.GainsPre_Q14[k], 4));
tmp_32 = Inlines.silk_SMLABB(((int)((TuningParameters.INPUT_TILT) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.INPUT_TILT, 26)*/, psEncCtrl.HarmBoost_Q14[k], HarmShapeGain_Q12); /* Q26 */
tmp_32 = Inlines.silk_SMLABB(tmp_32, psEncCtrl.coding_quality_Q14, ((int)((TuningParameters.HIGH_RATE_INPUT_TILT) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_INPUT_TILT, 12)*/); /* Q26 */
tmp_32 = Inlines.silk_SMULWB(tmp_32, -psEncCtrl.GainsPre_Q14[k]); /* Q24 */
tmp_32 = Inlines.silk_RSHIFT_ROUND(tmp_32, 14); /* Q10 */
B_Q10[1] = (short)(Inlines.silk_SAT16(tmp_32));
x_filt_Q12[0] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[0], B_Q10[0]), P.sHarmHP_Q2, B_Q10[1]);
for (j = 1; j < psEnc.subfr_length; j++)
{
x_filt_Q12[j] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[j], B_Q10[0]), st_res_Q2[j - 1], B_Q10[1]);
}
P.sHarmHP_Q2 = st_res_Q2[psEnc.subfr_length - 1];
silk_prefilt(P, x_filt_Q12, xw_Q3, pxw_Q3, HarmShapeFIRPacked_Q12, Tilt_Q14, LF_shp_Q14, lag, psEnc.subfr_length);
px += psEnc.subfr_length;
pxw_Q3 += psEnc.subfr_length;
}
P.lagPrev = psEncCtrl.pitchL[psEnc.nb_subfr - 1];
}
/* Prefilter for finding Quantizer input signal */
static void silk_prefilt(
SilkPrefilterState P, /* I/O state */
int[] st_res_Q12, /* I short term residual signal */
int[] xw_Q3, /* O prefiltered signal */
int xw_Q3_ptr,
int HarmShapeFIRPacked_Q12, /* I Harmonic shaping coeficients */
int Tilt_Q14, /* I Tilt shaping coeficient */
int LF_shp_Q14, /* I Low-frequancy shaping coeficients */
int lag, /* I Lag for harmonic shaping */
int length /* I Length of signals */
)
{
int i, idx, LTP_shp_buf_idx;
int n_LTP_Q12, n_Tilt_Q10, n_LF_Q10;
int sLF_MA_shp_Q12, sLF_AR_shp_Q12;
short[] LTP_shp_buf;
/* To speed up use temp variables instead of using the struct */
LTP_shp_buf = P.sLTP_shp;
LTP_shp_buf_idx = P.sLTP_shp_buf_idx;
sLF_AR_shp_Q12 = P.sLF_AR_shp_Q12;
sLF_MA_shp_Q12 = P.sLF_MA_shp_Q12;
for (i = 0; i < length; i++)
{
if (lag > 0)
{
/* unrolled loop */
Inlines.OpusAssert(SilkConstants.HARM_SHAPE_FIR_TAPS == 3);
idx = lag + LTP_shp_buf_idx;
n_LTP_Q12 = Inlines.silk_SMULBB(LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2 - 1) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12);
n_LTP_Q12 = Inlines.silk_SMLABT(n_LTP_Q12, LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12);
n_LTP_Q12 = Inlines.silk_SMLABB(n_LTP_Q12, LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2 + 1) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12);
}
else {
n_LTP_Q12 = 0;
}
n_Tilt_Q10 = Inlines.silk_SMULWB(sLF_AR_shp_Q12, Tilt_Q14);
n_LF_Q10 = Inlines.silk_SMLAWB(Inlines.silk_SMULWT(sLF_AR_shp_Q12, LF_shp_Q14), sLF_MA_shp_Q12, LF_shp_Q14);
sLF_AR_shp_Q12 = Inlines.silk_SUB32(st_res_Q12[i], Inlines.silk_LSHIFT(n_Tilt_Q10, 2));
sLF_MA_shp_Q12 = Inlines.silk_SUB32(sLF_AR_shp_Q12, Inlines.silk_LSHIFT(n_LF_Q10, 2));
LTP_shp_buf_idx = (LTP_shp_buf_idx - 1) & SilkConstants.LTP_MASK;
LTP_shp_buf[LTP_shp_buf_idx] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sLF_MA_shp_Q12, 12));
xw_Q3[xw_Q3_ptr + i] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(sLF_MA_shp_Q12, n_LTP_Q12), 9);
}
/* Copy temp variable back to state */
P.sLF_AR_shp_Q12 = sLF_AR_shp_Q12;
P.sLF_MA_shp_Q12 = sLF_MA_shp_Q12;
P.sLTP_shp_buf_idx = LTP_shp_buf_idx;
}
#if !UNSAFE
/// <summary>
/// Second order ARMA filter, alternative implementation
/// </summary>
/// <param name="input">I input signal</param>
/// <param name="B_Q28">I MA coefficients [3]</param>
/// <param name="A_Q28">I AR coefficients [2]</param>
/// <param name="S">I/O State vector [2]</param>
/// <param name="output">O output signal</param>
/// <param name="len">I signal length (must be even)</param>
/// <param name="stride">I Operate on interleaved signal if > 1</param>
internal static void silk_biquad_alt(
short[] input,
int input_ptr,
int[] B_Q28,
int[] A_Q28,
int[] S,
short[] output,
int output_ptr,
int len,
int stride)
{
/* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */
int k;
int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14;
/* Negate A_Q28 values and split in two parts */
A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; /* lower part */
A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); /* upper part */
A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; /* lower part */
A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); /* upper part */
for (k = 0; k < len; k++)
{
/* S[ 0 ], S[ 1 ]: Q12 */
inval = input[input_ptr + k * stride];
out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[0], B_Q28[0], inval), 2);
S[0] = S[1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14);
S[0] = Inlines.silk_SMLAWB(S[0], out32_Q14, A0_U_Q28);
S[0] = Inlines.silk_SMLAWB(S[0], B_Q28[1], inval);
S[1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14);
S[1] = Inlines.silk_SMLAWB(S[1], out32_Q14, A1_U_Q28);
S[1] = Inlines.silk_SMLAWB(S[1], B_Q28[2], inval);
/* Scale back to Q0 and saturate */
output[output_ptr + k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14));
}
}
internal static void silk_biquad_alt(
short[] input,
int input_ptr,
int[] B_Q28,
int[] A_Q28,
int[] S,
int S_ptr,
short[] output,
int output_ptr,
int len,
int stride)
{
/* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */
int k;
int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14;
/* Negate A_Q28 values and split in two parts */
A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; /* lower part */
A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); /* upper part */
A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; /* lower part */
A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); /* upper part */
for (k = 0; k < len; k++)
{
int s1 = S_ptr + 1;
/* S[ 0 ], S[ 1 ]: Q12 */
inval = input[input_ptr + k * stride];
out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[S_ptr], B_Q28[0], inval), 2);
S[S_ptr] = S[s1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14);
S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], out32_Q14, A0_U_Q28);
S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], B_Q28[1], inval);
S[s1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14);
S[s1] = Inlines.silk_SMLAWB(S[s1], out32_Q14, A1_U_Q28);
S[s1] = Inlines.silk_SMLAWB(S[s1], B_Q28[2], inval);
/* Scale back to Q0 and saturate */
output[output_ptr + k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14));
}
}
#else
/// <summary>
/// Second order ARMA filter, alternative implementation
/// </summary>
/// <param name="input">I input signal</param>
/// <param name="B_Q28">I MA coefficients [3]</param>
/// <param name="A_Q28">I AR coefficients [2]</param>
/// <param name="S">I/O State vector [2]</param>
/// <param name="output">O output signal</param>
/// <param name="len">I signal length (must be even)</param>
/// <param name="stride">I Operate on interleaved signal if > 1</param>
internal static unsafe void silk_biquad_alt(
short[] input,
int input_ptr,
int[] B_Q28,
int[] A_Q28,
int[] S,
int S_ptr,
short[] output,
int output_ptr,
int len,
int stride)
{
/* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */
int k;
int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14;
/* Negate A_Q28 values and split in two parts */
A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; /* lower part */
A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); /* upper part */
A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; /* lower part */
A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); /* upper part */
fixed (short* pinput_base = input, poutput_base = output)
{
fixed (int* pS_base = S, pBQ28 = B_Q28)
{
int* pS = pS_base + S_ptr;
short* pinput = pinput_base + input_ptr;
short* poutput = poutput_base + output_ptr;
for (k = 0; k < len; k++)
{
/* S[ 0 ], S[ 1 ]: Q12 */
inval = pinput[k * stride];
out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(pS[0], pBQ28[0], inval), 2);
pS[0] = pS[1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14);
pS[0] = Inlines.silk_SMLAWB(pS[0], out32_Q14, A0_U_Q28);
pS[0] = Inlines.silk_SMLAWB(pS[0], pBQ28[1], inval);
pS[1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14);
pS[1] = Inlines.silk_SMLAWB(pS[1], out32_Q14, A1_U_Q28);
pS[1] = Inlines.silk_SMLAWB(pS[1], pBQ28[2], inval);
/* Scale back to Q0 and saturate */
poutput[k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14));
}
}
}
}
internal static unsafe void silk_biquad_alt(
short[] input,
int input_ptr,
int[] B_Q28,
int[] A_Q28,
int[] S,
short[] output,
int output_ptr,
int len,
int stride)
{
silk_biquad_alt(input, input_ptr, B_Q28, A_Q28, S, 0, output, output_ptr, len, stride);
}
#endif
/* Coefficients for 2-band filter bank based on first-order allpass filters */
private readonly static short A_fb1_20 = 5394 << 1;
private readonly static short A_fb1_21 = -24290; /* (opus_int16)(20623 << 1) */
/// <summary>
/// Split signal into two decimated bands using first-order allpass filters
/// </summary>
/// <param name="input">I Input signal [N]</param>
/// <param name="S">I/O State vector [2]</param>
/// <param name="outL">O Low band [N/2]</param>
/// <param name="outH">O High band [N/2]</param>
/// <param name="N">I Number of input samples</param>
internal static void silk_ana_filt_bank_1(
short[] input,
int input_ptr,
int[] S,
short[] outL,
short[] outH,
int outH_ptr,
int N)
{
int k, N2 = Inlines.silk_RSHIFT(N, 1);
int in32, X, Y, out_1, out_2;
/* Internal variables and state are in Q10 format */
for (k = 0; k < N2; k++)
{
/* Convert to Q10 */
in32 = Inlines.silk_LSHIFT((int)input[input_ptr + 2 * k], 10);
/* All-pass section for even input sample */
Y = Inlines.silk_SUB32(in32, S[0]);
X = Inlines.silk_SMLAWB(Y, Y, A_fb1_21);
out_1 = Inlines.silk_ADD32(S[0], X);
S[0] = Inlines.silk_ADD32(in32, X);
/* Convert to Q10 */
in32 = Inlines.silk_LSHIFT((int)input[input_ptr + 2 * k + 1], 10);
/* All-pass section for odd input sample, and add to output of previous section */
Y = Inlines.silk_SUB32(in32, S[1]);
X = Inlines.silk_SMULWB(Y, A_fb1_20);
out_2 = Inlines.silk_ADD32(S[1], X);
S[1] = Inlines.silk_ADD32(in32, X);
/* Add/subtract, convert back to int16 and store to output */
outL[k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD32(out_2, out_1), 11));
outH[outH_ptr + k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(out_2, out_1), 11));
}
}
/// <summary>
/// Chirp (bandwidth expand) LP AR filter
/// </summary>
/// <param name="ar">I/O AR filter to be expanded (without leading 1)</param>
/// <param name="d">I Length of ar</param>
/// <param name="chirp_Q16">I Chirp factor in Q16</param>
internal static void silk_bwexpander_32(int[] ar, int d, int chirp_Q16)
{
int i;
int chirp_minus_one_Q16 = chirp_Q16 - 65536;
for (i = 0; i < d - 1; i++)
{
ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]);
chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16);
}
ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]);
}
/// <summary>
/// Elliptic/Cauer filters designed with 0.1 dB passband ripple,
/// 80 dB minimum stopband attenuation, and
/// [0.95 : 0.15 : 0.35] normalized cut off frequencies.
/// Helper function, interpolates the filter taps
/// </summary>
/// <param name="B_Q28">order [TRANSITION_NB]</param>
/// <param name="A_Q28">order [TRANSITION_NA]</param>
/// <param name="ind"></param>
/// <param name="fac_Q16"></param>
internal static void silk_LP_interpolate_filter_taps(
int[] B_Q28,
int[] A_Q28,
int ind,
int fac_Q16)
{
int nb, na;
if (ind < SilkConstants.TRANSITION_INT_NUM - 1)
{
if (fac_Q16 > 0)
{
if (fac_Q16 < 32768)
{
/* fac_Q16 is in range of a 16-bit int */
/* Piece-wise linear interpolation of B and A */
for (nb = 0; nb < SilkConstants.TRANSITION_NB; nb++)
{
B_Q28[nb] = Inlines.silk_SMLAWB(
Tables.silk_Transition_LP_B_Q28[ind][nb],
Tables.silk_Transition_LP_B_Q28[ind + 1][nb] -
Tables.silk_Transition_LP_B_Q28[ind][nb],
fac_Q16);
}
for (na = 0; na < SilkConstants.TRANSITION_NA; na++)
{
A_Q28[na] = Inlines.silk_SMLAWB(
Tables.silk_Transition_LP_A_Q28[ind][na],
Tables.silk_Transition_LP_A_Q28[ind + 1][na] -
Tables.silk_Transition_LP_A_Q28[ind][na],
fac_Q16);
}
}
else
{
/* ( fac_Q16 - ( 1 << 16 ) ) is in range of a 16-bit int */
Inlines.OpusAssert(fac_Q16 - (1 << 16) == Inlines.silk_SAT16(fac_Q16 - (1 << 16)));
/* Piece-wise linear interpolation of B and A */
for (nb = 0; nb < SilkConstants.TRANSITION_NB; nb++)
{
B_Q28[nb] = Inlines.silk_SMLAWB(
Tables.silk_Transition_LP_B_Q28[ind + 1][nb],
Tables.silk_Transition_LP_B_Q28[ind + 1][nb] -
Tables.silk_Transition_LP_B_Q28[ind][nb],
fac_Q16 - ((int)1 << 16));
}
for (na = 0; na < SilkConstants.TRANSITION_NA; na++)
{
A_Q28[na] = Inlines.silk_SMLAWB(
Tables.silk_Transition_LP_A_Q28[ind + 1][na],
Tables.silk_Transition_LP_A_Q28[ind + 1][na] -
Tables.silk_Transition_LP_A_Q28[ind][na],
fac_Q16 - ((int)1 << 16));
}
}
}
else
{
Array.Copy(Tables.silk_Transition_LP_B_Q28[ind], 0, B_Q28, 0, SilkConstants.TRANSITION_NB);
Array.Copy(Tables.silk_Transition_LP_A_Q28[ind], 0, A_Q28, 0, SilkConstants.TRANSITION_NA);
}
}
else
{
Array.Copy(Tables.silk_Transition_LP_B_Q28[SilkConstants.TRANSITION_INT_NUM - 1], 0, B_Q28, 0, SilkConstants.TRANSITION_NB);
Array.Copy(Tables.silk_Transition_LP_A_Q28[SilkConstants.TRANSITION_INT_NUM - 1], 0, A_Q28, 0, SilkConstants.TRANSITION_NA);
}
}
/// <summary>
/// LPC analysis filter
/// NB! State is kept internally and the
/// filter always starts with zero state
/// first d output samples are set to zero
/// </summary>
/// <param name="output">O Output signal</param>
/// <param name="input">I Input signal</param>
/// <param name="B">I MA prediction coefficients, Q12 [order]</param>
/// <param name="len">I Signal length</param>
/// <param name="d">I Filter order</param>
internal static void silk_LPC_analysis_filter(
short[] output,
int output_ptr,
short[] input,
int input_ptr,
short[] B,
int B_ptr,
int len,
int d)
{
int j;
short[] mem = new short[SilkConstants.SILK_MAX_ORDER_LPC];
short[] num = new short[SilkConstants.SILK_MAX_ORDER_LPC];
Inlines.OpusAssert(d >= 6);
Inlines.OpusAssert((d & 1) == 0);
Inlines.OpusAssert(d <= len);
Inlines.OpusAssert(d <= SilkConstants.SILK_MAX_ORDER_LPC);
for (j = 0; j < d; j++)
{
num[j] = (short)(0 - B[B_ptr + j]);
}
for (j = 0; j < d; j++)
{
mem[j] = input[input_ptr + d - j - 1];
}
#if UNSAFE
unsafe
{
fixed (short* pinput_base = input, poutput_base = output)
{
short* pinput = pinput_base + input_ptr + d;
short* poutput = poutput_base + output_ptr + d;
Kernels.celt_fir(pinput, num, poutput, len - d, d, mem);
}
}
#else
Kernels.celt_fir(input, input_ptr + d, num, output, output_ptr + d, len - d, d, mem);
#endif
for (j = output_ptr; j < output_ptr + d; j++)
{
output[j] = 0;
}
}
private const int QA = 24;
private static readonly int A_LIMIT = ((int)((0.99975f) * ((long)1 << (QA)) + 0.5))/*Inlines.SILK_CONST(0.99975f, QA)*/;
/// <summary>
/// Compute inverse of LPC prediction gain, and
/// test if LPC coefficients are stable (all poles within unit circle)
/// </summary>
/// <param name="A_QA">Prediction coefficients, order [2][SILK_MAX_ORDER_LPC]</param>
/// <param name="order">Prediction order</param>
/// <returns>inverse prediction gain in energy domain, Q30</returns>
internal static int LPC_inverse_pred_gain_QA(
int[][] A_QA,
int order)
{
int k, n, mult2Q;
int invGain_Q30, rc_Q31, rc_mult1_Q30, rc_mult2, tmp_QA;
int[] Aold_QA, Anew_QA;
Anew_QA = A_QA[order & 1];
invGain_Q30 = (int)1 << 30;
for (k = order - 1; k > 0; k--)
{
/* Check for stability */
if ((Anew_QA[k] > A_LIMIT) || (Anew_QA[k] < -A_LIMIT))
{
return 0;
}
/* Set RC equal to negated AR coef */
rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[k], 31 - QA);
/* rc_mult1_Q30 range: [ 1 : 2^30 ] */
rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
Inlines.OpusAssert(rc_mult1_Q30 > (1 << 15)); /* reduce A_LIMIT if fails */
Inlines.OpusAssert(rc_mult1_Q30 <= (1 << 30));
/* rc_mult2 range: [ 2^30 : silk_int32_MAX ] */
mult2Q = 32 - Inlines.silk_CLZ32(Inlines.silk_abs(rc_mult1_Q30));
rc_mult2 = Inlines.silk_INVERSE32_varQ(rc_mult1_Q30, mult2Q + 30);
/* Update inverse gain */
/* invGain_Q30 range: [ 0 : 2^30 ] */
invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2);
Inlines.OpusAssert(invGain_Q30 >= 0);
Inlines.OpusAssert(invGain_Q30 <= (1 << 30));
/* Swap pointers */
Aold_QA = Anew_QA;
Anew_QA = A_QA[k & 1];
/* Update AR coefficient */
for (n = 0; n < k; n++)
{
tmp_QA = Aold_QA[n] - Inlines.MUL32_FRAC_Q(Aold_QA[k - n - 1], rc_Q31, 31);
Anew_QA[n] = Inlines.MUL32_FRAC_Q(tmp_QA, rc_mult2, mult2Q);
}
}
/* Check for stability */
if ((Anew_QA[0] > A_LIMIT) || (Anew_QA[0] < -A_LIMIT))
{
return 0;
}
/* Set RC equal to negated AR coef */
rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[0], 31 - QA);
/* Range: [ 1 : 2^30 ] */
rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
/* Update inverse gain */
/* Range: [ 0 : 2^30 ] */
invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2);
Inlines.OpusAssert(invGain_Q30 >= 0);
Inlines.OpusAssert(invGain_Q30 <= 1 << 30);
return invGain_Q30;
}
/// <summary>
/// For input in Q12 domain
/// </summary>
/// <param name="A_Q12">Prediction coefficients, Q12 [order]</param>
/// <param name="order">I Prediction order</param>
/// <returns>inverse prediction gain in energy domain, Q30</returns>
internal static int silk_LPC_inverse_pred_gain(short[] A_Q12, int order)
{
int k;
int[][] Atmp_QA = new int[2][];
Atmp_QA[0] = new int[order];
Atmp_QA[1] = new int[order];
int[] Anew_QA;
int DC_resp = 0;
Anew_QA = Atmp_QA[order & 1];
/* Increase Q domain of the AR coefficients */
for (k = 0; k < order; k++)
{
DC_resp += (int)A_Q12[k];
Anew_QA[k] = Inlines.silk_LSHIFT32((int)A_Q12[k], QA - 12);
}
/* If the DC is unstable, we don't even need to do the full calculations */
if (DC_resp >= 4096)
{
return 0;
}
return LPC_inverse_pred_gain_QA(Atmp_QA, order);
}
}
}

View File

@@ -0,0 +1,184 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class FindLPC
{
/* Finds LPC vector from correlations, and converts to NLSF */
internal static void silk_find_LPC(
SilkChannelEncoder psEncC, /* I/O Encoder state */
short[] NLSF_Q15, /* O NLSFs */
short[] x, /* I Input signal */
int minInvGain_Q30 /* I Inverse of max prediction gain */
)
{
int k, subfr_length;
int[] a_Q16 = new int[SilkConstants.MAX_LPC_ORDER];
int isInterpLower, shift;
int res_nrg0, res_nrg1;
int rshift0, rshift1;
BoxedValueInt scratch_box1 = new BoxedValueInt();
BoxedValueInt scratch_box2 = new BoxedValueInt();
/* Used only for LSF interpolation */
int[] a_tmp_Q16 = new int[SilkConstants.MAX_LPC_ORDER];
int res_nrg_interp, res_nrg, res_tmp_nrg;
int res_nrg_interp_Q, res_nrg_Q, res_tmp_nrg_Q;
short[] a_tmp_Q12 = new short[SilkConstants.MAX_LPC_ORDER];
short[] NLSF0_Q15 = new short[SilkConstants.MAX_LPC_ORDER];
subfr_length = psEncC.subfr_length + psEncC.predictLPCOrder;
/* Default: no interpolation */
psEncC.indices.NLSFInterpCoef_Q2 = 4;
/* Burg AR analysis for the full frame */
BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_Q16, x, 0, minInvGain_Q30, subfr_length, psEncC.nb_subfr, psEncC.predictLPCOrder);
res_nrg = scratch_box1.Val;
res_nrg_Q = scratch_box2.Val;
if (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR)
{
short[] LPC_res;
/* Optimal solution for last 10 ms */
BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_tmp_Q16, x, (2 * subfr_length), minInvGain_Q30, subfr_length, 2, psEncC.predictLPCOrder);
res_tmp_nrg = scratch_box1.Val;
res_tmp_nrg_Q = scratch_box2.Val;
/* subtract residual energy here, as that's easier than adding it to the */
/* residual energy of the first 10 ms in each iteration of the search below */
shift = res_tmp_nrg_Q - res_nrg_Q;
if (shift >= 0)
{
if (shift < 32)
{
res_nrg = res_nrg - Inlines.silk_RSHIFT(res_tmp_nrg, shift);
}
}
else {
Inlines.OpusAssert(shift > -32);
res_nrg = Inlines.silk_RSHIFT(res_nrg, -shift) - res_tmp_nrg;
res_nrg_Q = res_tmp_nrg_Q;
}
/* Convert to NLSFs */
NLSF.silk_A2NLSF(NLSF_Q15, a_tmp_Q16, psEncC.predictLPCOrder);
LPC_res = new short[2 * subfr_length];
/* Search over interpolation indices to find the one with lowest residual energy */
for (k = 3; k >= 0; k--)
{
/* Interpolate NLSFs for first half */
Inlines.silk_interpolate(NLSF0_Q15, psEncC.prev_NLSFq_Q15, NLSF_Q15, k, psEncC.predictLPCOrder);
/* Convert to LPC for residual energy evaluation */
NLSF.silk_NLSF2A(a_tmp_Q12, NLSF0_Q15, psEncC.predictLPCOrder);
/* Calculate residual energy with NLSF interpolation */
Filters.silk_LPC_analysis_filter(LPC_res, 0, x, 0, a_tmp_Q12, 0, 2 * subfr_length, psEncC.predictLPCOrder);
SumSqrShift.silk_sum_sqr_shift(out res_nrg0, out rshift0, LPC_res, psEncC.predictLPCOrder, subfr_length - psEncC.predictLPCOrder);
SumSqrShift.silk_sum_sqr_shift(out res_nrg1, out rshift1, LPC_res, psEncC.predictLPCOrder + subfr_length, subfr_length - psEncC.predictLPCOrder);
/* Add subframe energies from first half frame */
shift = rshift0 - rshift1;
if (shift >= 0)
{
res_nrg1 = Inlines.silk_RSHIFT(res_nrg1, shift);
res_nrg_interp_Q = -rshift0;
}
else {
res_nrg0 = Inlines.silk_RSHIFT(res_nrg0, -shift);
res_nrg_interp_Q = -rshift1;
}
res_nrg_interp = Inlines.silk_ADD32(res_nrg0, res_nrg1);
/* Compare with first half energy without NLSF interpolation, or best interpolated value so far */
shift = res_nrg_interp_Q - res_nrg_Q;
if (shift >= 0)
{
if (Inlines.silk_RSHIFT(res_nrg_interp, shift) < res_nrg)
{
isInterpLower = (true ? 1 : 0);
}
else {
isInterpLower = (false ? 1 : 0);
}
}
else {
if (-shift < 32)
{
if (res_nrg_interp < Inlines.silk_RSHIFT(res_nrg, -shift))
{
isInterpLower = (true ? 1 : 0);
}
else {
isInterpLower = (false ? 1 : 0);
}
}
else {
isInterpLower = (false ? 1 : 0);
}
}
/* Determine whether current interpolated NLSFs are best so far */
if (isInterpLower == (true ? 1 : 0))
{
/* Interpolation has lower residual energy */
res_nrg = res_nrg_interp;
res_nrg_Q = res_nrg_interp_Q;
psEncC.indices.NLSFInterpCoef_Q2 = (sbyte)k;
}
}
}
if (psEncC.indices.NLSFInterpCoef_Q2 == 4)
{
/* NLSF interpolation is currently inactive, calculate NLSFs from full frame AR coefficients */
NLSF.silk_A2NLSF(NLSF_Q15, a_Q16, psEncC.predictLPCOrder);
}
Inlines.OpusAssert(psEncC.indices.NLSFInterpCoef_Q2 == 4 || (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR));
}
}
}

View File

@@ -0,0 +1,295 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class FindLTP
{
/* Head room for correlations */
private const int LTP_CORRS_HEAD_ROOM = 2;
/// <summary>
/// Finds linear prediction coeffecients and weights
/// </summary>
/// <param name="b_Q14"></param>
/// <param name="WLTP"></param>
/// <param name="LTPredCodGain_Q7"></param>
/// <param name="r_lpc"></param>
/// <param name="lag"></param>
/// <param name="Wght_Q15"></param>
/// <param name="subfr_length"></param>
/// <param name="nb_subfr"></param>
/// <param name="mem_offset"></param>
/// <param name="corr_rshifts"></param>
internal static void silk_find_LTP(
short[] b_Q14, /* O LTP coefs [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER] */
int[] WLTP, /* O Weight for LTP quantization [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER] */
BoxedValueInt LTPredCodGain_Q7, /* O LTP coding gain */
short[] r_lpc, /* I residual signal after LPC signal + state for first 10 ms */
int[] lag, /* I LTP lags [SilkConstants.MAX_NB_SUBFR] */
int[] Wght_Q15, /* I weights [SilkConstants.MAX_NB_SUBFR] */
int subfr_length, /* I subframe length */
int nb_subfr, /* I number of subframes */
int mem_offset, /* I number of samples in LTP memory */
int[] corr_rshifts /* O right shifts applied to correlations [SilkConstants.MAX_NB_SUBFR] */
)
{
int i, k, lshift;
int r_ptr;
int lag_ptr;
int b_Q14_ptr;
int regu;
int WLTP_ptr;
int[] b_Q16 = new int[SilkConstants.LTP_ORDER];
int[] delta_b_Q14 = new int[SilkConstants.LTP_ORDER];
int[] d_Q14 = new int[SilkConstants.MAX_NB_SUBFR];
int[] nrg = new int[SilkConstants.MAX_NB_SUBFR];
int g_Q26;
int[] w = new int[SilkConstants.MAX_NB_SUBFR];
int WLTP_max, max_abs_d_Q14, max_w_bits;
int temp32, denom32;
int extra_shifts;
int rr_shifts, maxRshifts, maxRshifts_wxtra, LZs;
int LPC_res_nrg, LPC_LTP_res_nrg, div_Q16;
int[] Rr = new int[SilkConstants.LTP_ORDER];
int[] rr = new int[SilkConstants.MAX_NB_SUBFR];
int wd, m_Q12;
b_Q14_ptr = 0;
WLTP_ptr = 0;
r_ptr = mem_offset;
for (k = 0; k < nb_subfr; k++)
{
lag_ptr = r_ptr - (lag[k] + SilkConstants.LTP_ORDER / 2);
SumSqrShift.silk_sum_sqr_shift(out rr[k], out rr_shifts, r_lpc, r_ptr, subfr_length); /* rr[ k ] in Q( -rr_shifts ) */
/* Assure headroom */
LZs = Inlines.silk_CLZ32(rr[k]);
if (LZs < LTP_CORRS_HEAD_ROOM)
{
rr[k] = Inlines.silk_RSHIFT_ROUND(rr[k], LTP_CORRS_HEAD_ROOM - LZs);
rr_shifts += (LTP_CORRS_HEAD_ROOM - LZs);
}
corr_rshifts[k] = rr_shifts;
BoxedValueInt boxed_shifts = new BoxedValueInt(corr_rshifts[k]);
CorrelateMatrix.silk_corrMatrix(r_lpc, lag_ptr, subfr_length, SilkConstants.LTP_ORDER, LTP_CORRS_HEAD_ROOM, WLTP, WLTP_ptr, boxed_shifts); /* WLTP_ptr in Q( -corr_rshifts[ k ] ) */
corr_rshifts[k] = boxed_shifts.Val;
/* The correlation vector always has lower max abs value than rr and/or RR so head room is assured */
CorrelateMatrix.silk_corrVector(r_lpc, lag_ptr, r_lpc, r_ptr, subfr_length, SilkConstants.LTP_ORDER, Rr, corr_rshifts[k]); /* Rr_ptr in Q( -corr_rshifts[ k ] ) */
if (corr_rshifts[k] > rr_shifts)
{
rr[k] = Inlines.silk_RSHIFT(rr[k], corr_rshifts[k] - rr_shifts); /* rr[ k ] in Q( -corr_rshifts[ k ] ) */
}
Inlines.OpusAssert(rr[k] >= 0);
regu = 1;
regu = Inlines.silk_SMLAWB(regu, rr[k], ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/);
regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, 0, 0, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/);
regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/);
RegularizeCorrelations.silk_regularize_correlations(WLTP, WLTP_ptr, rr, k, regu, SilkConstants.LTP_ORDER);
LinearAlgebra.silk_solve_LDL(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER, Rr, b_Q16); /* WLTP_ptr and Rr_ptr both in Q(-corr_rshifts[k]) */
/* Limit and store in Q14 */
silk_fit_LTP(b_Q16, b_Q14, b_Q14_ptr);
/* Calculate residual energy */
nrg[k] = ResidualEnergy.silk_residual_energy16_covar(b_Q14, b_Q14_ptr, WLTP, WLTP_ptr, Rr, rr[k], SilkConstants.LTP_ORDER, 14); /* nrg in Q( -corr_rshifts[ k ] ) */
/* temp = Wght[ k ] / ( nrg[ k ] * Wght[ k ] + 0.01f * subfr_length ); */
extra_shifts = Inlines.silk_min_int(corr_rshifts[k], LTP_CORRS_HEAD_ROOM);
denom32 = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1 + extra_shifts) + /* Q( -corr_rshifts[ k ] + extra_shifts ) */
Inlines.silk_RSHIFT(Inlines.silk_SMULWB((int)subfr_length, 655), corr_rshifts[k] - extra_shifts); /* Q( -corr_rshifts[ k ] + extra_shifts ) */
denom32 = Inlines.silk_max(denom32, 1);
Inlines.OpusAssert(((long)Wght_Q15[k] << 16) < int.MaxValue); /* Wght always < 0.5 in Q0 */
temp32 = Inlines.silk_DIV32(Inlines.silk_LSHIFT((int)Wght_Q15[k], 16), denom32); /* Q( 15 + 16 + corr_rshifts[k] - extra_shifts ) */
temp32 = Inlines.silk_RSHIFT(temp32, 31 + corr_rshifts[k] - extra_shifts - 26); /* Q26 */
/* Limit temp such that the below scaling never wraps around */
WLTP_max = 0;
for (i = WLTP_ptr; i < WLTP_ptr + (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); i++)
{
WLTP_max = Inlines.silk_max(WLTP[i], WLTP_max);
}
lshift = Inlines.silk_CLZ32(WLTP_max) - 1 - 3; /* keep 3 bits free for vq_nearest_neighbor */
Inlines.OpusAssert(26 - 18 + lshift >= 0);
if (26 - 18 + lshift < 31)
{
temp32 = Inlines.silk_min_32(temp32, Inlines.silk_LSHIFT((int)1, 26 - 18 + lshift));
}
Inlines.silk_scale_vector32_Q26_lshift_18(WLTP, WLTP_ptr, temp32, SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); /* WLTP_ptr in Q( 18 - corr_rshifts[ k ] ) */
w[k] = Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER); /* w in Q( 18 - corr_rshifts[ k ] ) */
Inlines.OpusAssert(w[k] >= 0);
r_ptr += subfr_length;
b_Q14_ptr += SilkConstants.LTP_ORDER;
WLTP_ptr += (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER);
}
maxRshifts = 0;
for (k = 0; k < nb_subfr; k++)
{
maxRshifts = Inlines.silk_max_int(corr_rshifts[k], maxRshifts);
}
/* Compute LTP coding gain */
if (LTPredCodGain_Q7 != null)
{
LPC_LTP_res_nrg = 0;
LPC_res_nrg = 0;
Inlines.OpusAssert(LTP_CORRS_HEAD_ROOM >= 2); /* Check that no overflow will happen when adding */
for (k = 0; k < nb_subfr; k++)
{
LPC_res_nrg = Inlines.silk_ADD32(LPC_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(rr[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); /* Q( -maxRshifts ) */
LPC_LTP_res_nrg = Inlines.silk_ADD32(LPC_LTP_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); /* Q( -maxRshifts ) */
}
LPC_LTP_res_nrg = Inlines.silk_max(LPC_LTP_res_nrg, 1); /* avoid division by zero */
div_Q16 = Inlines.silk_DIV32_varQ(LPC_res_nrg, LPC_LTP_res_nrg, 16);
LTPredCodGain_Q7.Val = (int)Inlines.silk_SMULBB(3, Inlines.silk_lin2log(div_Q16) - (16 << 7));
Inlines.OpusAssert(LTPredCodGain_Q7.Val == (int)Inlines.silk_SAT16(Inlines.silk_MUL(3, Inlines.silk_lin2log(div_Q16) - (16 << 7))));
}
/* smoothing */
/* d = sum( B, 1 ); */
b_Q14_ptr = 0;
for (k = 0; k < nb_subfr; k++)
{
d_Q14[k] = 0;
for (i = b_Q14_ptr; i < b_Q14_ptr + SilkConstants.LTP_ORDER; i++)
{
d_Q14[k] += b_Q14[i];
}
b_Q14_ptr += SilkConstants.LTP_ORDER;
}
/* m = ( w * d' ) / ( sum( w ) + 1e-3 ); */
/* Find maximum absolute value of d_Q14 and the bits used by w in Q0 */
max_abs_d_Q14 = 0;
max_w_bits = 0;
for (k = 0; k < nb_subfr; k++)
{
max_abs_d_Q14 = Inlines.silk_max_32(max_abs_d_Q14, Inlines.silk_abs(d_Q14[k]));
/* w[ k ] is in Q( 18 - corr_rshifts[ k ] ) */
/* Find bits needed in Q( 18 - maxRshifts ) */
max_w_bits = Inlines.silk_max_32(max_w_bits, 32 - Inlines.silk_CLZ32(w[k]) + corr_rshifts[k] - maxRshifts);
}
/* max_abs_d_Q14 = (5 << 15); worst case, i.e. SilkConstants.LTP_ORDER * -silk_int16_MIN */
Inlines.OpusAssert(max_abs_d_Q14 <= (5 << 15));
/* How many bits is needed for w*d' in Q( 18 - maxRshifts ) in the worst case, of all d_Q14's being equal to max_abs_d_Q14 */
extra_shifts = max_w_bits + 32 - Inlines.silk_CLZ32(max_abs_d_Q14) - 14;
/* Subtract what we got available; bits in output var plus maxRshifts */
extra_shifts -= (32 - 1 - 2 + maxRshifts); /* Keep sign bit free as well as 2 bits for accumulation */
extra_shifts = Inlines.silk_max_int(extra_shifts, 0);
maxRshifts_wxtra = maxRshifts + extra_shifts;
temp32 = Inlines.silk_RSHIFT(262, maxRshifts + extra_shifts) + 1; /* 1e-3f in Q( 18 - (maxRshifts + extra_shifts) ) */
wd = 0;
for (k = 0; k < nb_subfr; k++)
{
/* w has at least 2 bits of headroom so no overflow should happen */
temp32 = Inlines.silk_ADD32(temp32, Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k])); /* Q( 18 - maxRshifts_wxtra ) */
wd = Inlines.silk_ADD32(wd, Inlines.silk_LSHIFT(Inlines.silk_SMULWW(Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k]), d_Q14[k]), 2)); /* Q( 18 - maxRshifts_wxtra ) */
}
m_Q12 = Inlines.silk_DIV32_varQ(wd, temp32, 12);
b_Q14_ptr = 0;
for (k = 0; k < nb_subfr; k++)
{
/* w[ k ] from Q( 18 - corr_rshifts[ k ] ) to Q( 16 ) */
if (2 - corr_rshifts[k] > 0)
{
temp32 = Inlines.silk_RSHIFT(w[k], 2 - corr_rshifts[k]);
}
else {
temp32 = Inlines.silk_LSHIFT_SAT32(w[k], corr_rshifts[k] - 2);
}
g_Q26 = Inlines.silk_MUL(
Inlines.silk_DIV32(
((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/,
Inlines.silk_RSHIFT(((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/, 10) + temp32), /* Q10 */
Inlines.silk_LSHIFT_SAT32(Inlines.silk_SUB_SAT32((int)m_Q12, Inlines.silk_RSHIFT(d_Q14[k], 2)), 4)); /* Q16 */
temp32 = 0;
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
delta_b_Q14[i] = Inlines.silk_max_16(b_Q14[b_Q14_ptr + i], 1638); /* 1638_Q14 = 0.1_Q0 */
temp32 += delta_b_Q14[i]; /* Q14 */
}
temp32 = Inlines.silk_DIV32(g_Q26, temp32); /* Q14 . Q12 */
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
b_Q14[b_Q14_ptr + i] = (short)(Inlines.silk_LIMIT_32((int)b_Q14[b_Q14_ptr + i] + Inlines.silk_SMULWB(Inlines.silk_LSHIFT_SAT32(temp32, 4), delta_b_Q14[i]), -16000, 28000));
}
b_Q14_ptr += SilkConstants.LTP_ORDER;
}
}
/// <summary>
///
/// </summary>
/// <param name="LTP_coefs_Q16">[SilkConstants.LTP_ORDER]</param>
/// <param name="LTP_coefs_Q14">[SilkConstants.LTP_ORDER]</param>
/// <param name="LTP_coefs_Q14_ptr"></param>
internal static void silk_fit_LTP(
int[] LTP_coefs_Q16,
short[] LTP_coefs_Q14,
int LTP_coefs_Q14_ptr)
{
int i;
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
LTP_coefs_Q14[LTP_coefs_Q14_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(LTP_coefs_Q16[i], 2));
}
}
}
}

View File

@@ -0,0 +1,167 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class FindPitchLags
{
/* Find pitch lags */
internal static void silk_find_pitch_lags(
SilkChannelEncoder psEnc, /* I/O encoder state */
SilkEncoderControl psEncCtrl, /* I/O encoder control */
short[] res, /* O residual */
short[] x, /* I Speech signal */
int x_ptr
)
{
int buf_len, i, scale;
int thrhld_Q13, res_nrg;
int x_buf, x_buf_ptr;
short[] Wsig;
int Wsig_ptr;
int[] auto_corr = new int[SilkConstants.MAX_FIND_PITCH_LPC_ORDER + 1];
short[] rc_Q15 = new short[SilkConstants.MAX_FIND_PITCH_LPC_ORDER];
int[] A_Q24 = new int[SilkConstants.MAX_FIND_PITCH_LPC_ORDER];
short[] A_Q12 = new short[SilkConstants.MAX_FIND_PITCH_LPC_ORDER];
/******************************************/
/* Set up buffer lengths etc based on Fs */
/******************************************/
buf_len = psEnc.la_pitch + psEnc.frame_length + psEnc.ltp_mem_length;
/* Safety check */
Inlines.OpusAssert(buf_len >= psEnc.pitch_LPC_win_length);
x_buf = x_ptr - psEnc.ltp_mem_length;
/*************************************/
/* Estimate LPC AR coefficients */
/*************************************/
/* Calculate windowed signal */
Wsig = new short[psEnc.pitch_LPC_win_length];
/* First LA_LTP samples */
x_buf_ptr = x_buf + buf_len - psEnc.pitch_LPC_win_length;
Wsig_ptr = 0;
ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr, x, x_buf_ptr, 1, psEnc.la_pitch);
/* Middle un - windowed samples */
Wsig_ptr += psEnc.la_pitch;
x_buf_ptr += psEnc.la_pitch;
Array.Copy(x, x_buf_ptr, Wsig, Wsig_ptr, (psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1)));
/* Last LA_LTP samples */
Wsig_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1);
x_buf_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1);
ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr, x, x_buf_ptr, 2, psEnc.la_pitch);
/* Calculate autocorrelation sequence */
BoxedValueInt boxed_scale = new BoxedValueInt();
Autocorrelation.silk_autocorr(auto_corr, boxed_scale, Wsig, psEnc.pitch_LPC_win_length, psEnc.pitchEstimationLPCOrder + 1);
scale = boxed_scale.Val;
/* Add white noise, as fraction of energy */
auto_corr[0] = Inlines.silk_SMLAWB(auto_corr[0], auto_corr[0], ((int)((TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION, 16)*/) + 1;
/* Calculate the reflection coefficients using schur */
res_nrg = Schur.silk_schur(rc_Q15, auto_corr, psEnc.pitchEstimationLPCOrder);
/* Prediction gain */
psEncCtrl.predGain_Q16 = Inlines.silk_DIV32_varQ(auto_corr[0], Inlines.silk_max_int(res_nrg, 1), 16);
/* Convert reflection coefficients to prediction coefficients */
K2A.silk_k2a(A_Q24, rc_Q15, psEnc.pitchEstimationLPCOrder);
/* Convert From 32 bit Q24 to 16 bit Q12 coefs */
for (i = 0; i < psEnc.pitchEstimationLPCOrder; i++)
{
A_Q12[i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(A_Q24[i], 12));
}
/* Do BWE */
BWExpander.silk_bwexpander(A_Q12, psEnc.pitchEstimationLPCOrder, ((int)((TuningParameters.FIND_PITCH_BANDWIDTH_EXPANSION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_BANDWIDTH_EXPANSION, 16)*/);
/*****************************************/
/* LPC analysis filtering */
/*****************************************/
Filters.silk_LPC_analysis_filter(res, 0, x, x_buf, A_Q12, 0, buf_len, psEnc.pitchEstimationLPCOrder);
if (psEnc.indices.signalType != SilkConstants.TYPE_NO_VOICE_ACTIVITY && psEnc.first_frame_after_reset == 0)
{
/* Threshold for pitch estimator */
thrhld_Q13 = ((int)((0.6f) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(0.6f, 13)*/;
thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, ((int)((-0.004f) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(-0.004f, 13)*/, psEnc.pitchEstimationLPCOrder);
thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, ((int)((-0.1f) * ((long)1 << (21)) + 0.5))/*Inlines.SILK_CONST(-0.1f, 21)*/, psEnc.speech_activity_Q8);
thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, ((int)((-0.15f) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(-0.15f, 13)*/, Inlines.silk_RSHIFT(psEnc.prevSignalType, 1));
thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, ((int)((-0.1f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(-0.1f, 14)*/, psEnc.input_tilt_Q15);
thrhld_Q13 = Inlines.silk_SAT16(thrhld_Q13);
/*****************************************/
/* Call pitch estimator */
/*****************************************/
BoxedValueShort boxed_lagIndex = new BoxedValueShort(psEnc.indices.lagIndex);
BoxedValueSbyte boxed_contourIndex = new BoxedValueSbyte(psEnc.indices.contourIndex);
BoxedValueInt boxed_LTPcorr = new BoxedValueInt(psEnc.LTPCorr_Q15);
if (PitchAnalysisCore.silk_pitch_analysis_core(res, psEncCtrl.pitchL, boxed_lagIndex, boxed_contourIndex,
boxed_LTPcorr, psEnc.prevLag, psEnc.pitchEstimationThreshold_Q16,
(int)thrhld_Q13, psEnc.fs_kHz, psEnc.pitchEstimationComplexity, psEnc.nb_subfr) == 0)
{
psEnc.indices.signalType = SilkConstants.TYPE_VOICED;
}
else {
psEnc.indices.signalType = SilkConstants.TYPE_UNVOICED;
}
psEnc.indices.lagIndex = boxed_lagIndex.Val;
psEnc.indices.contourIndex = boxed_contourIndex.Val;
psEnc.LTPCorr_Q15 = boxed_LTPcorr.Val;
}
else {
Arrays.MemSetInt(psEncCtrl.pitchL, 0, SilkConstants.MAX_NB_SUBFR);
psEnc.indices.lagIndex = 0;
psEnc.indices.contourIndex = 0;
psEnc.LTPCorr_Q15 = 0;
}
}
}
}

View File

@@ -0,0 +1,171 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class FindPredCoefs
{
internal static void silk_find_pred_coefs(
SilkChannelEncoder psEnc, /* I/O encoder state */
SilkEncoderControl psEncCtrl, /* I/O encoder control */
short[] res_pitch, /* I Residual from pitch analysis */
short[] x, /* I Speech signal */
int x_ptr,
int condCoding /* I The type of conditional coding to use */
)
{
int i;
int[] invGains_Q16 = new int[SilkConstants.MAX_NB_SUBFR];
int[] local_gains = new int[SilkConstants.MAX_NB_SUBFR];
int[] Wght_Q15 = new int[SilkConstants.MAX_NB_SUBFR];
short[] NLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER];
int x_ptr2;
int x_pre_ptr;
short[] LPC_in_pre;
int tmp, min_gain_Q16, minInvGain_Q30;
int[] LTP_corrs_rshift = new int[SilkConstants.MAX_NB_SUBFR];
/* weighting for weighted least squares */
min_gain_Q16 = int.MaxValue >> 6;
for (i = 0; i < psEnc.nb_subfr; i++)
{
min_gain_Q16 = Inlines.silk_min(min_gain_Q16, psEncCtrl.Gains_Q16[i]);
}
for (i = 0; i < psEnc.nb_subfr; i++)
{
/* Divide to Q16 */
Inlines.OpusAssert(psEncCtrl.Gains_Q16[i] > 0);
/* Invert and normalize gains, and ensure that maximum invGains_Q16 is within range of a 16 bit int */
invGains_Q16[i] = Inlines.silk_DIV32_varQ(min_gain_Q16, psEncCtrl.Gains_Q16[i], 16 - 2);
/* Ensure Wght_Q15 a minimum value 1 */
invGains_Q16[i] = Inlines.silk_max(invGains_Q16[i], 363);
/* Square the inverted gains */
Inlines.OpusAssert(invGains_Q16[i] == Inlines.silk_SAT16(invGains_Q16[i]));
tmp = Inlines.silk_SMULWB(invGains_Q16[i], invGains_Q16[i]);
Wght_Q15[i] = Inlines.silk_RSHIFT(tmp, 1);
/* Invert the inverted and normalized gains */
local_gains[i] = Inlines.silk_DIV32(((int)1 << 16), invGains_Q16[i]);
}
LPC_in_pre = new short[psEnc.nb_subfr * psEnc.predictLPCOrder + psEnc.frame_length];
if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
int[] WLTP;
/**********/
/* VOICED */
/**********/
Inlines.OpusAssert(psEnc.ltp_mem_length - psEnc.predictLPCOrder >= psEncCtrl.pitchL[0] + SilkConstants.LTP_ORDER / 2);
WLTP = new int[psEnc.nb_subfr * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER];
/* LTP analysis */
BoxedValueInt boxed_codgain = new BoxedValueInt(psEncCtrl.LTPredCodGain_Q7);
FindLTP.silk_find_LTP(psEncCtrl.LTPCoef_Q14, WLTP, boxed_codgain,
res_pitch, psEncCtrl.pitchL, Wght_Q15, psEnc.subfr_length,
psEnc.nb_subfr, psEnc.ltp_mem_length, LTP_corrs_rshift);
psEncCtrl.LTPredCodGain_Q7 = boxed_codgain.Val;
/* Quantize LTP gain parameters */
BoxedValueSbyte boxed_periodicity = new BoxedValueSbyte(psEnc.indices.PERIndex);
BoxedValueInt boxed_gain = new BoxedValueInt(psEnc.sum_log_gain_Q7);
QuantizeLTPGains.silk_quant_LTP_gains(psEncCtrl.LTPCoef_Q14, psEnc.indices.LTPIndex, boxed_periodicity,
boxed_gain, WLTP, psEnc.mu_LTP_Q9, psEnc.LTPQuantLowComplexity, psEnc.nb_subfr
);
psEnc.indices.PERIndex = boxed_periodicity.Val;
psEnc.sum_log_gain_Q7 = boxed_gain.Val;
/* Control LTP scaling */
LTPScaleControl.silk_LTP_scale_ctrl(psEnc, psEncCtrl, condCoding);
/* Create LTP residual */
LTPAnalysisFilter.silk_LTP_analysis_filter(LPC_in_pre, x, x_ptr - psEnc.predictLPCOrder, psEncCtrl.LTPCoef_Q14,
psEncCtrl.pitchL, invGains_Q16, psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder);
}
else {
/************/
/* UNVOICED */
/************/
/* Create signal with prepended subframes, scaled by inverse gains */
x_ptr2 = x_ptr - psEnc.predictLPCOrder;
x_pre_ptr = 0;
for (i = 0; i < psEnc.nb_subfr; i++)
{
Inlines.silk_scale_copy_vector16(LPC_in_pre, x_pre_ptr, x, x_ptr2, invGains_Q16[i],
psEnc.subfr_length + psEnc.predictLPCOrder);
x_pre_ptr += psEnc.subfr_length + psEnc.predictLPCOrder;
x_ptr2 += psEnc.subfr_length;
}
Arrays.MemSetShort(psEncCtrl.LTPCoef_Q14, 0, psEnc.nb_subfr * SilkConstants.LTP_ORDER);
psEncCtrl.LTPredCodGain_Q7 = 0;
psEnc.sum_log_gain_Q7 = 0;
}
/* Limit on total predictive coding gain */
if (psEnc.first_frame_after_reset != 0)
{
minInvGain_Q30 = ((int)((1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN_AFTER_RESET) * ((long)1 << (30)) + 0.5))/*Inlines.SILK_CONST(1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN_AFTER_RESET, 30)*/;
}
else {
minInvGain_Q30 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(16 << 7, (int)psEncCtrl.LTPredCodGain_Q7, ((int)((1.0f / 3f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f / 3f, 16)*/)); /* Q16 */
minInvGain_Q30 = Inlines.silk_DIV32_varQ(minInvGain_Q30,
Inlines.silk_SMULWW(((int)((SilkConstants.MAX_PREDICTION_POWER_GAIN) * ((long)1 << (0)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.MAX_PREDICTION_POWER_GAIN, 0)*/,
Inlines.silk_SMLAWB(((int)((0.25f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.25f, 18)*/, ((int)((0.75f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.75f, 18)*/, psEncCtrl.coding_quality_Q14)), 14);
}
/* LPC_in_pre contains the LTP-filtered input for voiced, and the unfiltered input for unvoiced */
FindLPC.silk_find_LPC(psEnc, NLSF_Q15, LPC_in_pre, minInvGain_Q30);
/* Quantize LSFs */
NLSF.silk_process_NLSFs(psEnc, psEncCtrl.PredCoef_Q12, NLSF_Q15, psEnc.prev_NLSFq_Q15);
/* Calculate residual energy using quantized LPC coefficients */
ResidualEnergy.silk_residual_energy(psEncCtrl.ResNrg, psEncCtrl.ResNrgQ, LPC_in_pre, psEncCtrl.PredCoef_Q12, local_gains,
psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder);
/* Copy to prediction struct for use in next frame for interpolation */
Array.Copy(NLSF_Q15, psEnc.prev_NLSFq_Q15, SilkConstants.MAX_LPC_ORDER);
}
}
}

View File

@@ -0,0 +1,187 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class GainQuantization
{
private static readonly int OFFSET = ((SilkConstants.MIN_QGAIN_DB * 128) / 6 + 16 * 128);
private static readonly int SCALE_Q16 = ((65536 * (SilkConstants.N_LEVELS_QGAIN - 1)) / (((SilkConstants.MAX_QGAIN_DB - SilkConstants.MIN_QGAIN_DB) * 128) / 6));
private static readonly int INV_SCALE_Q16 = ((65536 * (((SilkConstants.MAX_QGAIN_DB - SilkConstants.MIN_QGAIN_DB) * 128) / 6)) / (SilkConstants.N_LEVELS_QGAIN - 1));
/// <summary>
/// Gain scalar quantization with hysteresis, uniform on log scale
/// </summary>
/// <param name="ind">O gain indices [MAX_NB_SUBFR]</param>
/// <param name="gain_Q16">I/O gains (quantized out) [MAX_NB_SUBFR]</param>
/// <param name="prev_ind">I/O last index in previous frame. [Porting note] original implementation passed this as an int8*</param>
/// <param name="conditional">I first gain is delta coded if 1</param>
/// <param name="nb_subfr">I number of subframes</param>
internal static void silk_gains_quant(
sbyte[] ind,
int[] gain_Q16,
BoxedValueSbyte prev_ind,
int conditional,
int nb_subfr)
{
int k, double_step_size_threshold;
for (k = 0; k < nb_subfr; k++)
{
// Debug.WriteLine("2a 0x{0:x}", (uint)gain_Q16[k]);
/* Convert to log scale, scale, floor() */
ind[k] = (sbyte)(Inlines.silk_SMULWB(SCALE_Q16, Inlines.silk_lin2log(gain_Q16[k]) - OFFSET));
/* Round towards previous quantized gain (hysteresis) */
if (ind[k] < prev_ind.Val)
{
ind[k]++;
}
ind[k] = (sbyte)(Inlines.silk_LIMIT_int(ind[k], 0, SilkConstants.N_LEVELS_QGAIN - 1));
/* Compute delta indices and limit */
if (k == 0 && conditional == 0)
{
/* Full index */
ind[k] = (sbyte)(Inlines.silk_LIMIT_int(ind[k], prev_ind.Val + SilkConstants.MIN_DELTA_GAIN_QUANT, SilkConstants.N_LEVELS_QGAIN - 1));
prev_ind.Val = ind[k];
}
else
{
/* Delta index */
ind[k] = (sbyte)(ind[k] - prev_ind.Val);
/* Double the quantization step size for large gain increases, so that the max gain level can be reached */
double_step_size_threshold = 2 * SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.N_LEVELS_QGAIN + prev_ind.Val;
if (ind[k] > double_step_size_threshold)
{
ind[k] = (sbyte)(double_step_size_threshold + Inlines.silk_RSHIFT(ind[k] - double_step_size_threshold + 1, 1));
}
ind[k] = (sbyte)(Inlines.silk_LIMIT_int(ind[k], SilkConstants.MIN_DELTA_GAIN_QUANT, SilkConstants.MAX_DELTA_GAIN_QUANT));
/* Accumulate deltas */
if (ind[k] > double_step_size_threshold)
{
prev_ind.Val += (sbyte)(Inlines.silk_LSHIFT(ind[k], 1) - double_step_size_threshold);
}
else
{
prev_ind.Val += ind[k];
}
/* Shift to make non-negative */
ind[k] -= SilkConstants.MIN_DELTA_GAIN_QUANT;
// Debug.WriteLine("2b 0x{0:x}", (uint)ind[k]);
}
/* Scale and convert to linear scale */
gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(INV_SCALE_Q16, prev_ind.Val) + OFFSET, 3967)); /* 3967 = 31 in Q7 */
}
}
/// <summary>
/// Gains scalar dequantization, uniform on log scale
/// </summary>
/// <param name="gain_Q16">O quantized gains [MAX_NB_SUBFR]</param>
/// <param name="ind">I gain indices [MAX_NB_SUBFR]</param>
/// <param name="prev_ind">I/O last index in previous frame [Porting note] original implementation passed this as an int8*</param>
/// <param name="conditional">I first gain is delta coded if 1</param>
/// <param name="nb_subfr">I number of subframes</param>
internal static void silk_gains_dequant(
int[] gain_Q16,
sbyte[] ind,
BoxedValueSbyte prev_ind,
int conditional,
int nb_subfr)
{
int k, ind_tmp, double_step_size_threshold;
for (k = 0; k < nb_subfr; k++)
{
if (k == 0 && conditional == 0)
{
/* Gain index is not allowed to go down more than 16 steps (~21.8 dB) */
prev_ind.Val = (sbyte)(Inlines.silk_max_int(ind[k], prev_ind.Val - 16));
}
else
{
/* Delta index */
ind_tmp = ind[k] + SilkConstants.MIN_DELTA_GAIN_QUANT;
/* Accumulate deltas */
double_step_size_threshold = 2 * SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.N_LEVELS_QGAIN + prev_ind.Val;
if (ind_tmp > double_step_size_threshold)
{
prev_ind.Val += (sbyte)(Inlines.silk_LSHIFT(ind_tmp, 1) - double_step_size_threshold);
}
else
{
prev_ind.Val += (sbyte)(ind_tmp);
}
}
prev_ind.Val = (sbyte)(Inlines.silk_LIMIT_int(prev_ind.Val, 0, SilkConstants.N_LEVELS_QGAIN - 1));
/* Scale and convert to linear scale */
gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(INV_SCALE_Q16, prev_ind.Val) + OFFSET, 3967)); /* 3967 = 31 in Q7 */
}
}
/// <summary>
/// Compute unique identifier of gain indices vector
/// </summary>
/// <param name="ind">I gain indices [MAX_NB_SUBFR]</param>
/// <param name="nb_subfr">I number of subframes</param>
/// <returns>unique identifier of gains</returns>
internal static int silk_gains_ID(sbyte[] ind, int nb_subfr)
{
int k;
int gainsID;
gainsID = 0;
for (k = 0; k < nb_subfr; k++)
{
gainsID = Inlines.silk_ADD_LSHIFT32(ind[k], gainsID, 8);
}
return gainsID;
}
}
}

View File

@@ -0,0 +1,90 @@
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class HPVariableCutoff
{
/// <summary>
/// High-pass filter with cutoff frequency adaptation based on pitch lag statistics
/// </summary>
/// <param name="state_Fxx">I/O Encoder states</param>
internal static void silk_HP_variable_cutoff(SilkChannelEncoder[] state_Fxx)
{
int quality_Q15;
int pitch_freq_Hz_Q16, pitch_freq_log_Q7, delta_freq_Q7;
SilkChannelEncoder psEncC1 = state_Fxx[0];
/* Adaptive cutoff frequency: estimate low end of pitch frequency range */
if (psEncC1.prevSignalType == SilkConstants.TYPE_VOICED)
{
/* difference, in log domain */
pitch_freq_Hz_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(Inlines.silk_MUL(psEncC1.fs_kHz, 1000), 16), psEncC1.prevLag);
pitch_freq_log_Q7 = Inlines.silk_lin2log(pitch_freq_Hz_Q16) - (16 << 7);
/* adjustment based on quality */
quality_Q15 = psEncC1.input_quality_bands_Q15[0];
pitch_freq_log_Q7 = Inlines.silk_SMLAWB(pitch_freq_log_Q7, Inlines.silk_SMULWB(Inlines.silk_LSHIFT(-quality_Q15, 2), quality_Q15),
pitch_freq_log_Q7 - (Inlines.silk_lin2log(((int)((TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ, 16)*/) - (16 << 7)));
/* delta_freq = pitch_freq_log - psEnc.variable_HP_smth1; */
delta_freq_Q7 = pitch_freq_log_Q7 - Inlines.silk_RSHIFT(psEncC1.variable_HP_smth1_Q15, 8);
if (delta_freq_Q7 < 0)
{
/* less smoothing for decreasing pitch frequency, to track something close to the minimum */
delta_freq_Q7 = Inlines.silk_MUL(delta_freq_Q7, 3);
}
/* limit delta, to reduce impact of outliers in pitch estimation */
delta_freq_Q7 = Inlines.silk_LIMIT_32(
delta_freq_Q7,
0 - ((int)((TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ, 7)*/,
((int)((TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ, 7)*/);
/* update smoother */
psEncC1.variable_HP_smth1_Q15 = Inlines.silk_SMLAWB(psEncC1.variable_HP_smth1_Q15,
Inlines.silk_SMULBB(psEncC1.speech_activity_Q8, delta_freq_Q7), ((int)((TuningParameters.VARIABLE_HP_SMTH_COEF1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_SMTH_COEF1, 16)*/);
/* limit frequency range */
psEncC1.variable_HP_smth1_Q15 = Inlines.silk_LIMIT_32(psEncC1.variable_HP_smth1_Q15,
Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8),
Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MAX_CUTOFF_HZ), 8));
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More