-
Notifications
You must be signed in to change notification settings - Fork 348
Expand file tree
/
Copy pathBassHelpers.cs
More file actions
191 lines (157 loc) · 6.73 KB
/
Copy pathBassHelpers.cs
File metadata and controls
191 lines (157 loc) · 6.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
using System;
using ManagedBass;
using ManagedBass.DirectX8;
using ManagedBass.Fx;
using UnityEngine;
using YARG.Core.Audio;
using YARG.Core.Logging;
using YARG.Settings;
namespace YARG.Audio.BASS
{
public static class BassHelpers
{
public const int PLAYBACK_BUFFER_LENGTH = 75;
public const double PLAYBACK_BUFFER_DESYNC = PLAYBACK_BUFFER_LENGTH / 1000.0;
public const float REVERB_VOLUME_MULTIPLIER = 0.80f;
public const int FADE_TIME_MILLISECONDS = 1000;
public static int ConfiguredPlaybackBufferLength => ClampPlaybackBufferLength(
SettingsManager.Settings?.PlaybackBufferLength.Value ?? 0);
public const int REVERB_SLIDE_IN_MILLISECONDS = 300;
public const int REVERB_SLIDE_OUT_MILLISECONDS = 500;
public const EffectType REVERB_TYPE = EffectType.Freeverb;
/*
* From Bass documentation (http://bass.radio42.com/help/html/4c663bda-2751-c2c3-eaf2-770b846b6652.htm)
* "With a ratio of 4:1, when the (time averaged) input level is 4 dB over the threshold, the output signal level will be 1 dB over the threshold."
* "[Additionally,] with any threshold/ratio combination, you could calculate the gain for a 0dB peak like this: fGain=fThreshold*(1/fRatio-1)"
*
* The intention of the gain is to normalize 0dB signals back to 0dB after compression.
* However, we only want the compressors to handle "clipping" situations (audio that exceeds 0dB).
* So we set the gain and thresholds both to zero - which still follows the formula.
* We can then set the ratio to whatever we want.
*
* Note: you don't want to apply a negative gain as the gain value effects ALL audio, not just the part that got compressed.
* We don't want to make quiet parts even quieter.
*/
public static readonly CompressorParameters CompressorParams = new()
{
fGain = 0f, fThreshold = 0, fAttack = 10f, fRelease = 100f, fRatio = 8,
};
public static readonly PeakEQParameters LowEqParams = new()
{
fBandwidth = 1.25f, fCenter = 250.0f, fGain = -12f
};
public static readonly PeakEQParameters MidEqParams = new()
{
fBandwidth = 1.25f, fCenter = 2300.0f, fGain = 2.25f
};
public static readonly PeakEQParameters HighEqParams = new()
{
fBandwidth = 0.75f, fCenter = 6000.0f, fGain = 2.25f
};
public static readonly DXReverbParameters DXReverbParams = new()
{
fInGain = -5f, fReverbMix = 0f, fReverbTime = 1000.0f, fHighFreqRTRatio = 0.001f
};
public static readonly ReverbParameters FreeverbParams = new()
{
fDryMix = 0.5f, fWetMix = 1.0f, fRoomSize = 0.8f, fDamp = 0.5f, fWidth = 1.0f, lMode = 0
};
public static int ClampPlaybackBufferLength(int length)
{
int minimumLength = GlobalAudioHandler.MinimumBufferLength;
if (length > 0 && minimumLength > 0 && length < minimumLength)
{
return minimumLength;
}
return length;
}
public static int FXAddParameters(int streamHandle, EffectType type, IEffectParameter parameters,
int priority = 0)
{
int fxHandle = Bass.ChannelSetFX(streamHandle, type, priority);
if (fxHandle == 0)
{
YargLogger.LogFormatError("Failed to create effects handle for {0}: {1}", type, Bass.LastError);
return 0;
}
if (!Bass.FXSetParameters(fxHandle, parameters))
{
YargLogger.LogFormatError("Failed to apply effects parameters for {0}: {1}", type, Bass.LastError);
Bass.ChannelRemoveFX(streamHandle, fxHandle);
return 0;
}
return fxHandle;
}
public static int FXAddParameters<T>(int streamHandle, EffectType type, T parameters, int priority = 0)
where T : unmanaged, IEffectParameter
{
int fxHandle = Bass.ChannelSetFX(streamHandle, type, priority);
if (fxHandle == 0)
{
YargLogger.LogFormatError("Failed to create effects handle: {0}", Bass.LastError);
return 0;
}
if (!FXSetParameters(fxHandle, parameters))
{
YargLogger.LogFormatError("Failed to apply effects parameters: {0}", Bass.LastError);
Bass.ChannelRemoveFX(streamHandle, fxHandle);
return 0;
}
return fxHandle;
}
public static unsafe bool FXSetParameters<T>(int Handle, T Parameters)
where T : unmanaged, IEffectParameter
{
return Bass.FXSetParameters(Handle, (IntPtr) (void*) &Parameters);
}
public static int AddCompressorToChannel(int handle)
{
return FXAddParameters(handle, EffectType.Compressor, CompressorParams);
}
public static int AddReverbToChannel(int handle)
{
IEffectParameter reverbParams = REVERB_TYPE switch
{
EffectType.DXReverb => DXReverbParams,
EffectType.Freeverb => FreeverbParams,
_ => throw new ArgumentOutOfRangeException()
};
return FXAddParameters(handle, REVERB_TYPE, reverbParams);
}
public static int AddEqToChannel(int handle, IEffectParameter eqParams)
{
return FXAddParameters(handle, EffectType.PeakEQ, eqParams);
}
public static int AddPitchShiftToChannel(int handle, IEffectParameter pitchParams)
{
return FXAddParameters(handle, EffectType.PitchShift, pitchParams);
}
public static unsafe bool ApplyGain(float gain, IntPtr buffer, int length)
{
var sampleBuffer = new Span<float>((void*) buffer, length / sizeof(float));
foreach (ref float sample in sampleBuffer)
{
sample *= gain;
}
return true;
}
public static int GetOutputChannelCount()
{
Bass.GetInfo(out BassInfo info);
return info.SpeakerCount;
}
#nullable enable
public static void UpdateOutputChannels(int stream, OutputChannel? channel)
#nullable disable
{
if (channel is not BassOutputChannel bassChannel)
{
// Remove assigned output channels
Bass.ChannelFlags(stream, 0, BassFlags.SpeakerFront);
return;
}
// Set channel(s)
Bass.ChannelFlags(stream, bassChannel.Flags, BassFlags.SpeakerFront);
}
}
}