Skip to content

Commit

Permalink
Fix various minor bugs (#40)
Browse files Browse the repository at this point in the history
* fix MS decoder channels and sample rate not being set in some cases

* Add extra native libs for linux-arm64, osx-arm64, and win-arm64

* Adding GetVersionString() to the public codec interfaces since there's no other clear way to expose what implementation you're using. Recompiling win-x64 and win-x86 native binaries to circumvent an AVX2 alignment issues that was recently fixed in libopus. Bumping nuget version to 2.1.2 and updating internal version strings.

* Addressing analyzer warnings (mostly missing documentation and comparing Span<> to null. Condition the IsAotCompatible tag to the .NetCore export only.
  • Loading branch information
lostromb authored May 4, 2024
1 parent ab71ede commit 8f78de3
Show file tree
Hide file tree
Showing 29 changed files with 205 additions and 54 deletions.
37 changes: 18 additions & 19 deletions CSharp/Concentus/Celt/Bands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ internal static uint quant_band_n1_encode(band_ctx ctx, Span<byte> encodedData,

ec = ctx.ec;

stereo = (Y != null) ? 1 : 0;
stereo = (!Y.IsEmpty) ? 1 : 0;
c = 0;
do
{
Expand All @@ -1026,7 +1026,7 @@ internal static uint quant_band_n1_encode(band_ctx ctx, Span<byte> encodedData,
x = Y;
x_ptr = Y_ptr;
} while (++c < 1 + stereo);
if (lowband_out != null)
if (!lowband_out.IsEmpty)
{
lowband_out[lowband_out_ptr] = Inlines.SHR16(X[X_ptr], 4);
}
Expand All @@ -1042,12 +1042,11 @@ internal static uint quant_band_n1_decode(band_ctx ctx, ReadOnlySpan<byte> encod
int stereo;
Span<int> x = X;
int x_ptr = X_ptr;
int encode;
EntropyCoder ec; // porting note: pointer

ec = ctx.ec;

stereo = (Y != null) ? 1 : 0;
stereo = (!Y.IsEmpty) ? 1 : 0;
c = 0;
do
{
Expand All @@ -1064,7 +1063,7 @@ internal static uint quant_band_n1_decode(band_ctx ctx, ReadOnlySpan<byte> encod
x_ptr = Y_ptr;
} while (++c < 1 + stereo);

if (lowband_out != null)
if (!lowband_out.IsEmpty)
{
lowband_out[lowband_out_ptr] = Inlines.SHR16(X[X_ptr], 4);
}
Expand Down Expand Up @@ -1145,7 +1144,7 @@ internal static uint quant_partition_encode(band_ctx ctx, Span<byte> encodedData
sbits = b - mbits;
ctx.remaining_bits -= qalloc;

if (lowband != null)
if (!lowband.IsEmpty)
{
next_lowband2 = (lowband_ptr + N); /* >32-bit split case */
}
Expand Down Expand Up @@ -1216,7 +1215,7 @@ internal static uint quant_partition_encode(band_ctx ctx, Span<byte> encodedData
}
else
{
if (lowband == null)
if (lowband.IsEmpty)
{
/* Noise */
for (j = 0; j < N; j++)
Expand Down Expand Up @@ -1323,7 +1322,7 @@ internal static uint quant_partition_decode(band_ctx ctx, ReadOnlySpan<byte> enc
sbits = b - mbits;
ctx.remaining_bits -= qalloc;

if (lowband != null)
if (!lowband.IsEmpty)
{
next_lowband2 = (lowband_ptr + N); /* >32-bit split case */
}
Expand Down Expand Up @@ -1396,7 +1395,7 @@ internal static uint quant_partition_decode(band_ctx ctx, ReadOnlySpan<byte> enc
}
else
{
if (lowband == null)
if (lowband.IsEmpty)
{
/* Noise */
for (j = 0; j < N; j++)
Expand Down Expand Up @@ -1471,7 +1470,7 @@ internal static uint quant_band_encode(band_ctx ctx, Span<byte> encodedData, Spa
recombine = tf_change;
/* Band recombining to increase frequency resolution */

if (lowband_scratch != null && lowband != null && (recombine != 0 || ((N_B & 1) == 0 && tf_change < 0) || B0 > 1))
if (!lowband_scratch.IsEmpty && !lowband.IsEmpty && (recombine != 0 || ((N_B & 1) == 0 && tf_change < 0) || B0 > 1))
{
lowband.Slice(lowband_ptr, N).CopyTo(lowband_scratch.Slice(lowband_scratch_ptr, N));
lowband = lowband_scratch;
Expand All @@ -1481,7 +1480,7 @@ internal static uint quant_band_encode(band_ctx ctx, Span<byte> encodedData, Spa
for (k = 0; k < recombine; k++)
{
haar1(X, X_ptr, N >> k, 1 << k);
if (lowband != null)
if (!lowband.IsEmpty)
haar1(lowband, lowband_ptr, N >> k, 1 << k);
fill = bit_interleave_table[fill & 0xF] | bit_interleave_table[fill >> 4] << 2;
}
Expand All @@ -1492,7 +1491,7 @@ internal static uint quant_band_encode(band_ctx ctx, Span<byte> encodedData, Spa
while ((N_B & 1) == 0 && tf_change < 0)
{
haar1(X, X_ptr, N_B, B);
if (lowband != null)
if (!lowband.IsEmpty)
haar1(lowband, lowband_ptr, N_B, B);
fill |= fill << B;
B <<= 1;
Expand All @@ -1507,7 +1506,7 @@ internal static uint quant_band_encode(band_ctx ctx, Span<byte> encodedData, Spa
if (B0 > 1)
{
deinterleave_hadamard(X, X_ptr, N_B >> recombine, B0 << recombine, longBlocks);
if (lowband != null)
if (!lowband.IsEmpty)
deinterleave_hadamard(lowband, lowband_ptr, N_B >> recombine, B0 << recombine, longBlocks);
}

Expand Down Expand Up @@ -1539,7 +1538,7 @@ internal static uint quant_band_encode(band_ctx ctx, Span<byte> encodedData, Spa
B <<= recombine;

/* Scale output for later folding */
if (lowband_out != null)
if (!lowband_out.IsEmpty)
{
int j;
int n;
Expand Down Expand Up @@ -1587,7 +1586,7 @@ internal static uint quant_band_decode(band_ctx ctx, ReadOnlySpan<byte> encodedD
recombine = tf_change;
/* Band recombining to increase frequency resolution */

if (lowband_scratch != null && lowband != null && (recombine != 0 || ((N_B & 1) == 0 && tf_change < 0) || B0 > 1))
if (!lowband_scratch.IsEmpty && !lowband.IsEmpty && (recombine != 0 || ((N_B & 1) == 0 && tf_change < 0) || B0 > 1))
{
lowband.Slice(lowband_ptr, N).CopyTo(lowband_scratch.Slice(lowband_scratch_ptr, N));
lowband = lowband_scratch;
Expand All @@ -1596,7 +1595,7 @@ internal static uint quant_band_decode(band_ctx ctx, ReadOnlySpan<byte> encodedD

for (k = 0; k < recombine; k++)
{
if (lowband != null)
if (!lowband.IsEmpty)
haar1(lowband, lowband_ptr, N >> k, 1 << k);
fill = bit_interleave_table[fill & 0xF] | bit_interleave_table[fill >> 4] << 2;
}
Expand All @@ -1606,7 +1605,7 @@ internal static uint quant_band_decode(band_ctx ctx, ReadOnlySpan<byte> encodedD
/* Increasing the time resolution */
while ((N_B & 1) == 0 && tf_change < 0)
{
if (lowband != null)
if (!lowband.IsEmpty)
haar1(lowband, lowband_ptr, N_B, B);
fill |= fill << B;
B <<= 1;
Expand All @@ -1620,7 +1619,7 @@ internal static uint quant_band_decode(band_ctx ctx, ReadOnlySpan<byte> encodedD
/* Reorganize the samples in time order instead of frequency order */
if (B0 > 1)
{
if (lowband != null)
if (!lowband.IsEmpty)
deinterleave_hadamard(lowband, lowband_ptr, N_B >> recombine, B0 << recombine, longBlocks);
}

Expand Down Expand Up @@ -1652,7 +1651,7 @@ internal static uint quant_band_decode(band_ctx ctx, ReadOnlySpan<byte> encodedD
B <<= recombine;

/* Scale output for later folding */
if (lowband_out != null)
if (!lowband_out.IsEmpty)
{
int j;
int n;
Expand Down
2 changes: 1 addition & 1 deletion CSharp/Concentus/Celt/Structs/CELTDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ internal int celt_decode_with_ec(ReadOnlySpan<byte> data, int data_ptr,
}
M = 1 << LM;

if (len < 0 || len > 1275 || pcm == null)
if (len < 0 || len > 1275 || pcm.IsEmpty)
return OpusError.OPUS_BAD_ARG;

N = M * mode.shortMdctSize;
Expand Down
2 changes: 1 addition & 1 deletion CSharp/Concentus/Celt/Structs/CeltEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ internal int celt_encode_with_ec(Span<short> pcm, int pcm_ptr, int frame_size, S
start = this.start;
end = this.end;
tf_estimate = 0;
if (nbCompressedBytes < 2 || pcm == null)
if (nbCompressedBytes < 2 || pcm.IsEmpty)
{
return OpusError.OPUS_BAD_ARG;
}
Expand Down
31 changes: 25 additions & 6 deletions CSharp/Concentus/Common/Resampler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -632,12 +632,31 @@ public SpeexResampler(int nb_channels, int ratio_num, int ratio_den, int in_rate
this.initialised = 1;
}

/// <summary>
/// Create a new resampler with integer input and output rates (in hertz).
/// </summary>
/// <param name="nb_channels">The number of channels to be processed</param>
/// <param name="in_rate">Input sampling rate, in hertz</param>
/// <param name="out_rate">Output sampling rate, in hertz</param>
/// <param name="quality">Resampling quality, from 0 to 10</param>
[Obsolete("Just use the regular constructor")]
public static SpeexResampler Create(int nb_channels, int in_rate, int out_rate, int quality)
{
return new SpeexResampler(nb_channels, in_rate, out_rate, in_rate, out_rate, quality);
}

/// <summary>
/// Create a new resampler with fractional input/output rates. The sampling
/// rate ratio is an arbitrary rational number with both the numerator and
/// denominator being 32-bit integers.
/// </summary>
/// <param name="nb_channels">The number of channels to be processed</param>
/// <param name="ratio_num">Numerator of sampling rate ratio</param>
/// <param name="ratio_den">Denominator of sampling rate ratio</param>
/// <param name="in_rate">Input sample rate rounded to the nearest integer (in hz)</param>
/// <param name="out_rate">Output sample rate rounded to the nearest integer (in hz)</param>
/// <param name="quality">Resampling quality, from 0 to 10</param>
/// <returns>A newly created restampler</returns>
[Obsolete("Just use the regular constructor")]
public static SpeexResampler Create(int nb_channels, int ratio_num, int ratio_den, int in_rate, int out_rate, int quality)
{
Expand Down Expand Up @@ -679,7 +698,7 @@ internal void Process(int channel_index, Span<short> input, int input_ptr, ref i
int ichunk = (ilen > xlen) ? xlen : ilen;
int ochunk = olen;

if (input != null)
if (!input.IsEmpty)
{
for (j = 0; j < ichunk; ++j)
this.mem[x + j + filt_offs] = input[input_ptr + j * istride];
Expand All @@ -693,7 +712,7 @@ internal void Process(int channel_index, Span<short> input, int input_ptr, ref i
ilen -= ichunk;
olen -= ochunk;
output_ptr += ochunk * this.out_stride;
if (input != null)
if (!input.IsEmpty)

input_ptr += ichunk * istride;
}
Expand Down Expand Up @@ -746,7 +765,7 @@ internal void Process(int channel_index, Span<float> input, int input_ptr, ref i
}
if (this.magic_samples[channel_index] == 0)
{
if (input != null)
if (!input.IsEmpty)
{
for (j = 0; j < ichunk; ++j)
this.mem[x + j + this.filt_len - 1] = WORD2INT(input[input_ptr + j * istride_save]);
Expand All @@ -769,7 +788,7 @@ internal void Process(int channel_index, Span<float> input, int input_ptr, ref i
ilen -= ichunk;
olen -= ochunk;
output_ptr += ((ochunk + omagic) * ostride_save);
if (input != null)
if (!input.IsEmpty)
input_ptr += ichunk * istride_save;
}
this.out_stride = ostride_save;
Expand Down Expand Up @@ -801,7 +820,7 @@ internal void ProcessInterleaved(Span<float> input, int input_ptr, ref int in_le
{
out_len = bak_out_len;
in_len = bak_in_len;
if (input != null)
if (!input.IsEmpty)
this.Process(i, input, input_ptr + i, ref in_len, output, output_ptr + i, ref out_len);
else
this.Process(i, null, 0, ref in_len, output, output_ptr + i, ref out_len);
Expand Down Expand Up @@ -834,7 +853,7 @@ internal void ProcessInterleaved(Span<short> input, int input_ptr, ref int in_le
{
out_len = bak_out_len;
in_len = bak_in_len;
if (input != null)
if (!input.IsEmpty)
this.Process(i, input, input_ptr + i, ref in_len, output, output_ptr + i, ref out_len);
else
this.Process(i, null, 0, ref in_len, output, output_ptr + i, ref out_len);
Expand Down
5 changes: 3 additions & 2 deletions CSharp/Concentus/Concentus.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\Concentus.snk</AssemblyOriginatorKeyFile>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>2.1.1</Version>
<Version>2.1.2</Version>
<Authors>Logan Stromberg</Authors>
<Description>This package is a portable C# implementation of the Opus audio compression codec (see https://opus-codec.org/ for more details). This package contains the Opus encoder, decoder, multistream codecs, repacketizer, as well as a port of the libspeexdsp resampler. It does NOT contain code to parse .ogg or .opus container files or to manage RTP packet streams.</Description>
<Copyright>© Xiph.Org Foundation, Skype Limited, CSIRO, Microsoft Corp.</Copyright>
Expand All @@ -17,7 +17,8 @@
<PackageTags>Concentus;Opus;Audio;Codec;Resampler;Resampling;DSP;Compression;Encoder;Decoder</PackageTags>
<PackageReleaseNotes>Big v2.0 overhaul! See commit notes on Git. Multistream encoding is no longer broken. Encoders / decoders are now IDisposable, accept Span inputs, and will try to use native Opus libraries if supported by your system.</PackageReleaseNotes>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<IsAotCompatible>true</IsAotCompatible>
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
<Title>Concentus</Title>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
6 changes: 6 additions & 0 deletions CSharp/Concentus/IOpusDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ public interface IOpusDecoder : IDisposable
/// </summary>
void ResetState();

/// <summary>
/// Gets the version string of the library backing this implementation.
/// </summary>
/// <returns>An arbitrary version string.</returns>
string GetVersionString();

/// <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
Expand Down
6 changes: 6 additions & 0 deletions CSharp/Concentus/IOpusEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ public interface IOpusEncoder : IDisposable
/// </summary>
void ResetState();

/// <summary>
/// Gets the version string of the library backing this implementation.
/// </summary>
/// <returns>An arbitrary version string.</returns>
string GetVersionString();

/// <summary>
/// Gets or sets the application (or signal type) of the input signal. This hints
/// to the encoder what type of details we want to preserve in the encoding.
Expand Down
6 changes: 6 additions & 0 deletions CSharp/Concentus/IOpusMultiStreamDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ public interface IOpusMultiStreamDecoder : IDisposable
/// </summary>
void ResetState();

/// <summary>
/// Gets the version string of the library backing this implementation.
/// </summary>
/// <returns>An arbitrary version string.</returns>
string GetVersionString();

/// <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
Expand Down
6 changes: 6 additions & 0 deletions CSharp/Concentus/IOpusMultiStreamEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ public interface IOpusMultiStreamEncoder : IDisposable
/// </summary>
void ResetState();

/// <summary>
/// Gets the version string of the library backing this implementation.
/// </summary>
/// <returns>An arbitrary version string.</returns>
string GetVersionString();

/// <summary>
/// Gets or sets the application (or signal type) of the input signal. This hints
/// to the encoder what type of details we want to preserve in the encoding.
Expand Down
Loading

0 comments on commit 8f78de3

Please sign in to comment.