Skip to content

Commit 43e6df5

Browse files
committed
Merge branch 'main' into inaudible_behavior
2 parents 4814631 + 5cab532 commit 43e6df5

20 files changed

+1669
-17
lines changed

.vscode/tasks.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"tasks": [
44
{
55
"label": "compile linux debug verbose",
6-
"command": "cd ${workspaceFolder}/example; flutter build linux -t lib/buffer_stream/websocket.dart --debug --verbose",
6+
"command": "cd ${workspaceFolder}/example; flutter build linux -t lib/filters/limiter.dart --debug --verbose",
77
// "args": ["build", "linux", "--verbose"],
88
"type": "shell"
99
},

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### 2.1.8 ()
22
- fix: clicks and pops when changing waveform frequency #156
33
- added BufferStream #148. Now it's possible to add audio data and listen to them. It provides a customizable buffering length which automatycally pause the playing handle for example when receiving audio data from the web.
4+
- added Limiter and Compressor filters (see `example/lib/filters`)
45
- added `setInaudibleBehavior`. When a 3D sound is too far to be eard, you can tell `flutter_soloud` to kill it.
56

67
### 2.1.7 (29 Oct 2024)

android/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ list(APPEND PLUGIN_SOURCES
4444
"${SRC_DIR}/filters/filters.cpp"
4545
"${SRC_DIR}/filters/pitch_shift_filter.cpp"
4646
"${SRC_DIR}/filters/smbPitchShift.cpp"
47+
"${SRC_DIR}/filters/limiter.cpp"
48+
"${SRC_DIR}/filters/compressor.cpp"
4749
${TARGET_SOURCES}
4850
)
4951

example/lib/filters/compressor.dart

+308
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
import 'dart:developer' as dev;
2+
3+
import 'package:flutter/foundation.dart';
4+
import 'package:flutter/material.dart';
5+
import 'package:flutter_soloud/flutter_soloud.dart';
6+
import 'package:logging/logging.dart';
7+
8+
/// This example shows the use of the compressor filter.
9+
///
10+
/// Happens that playing multple sounds at the same time can create a lot of
11+
/// noise and output distortion. The compressor filter can be used to reduce
12+
/// this behaviour.
13+
///
14+
/// In this example we play a sound multiple times to have a distorted sound.
15+
/// Then we activate the compressor filter and adjust the compressor parameters.
16+
///
17+
/// ** Compressor parameters**:
18+
/// 'wet`: Mix between original (dry) and compressed (wet) signal. 0.0 = 100%
19+
/// dry, 1.0 = 100% wet.
20+
///
21+
/// `threshold`: The threshold in dB at which compression starts. Values
22+
/// lower than the threshold will be compressed.
23+
///
24+
/// `makeupGain`: The make-up gain in dB applied to the compressed signal
25+
/// to compensate for loss in volume due to compression.
26+
///
27+
/// `kneeWidth`: The width in dB of the soft knee where compression smoothly
28+
/// begins to take effect. A larger value smooths compression.
29+
///
30+
/// `ratio`: The compression ratio. The amount by which input exceeding the
31+
/// threshold will be reduced. For example, 4:1 reduces 4 dB of input to 1 dB.
32+
///
33+
/// `attackTime`: The time in ms for the compressor to react to a sudden
34+
/// increase in input level.
35+
///
36+
/// `releaseTime`: The time in ms for the compressor to release the gain
37+
/// reduction after the input level falls below the threshold.
38+
///
39+
/// Tip: when using other filter, it's preferable to activate the compressor
40+
/// filter after all the others. Doing this if underlaying filters gain the
41+
/// volume too much, this filter will act on top of it.
42+
43+
void main() async {
44+
// The `flutter_soloud` package logs everything
45+
// (from severe warnings to fine debug messages)
46+
// using the standard `package:logging`.
47+
// You can listen to the logs as shown below.
48+
Logger.root.level = kDebugMode ? Level.FINE : Level.INFO;
49+
Logger.root.onRecord.listen((record) {
50+
dev.log(
51+
record.message,
52+
time: record.time,
53+
level: record.level.value,
54+
name: record.loggerName,
55+
zone: record.zone,
56+
error: record.error,
57+
stackTrace: record.stackTrace,
58+
);
59+
});
60+
61+
WidgetsFlutterBinding.ensureInitialized();
62+
63+
/// Initialize the player.
64+
await SoLoud.instance.init(sampleRate: 22050);
65+
66+
runApp(
67+
const MaterialApp(
68+
home: CompressorExample(),
69+
),
70+
);
71+
}
72+
73+
class CompressorExample extends StatefulWidget {
74+
const CompressorExample({super.key});
75+
76+
@override
77+
State<CompressorExample> createState() => _CompressorExampleState();
78+
}
79+
80+
class _CompressorExampleState extends State<CompressorExample> {
81+
final compressor = SoLoud.instance.filters.compressorFilter;
82+
AudioSource? sound;
83+
late double wet;
84+
late double threshold;
85+
late double makeupGain;
86+
late double kneeWidth;
87+
late double ratio;
88+
late double attackTime;
89+
late double releaseTime;
90+
bool isFilterActive = false;
91+
92+
@override
93+
void initState() {
94+
super.initState();
95+
96+
wet = compressor.queryWet.def;
97+
threshold = compressor.queryThreshold.def;
98+
makeupGain = compressor.queryMakeupGain.def;
99+
kneeWidth = compressor.queryKneeWidth.def;
100+
ratio = compressor.queryRatio.def;
101+
attackTime = compressor.queryAttackTime.def;
102+
releaseTime = compressor.queryReleaseTime.def;
103+
}
104+
105+
@override
106+
void dispose() {
107+
SoLoud.instance.deinit();
108+
super.dispose();
109+
}
110+
111+
@override
112+
Widget build(BuildContext context) {
113+
if (!SoLoud.instance.isInitialized) return const SizedBox.shrink();
114+
115+
return Scaffold(
116+
body: Padding(
117+
padding: const EdgeInsets.all(16),
118+
child: Column(
119+
children: [
120+
Row(
121+
mainAxisSize: MainAxisSize.min,
122+
children: [
123+
const Text('Activate Compressor'),
124+
Checkbox(
125+
value: isFilterActive,
126+
onChanged: (value) {
127+
if (value!) {
128+
compressor.activate();
129+
compressor.wet.value = wet;
130+
compressor.threshold.value = threshold;
131+
compressor.makeupGain.value = makeupGain;
132+
compressor.kneeWidth.value = kneeWidth;
133+
compressor.ratio.value = ratio;
134+
compressor.attackTime.value = attackTime;
135+
compressor.releaseTime.value = releaseTime;
136+
} else {
137+
compressor.deactivate();
138+
}
139+
setState(() {
140+
isFilterActive = value;
141+
});
142+
},
143+
),
144+
],
145+
),
146+
147+
ElevatedButton(
148+
onPressed: () async {
149+
sound = await SoLoud.instance
150+
.loadAsset('assets/audio/8_bit_mentality.mp3');
151+
},
152+
child: const Text('load'),
153+
),
154+
155+
///
156+
ElevatedButton(
157+
onPressed: () {
158+
SoLoud.instance.play(sound!, looping: true);
159+
SoLoud.instance.play(sound!, looping: true);
160+
SoLoud.instance.play(sound!, looping: true);
161+
SoLoud.instance.play(sound!, looping: true);
162+
SoLoud.instance.play(sound!, looping: true);
163+
SoLoud.instance.play(sound!, looping: true);
164+
},
165+
child: const Text('play sound'),
166+
),
167+
168+
///
169+
ElevatedButton(
170+
onPressed: () async {
171+
await SoLoud.instance.disposeAllSources();
172+
},
173+
child: const Text('stop all'),
174+
),
175+
176+
///
177+
Row(
178+
children: [
179+
Text('Wet ${wet.toStringAsFixed(2)}'),
180+
Expanded(
181+
child: Slider(
182+
value: wet,
183+
min: compressor.queryWet.min,
184+
max: compressor.queryWet.max,
185+
onChanged: (value) {
186+
setState(() {
187+
wet = value;
188+
compressor.wet.value = value;
189+
});
190+
},
191+
),
192+
),
193+
],
194+
),
195+
Row(
196+
children: [
197+
Text('Threshold ${threshold.toStringAsFixed(2)}'),
198+
Expanded(
199+
child: Slider(
200+
value: threshold,
201+
min: compressor.queryThreshold.min,
202+
max: compressor.queryThreshold.max,
203+
onChanged: (value) {
204+
setState(() {
205+
threshold = value;
206+
compressor.threshold.value = value;
207+
});
208+
},
209+
),
210+
),
211+
],
212+
),
213+
Row(
214+
children: [
215+
Text('Makeup gain ${makeupGain.toStringAsFixed(2)}'),
216+
Expanded(
217+
child: Slider(
218+
value: makeupGain,
219+
min: compressor.queryMakeupGain.min,
220+
max: compressor.queryMakeupGain.max,
221+
onChanged: (value) {
222+
setState(() {
223+
makeupGain = value;
224+
compressor.makeupGain.value = value;
225+
});
226+
},
227+
),
228+
),
229+
],
230+
),
231+
Row(
232+
children: [
233+
Text('Knee width ${kneeWidth.toStringAsFixed(2)}'),
234+
Expanded(
235+
child: Slider(
236+
value: kneeWidth,
237+
min: compressor.queryKneeWidth.min,
238+
max: compressor.queryKneeWidth.max,
239+
onChanged: (value) {
240+
setState(() {
241+
kneeWidth = value;
242+
compressor.kneeWidth.value = value;
243+
});
244+
},
245+
),
246+
),
247+
],
248+
),
249+
Row(
250+
children: [
251+
Text('Ratio ${ratio.toStringAsFixed(2)}'),
252+
Expanded(
253+
child: Slider(
254+
value: ratio,
255+
min: compressor.queryRatio.min,
256+
max: compressor.queryRatio.max,
257+
onChanged: (value) {
258+
setState(() {
259+
ratio = value;
260+
compressor.ratio.value = value;
261+
});
262+
},
263+
),
264+
),
265+
],
266+
),
267+
Row(
268+
children: [
269+
Text('Attack time ${attackTime.toStringAsFixed(2)}'),
270+
Expanded(
271+
child: Slider(
272+
value: attackTime,
273+
min: compressor.queryAttackTime.min,
274+
max: compressor.queryAttackTime.max,
275+
onChanged: (value) {
276+
setState(() {
277+
attackTime = value;
278+
compressor.attackTime.value = value;
279+
});
280+
},
281+
),
282+
),
283+
],
284+
),
285+
Row(
286+
children: [
287+
Text('Release time ${releaseTime.toStringAsFixed(2)}'),
288+
Expanded(
289+
child: Slider(
290+
value: releaseTime,
291+
min: compressor.queryReleaseTime.min,
292+
max: compressor.queryReleaseTime.max,
293+
onChanged: (value) {
294+
setState(() {
295+
releaseTime = value;
296+
compressor.releaseTime.value = value;
297+
});
298+
},
299+
),
300+
),
301+
],
302+
),
303+
],
304+
),
305+
),
306+
);
307+
}
308+
}

0 commit comments

Comments
 (0)