|
1 | | -using osu.Framework.Audio.Mixing; |
| 1 | +using ManagedBass; |
| 2 | +using osu.Framework.Audio.Mixing; |
2 | 3 | using osu.Framework.Audio.Sample; |
3 | 4 | using System; |
4 | 5 | using System.Reflection; |
@@ -28,41 +29,66 @@ public SampleBassAdapter(ISample sample) : base("test") |
28 | 29 |
|
29 | 30 | public AudioBuffer AsAudioBuffer() |
30 | 31 | { |
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 | + |
35 | 44 | var format = new AudioFormat |
36 | 45 | { |
37 | 46 | Channels = info.Channels, |
38 | 47 | 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) |
40 | 49 | }; |
41 | 50 |
|
42 | | - var samples = info.Length / format.PCMSize / format.Channels; |
| 51 | + var bytesPerFrame = format.PCMSize * format.Channels; |
| 52 | + var samples = info.Length / bytesPerFrame; |
| 53 | + |
43 | 54 | var bytes = new byte[info.Length]; |
44 | | - ManagedBass.Bass.SampleGetData(SampleId, bytes); |
| 55 | + Bass.SampleGetData(SampleId, bytes); |
45 | 56 |
|
46 | 57 | var buff = new AudioBuffer(format, samples); |
| 58 | + var isFloat = info.Flags.HasFlag(BassFlags.Float); |
| 59 | + |
47 | 60 | for (int i = 0; i < samples * format.Channels; i++) |
48 | 61 | { |
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 | + |
54 | 64 | buff.Data[i] = format.PCMSize switch |
55 | 65 | { |
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, |
62 | 72 | _ => 0f |
63 | 73 | }; |
64 | 74 | } |
| 75 | + |
65 | 76 | 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 | + } |
66 | 92 | } |
67 | 93 | } |
68 | 94 | } |
0 commit comments