Skip to content

Commit 1b72da7

Browse files
committed
fix float audio formats
1 parent 8b64784 commit 1b72da7

2 files changed

Lines changed: 57 additions & 28 deletions

File tree

osu-replay-viewer/Audio/AudioFormat.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,32 +46,35 @@ public class AudioFormat
4646

4747
public WaveFormat ToBass()
4848
{
49-
return new WaveFormat(SampleRate, BytesPerSample * 8, Channels);
49+
return new WaveFormat(SampleRate, PCMSize * 8, Channels);
5050
}
5151

5252
public byte[] AmpToBytes(float amp)
5353
{
54-
const float pcm8MaxValue = byte.MaxValue;
55-
const float pcm16MaxValue = short.MaxValue;
56-
const float pcm24MaxValue = 1 << 23; // 24 bit signed int max value
57-
const float pcm32MaxValue = int.MaxValue;
54+
var clamped = Math.Clamp(amp, -1f, 1f);
55+
56+
const float pcm8MaxValue = byte.MaxValue; // unsigned 0..255
57+
const float pcm16MaxValue = short.MaxValue; // signed
58+
const float pcm24MaxValue = 0x7FFFFF; // 24-bit signed max
59+
const float pcm32MaxValue = int.MaxValue; // signed
5860

5961
switch (PCMSize)
6062
{
6163
case 1:
62-
return [(byte)Math.Floor(amp * pcm8MaxValue)];
64+
// 8-bit PCM is unsigned; map -1..1 to 0..255 with rounding.
65+
return [(byte)MathF.Round((clamped * 0.5f + 0.5f) * pcm8MaxValue)];
6366
case 2:
64-
return BitConverter.GetBytes((short)(amp * pcm16MaxValue));
67+
return BitConverter.GetBytes((short)MathF.Round(clamped * pcm16MaxValue));
6568
case 3:
66-
var value24 = (int)(amp * pcm24MaxValue);
69+
var value24 = (int)MathF.Round(clamped * pcm24MaxValue);
6770
return
6871
[
6972
(byte)(value24 & 0xFF),
7073
(byte)((value24 >> 8) & 0xFF),
7174
(byte)((value24 >> 16) & 0xFF)
7275
];
7376
case 4:
74-
return BitConverter.GetBytes((int)(amp * pcm32MaxValue));
77+
return BitConverter.GetBytes((int)MathF.Round(clamped * pcm32MaxValue));
7578
default:
7679
return null;
7780
}

osu-replay-viewer/Audio/SampleBassAdapter.cs

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using osu.Framework.Audio.Mixing;
1+
using ManagedBass;
2+
using osu.Framework.Audio.Mixing;
23
using osu.Framework.Audio.Sample;
34
using System;
45
using System.Reflection;
@@ -28,41 +29,66 @@ public SampleBassAdapter(ISample sample) : base("test")
2829

2930
public AudioBuffer AsAudioBuffer()
3031
{
31-
var info = ManagedBass.Bass.SampleGetInfo(SampleId);
32-
33-
if (info.Channels < 1) return null;
34-
32+
var info = Bass.SampleGetInfo(SampleId);
33+
34+
if (info.Channels < 1 || info.Length <= 0)
35+
return null;
36+
37+
// BASS exposes either OriginalResolution or flags describing the stored sample depth.
38+
var pcmBits = info.OriginalResolution > 0
39+
? info.OriginalResolution
40+
: info.Flags.HasFlag(BassFlags.Float) ? 32
41+
: info.Flags.HasFlag(BassFlags.Byte) ? 8
42+
: 16;
43+
3544
var format = new AudioFormat
3645
{
3746
Channels = info.Channels,
3847
SampleRate = info.Frequency,
39-
PCMSize = (int)Math.Ceiling(info.Length / (info.Channels * info.Frequency * (TargetedSample.Length / 1000.0)))
48+
PCMSize = Math.Max(1, pcmBits / 8)
4049
};
4150

42-
var samples = info.Length / format.PCMSize / format.Channels;
51+
var bytesPerFrame = format.PCMSize * format.Channels;
52+
var samples = info.Length / bytesPerFrame;
53+
4354
var bytes = new byte[info.Length];
44-
ManagedBass.Bass.SampleGetData(SampleId, bytes);
55+
Bass.SampleGetData(SampleId, bytes);
4556

4657
var buff = new AudioBuffer(format, samples);
58+
var isFloat = info.Flags.HasFlag(BassFlags.Float);
59+
4760
for (int i = 0; i < samples * format.Channels; i++)
4861
{
49-
const float pcm8MaxValue = byte.MaxValue;
50-
const float pcm16MaxValue = short.MaxValue;
51-
const float pcm24MaxValue = 1 << 23; // 24 bit signed int max value
52-
const float pcm32MaxValue = int.MaxValue;
53-
62+
var offset = i * format.PCMSize;
63+
5464
buff.Data[i] = format.PCMSize switch
5565
{
56-
1 => bytes[i] / pcm8MaxValue,
57-
2 => BitConverter.ToInt16(bytes, i * format.PCMSize) / pcm16MaxValue,
58-
3 => ((bytes[i * format.PCMSize] & 0xFF) |
59-
((bytes[i * format.PCMSize + 1] & 0xFF) << 8) |
60-
((bytes[i * format.PCMSize + 2] & 0xFF) << 16)) / pcm24MaxValue,
61-
4 => BitConverter.ToInt32(bytes, i * format.PCMSize) / pcm32MaxValue,
66+
1 => (bytes[offset] - 128) / 128f, // 8-bit PCM is unsigned
67+
2 => BitConverter.ToInt16(bytes, offset) / (float)short.MaxValue,
68+
3 => Read24Bit(bytes, offset),
69+
4 => isFloat
70+
? BitConverter.ToSingle(bytes, offset)
71+
: BitConverter.ToInt32(bytes, offset) / (float)int.MaxValue,
6272
_ => 0f
6373
};
6474
}
75+
6576
return buff;
77+
78+
static float Read24Bit(byte[] buffer, int offset)
79+
{
80+
const float pcm24MaxValue = 0x7FFFFF; // (1 << 23) - 1
81+
82+
var sample = buffer[offset]
83+
| (buffer[offset + 1] << 8)
84+
| (buffer[offset + 2] << 16);
85+
86+
// Sign-extend the 24-bit value to 32-bit int
87+
if ((sample & 0x800000) != 0)
88+
sample |= unchecked((int)0xFF000000);
89+
90+
return sample / pcm24MaxValue;
91+
}
6692
}
6793
}
6894
}

0 commit comments

Comments
 (0)