···1616{
1717 public sealed class TrackBass : Track, IBassAudio, IHasPitchAdjust
1818 {
1919+ public const int BYTES_PER_SAMPLE = 4;
2020+1921 private AsyncBufferStream dataStream;
20222123 /// <summary>
···3941 private bool isPlayed;
40424143 private long byteLength;
4444+4545+ /// <summary>
4646+ /// The last position that a seek will succeed for.
4747+ /// </summary>
4848+ private double lastSeekablePosition;
42494350 private FileCallbacks fileCallbacks;
4451 private SyncCallback stopCallback;
···96103 if (success)
97104 {
98105 Length = seconds * 1000;
106106+107107+ // Bass does not allow seeking to the end of the track, so the last available position is 1 sample before.
108108+ lastSeekablePosition = Bass.ChannelBytes2Seconds(activeStream, byteLength - BYTES_PER_SAMPLE) * 1000;
99109100110 Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Frequency, out float frequency);
101111 initialFrequency = frequency;
···227237 {
228238 // At this point the track may not yet be loaded which is indicated by a 0 length.
229239 // In that case we still want to return true, hence the conservative length.
230230- double conservativeLength = Length == 0 ? double.MaxValue : Length;
240240+ double conservativeLength = Length == 0 ? double.MaxValue : lastSeekablePosition;
231241 double conservativeClamped = MathHelper.Clamp(seek, 0, conservativeLength);
232242233243 await EnqueueAction(() =>
+3-5
osu.Framework/Audio/Track/Waveform.cs
···2828 /// </summary>
2929 private const int points_per_iteration = 100000;
30303131- private const int bytes_per_sample = 4;
3232-3331 /// <summary>
3432 /// FFT1024 gives ~40hz accuracy.
3533 /// </summary>
···9391 // Each "point" is generated from a number of samples, each sample contains a number of channels
9492 int samplesPerPoint = (int)(info.Frequency * resolution * info.Channels);
95939696- int bytesPerPoint = samplesPerPoint * bytes_per_sample;
9494+ int bytesPerPoint = samplesPerPoint * TrackBass.BYTES_PER_SAMPLE;
97959896 points.Capacity = (int)(length / bytesPerPoint);
999710098 // Each iteration pulls in several samples
10199 int bytesPerIteration = bytesPerPoint * points_per_iteration;
102102- var sampleBuffer = new float[bytesPerIteration / bytes_per_sample];
100100+ var sampleBuffer = new float[bytesPerIteration / TrackBass.BYTES_PER_SAMPLE];
103101104102 // Read sample data
105103 while (length > 0)
106104 {
107105 length = Bass.ChannelGetData(decodeStream, sampleBuffer, bytesPerIteration);
108108- int samplesRead = (int)(length / bytes_per_sample);
106106+ int samplesRead = (int)(length / TrackBass.BYTES_PER_SAMPLE);
109107110108 // Each point is composed of multiple samples
111109 for (int i = 0; i < samplesRead; i += samplesPerPoint)