Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added getPan, setPan and setPanAbsolute #91

Merged
merged 4 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"type": "cppvsdbg",
"request": "launch",
"args": [],
"program": "${workspaceFolder}/example/build/windows/runner/Debug/flutter_soloud_example.exe",
"program": "${workspaceFolder}/example/build/windows/x64/runner/Debug/flutter_soloud_example.exe",
"cwd": "${workspaceFolder}"
},
{
Expand All @@ -51,7 +51,7 @@
"type": "cppvsdbg",
"request": "launch",
"args": [],
"program": "${workspaceFolder}/example/build/windows/runner/Debug/flutter_soloud_example.exe",
"program": "${workspaceFolder}/example/build/windows/x64//runner/Debug/flutter_soloud_example.exe",
"cwd": "${workspaceFolder}"
},
{
Expand All @@ -70,6 +70,14 @@
"request": "launch",
"program": "${workspaceFolder}/example/build/linux/x64/debug/bundle/flutter_soloud_example",
"cwd": "${workspaceFolder}"
},
{
"name": "Debug native Linux test",
"preLaunchTask": "compile linux test debug",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/example/build/linux/x64/debug/bundle/flutter_soloud_example",
"cwd": "${workspaceFolder}"
}
]
}
6 changes: 6 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
// "args": ["build", "linux"],
"type": "shell"
},
{
"label": "compile linux test debug",
"command": "cd ${workspaceFolder}/example; flutter build linux -t tests/tests.dart --debug",
// "args": ["build", "linux"],
"type": "shell"
},
{
"label": "compile windows debug verbose",
"command": "cd ${workspaceFolder}/example; flutter build windows -t lib/main.dart --debug --verbose",
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 2.1.0
- added `getPan()`, `setPan()` and `setPanAbsolute()`.

### 2.0.2 (23 May 2024)
- Fixed wrong exception raised by `setVolume()` when a handle is no more valid.

Expand Down
29 changes: 29 additions & 0 deletions example/tests/tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ void main() async {
testAllInstancesFinished,
testCreateNotes,
testPlaySeekPause,
testPan,
testHandles,
loopingTests,
];
Expand Down Expand Up @@ -398,6 +399,30 @@ Future<void> testPlaySeekPause() async {
deinit();
}

/// Test instancing playing handles and their disposal
Future<void> testPan() async {
/// Start audio isolate
await initialize();

final song =
await SoLoud.instance.loadAsset('assets/audio/8_bit_mentality.mp3');

final handle = await SoLoud.instance.play(song, volume: 0.5);

SoLoud.instance.setPan(handle, -0.8);
var pan = SoLoud.instance.getPan(handle);
assert(closeTo(pan, -0.8, 0.00001), 'setPan() or getPan() failed!');

await delay(1000);

SoLoud.instance.setPan(handle, 0.8);
pan = SoLoud.instance.getPan(handle);
assert(closeTo(pan, 0.8, 0.00001), 'setPan() or getPan() failed!');
await delay(1000);

deinit();
}

/// Test instancing playing handles and their disposal
Future<void> testHandles() async {
/// Start audio isolate
Expand Down Expand Up @@ -501,6 +526,10 @@ Future<void> loadAsset() async {
});
}

bool closeTo(num value, num expected, num epsilon) {
return (value - expected).abs() <= epsilon.abs();
}

void printError(Object error, StackTrace stack) {
stderr.writeln('TEST error: $error\nstack: $stack');
exitCode = 1;
Expand Down
47 changes: 47 additions & 0 deletions lib/src/bindings_player_ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,53 @@ class FlutterSoLoudFfi {
'setVolume');
late final _setVolume = _setVolumePtr.asFunction<int Function(int, double)>();

/// Get a sound's current pan setting.
///
/// [handle] the sound handle.
/// Returns the range of the pan values is -1 to 1, where -1 is left, 0 is
/// middle and and 1 is right.
double getPan(int handle) {
// Note that because of the float<=>double conversion precision error
// (SoLoud lib uses floats), the returned value is not precise.
return _getPan(handle);
}

late final _getPanPtr =
_lookup<ffi.NativeFunction<ffi.Double Function(ffi.UnsignedInt)>>(
'getPan');
late final _getPan = _getPanPtr.asFunction<double Function(int)>();

/// Set a sound's current pan setting.
///
/// [handle] the sound handle.
/// [pan] the range of the pan values is -1 to 1, where -1 is left, 0 is
/// middle and and 1 is right.
void setPan(int handle, double pan) {
return _setPan(handle, pan);
}

late final _setPanPtr = _lookup<
ffi.NativeFunction<ffi.Void Function(ffi.UnsignedInt, ffi.Double)>>(
'setPan');
late final _setPan = _setPanPtr.asFunction<void Function(int, double)>();

/// Set the left/right volumes directly.
/// Note that this does not affect the value returned by getPan.
///
/// [handle] the sound handle.
/// [panLeft] value for the left pan.
/// [panRight] value for the right pan.
void setPanAbsolute(int handle, double panLeft, double panRight) {
return _setPanAbsolute(handle, panLeft, panRight);
}

late final _setPanAbsolutePtr = _lookup<
ffi.NativeFunction<
ffi.Void Function(
ffi.UnsignedInt, ffi.Double, ffi.Double)>>('setPanAbsolute');
late final _setPanAbsolute =
_setPanAbsolutePtr.asFunction<void Function(int, double, double)>();

/// Check if a handle is still valid.
///
/// [handle] handle to check
Expand Down
91 changes: 91 additions & 0 deletions lib/src/soloud.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,12 @@ interface class SoLoud {
/// and `1.0` meaning full volume.
///
/// Throws [SoLoudNotInitializedException] if the engine is not initialized.
///
/// Note that if you `setGlobalVolume()` to `0.8` and then
/// `getGlobalVolume()`, you might get a slightly different number,
/// such as `0.800000042353`.
/// This is expected since the internal audio engine uses float
/// instead of double, and so there are rounding errors.
double getGlobalVolume() {
if (!isInitialized) {
throw const SoLoudNotInitializedException();
Expand All @@ -1192,6 +1198,12 @@ interface class SoLoud {
/// to `1.0` (meaning full volume).
///
/// Throws [SoLoudNotInitializedException] if the engine is not initialized.
///
/// Note that if you `setGlobalVolume()` to `0.8` and then
/// `getGlobalVolume()`, you might get a slightly different number,
/// such as `0.800000042353`.
/// This is expected since the internal audio engine uses float
/// instead of double, and so there are rounding errors.
void setGlobalVolume(double volume) {
if (!isInitialized) {
throw const SoLoudNotInitializedException();
Expand All @@ -1211,6 +1223,11 @@ interface class SoLoud {
/// and `1.0` means its playing at full volume.
///
/// Throws [SoLoudNotInitializedException] if the engine is not initialized.
///
/// Note that if you `setVolume()` to `0.8` and then `getVolume()`, you might
/// get a slightly different number, such as `0.800000042353`.
/// This is expected since the internal audio engine uses float
/// instead of double, and so there are rounding errors.
double getVolume(SoundHandle handle) {
if (!isInitialized) {
throw const SoLoudNotInitializedException();
Expand All @@ -1225,13 +1242,87 @@ interface class SoLoud {
/// to `1.0` (meaning it should play at full volume).
///
/// Throws [SoLoudNotInitializedException] if the engine is not initialized.
///
/// Note that if you `setVolume()` to `0.8` and then `getVolume()`, you might
/// get a slightly different number, such as `0.800000042353`.
/// This is expected since the internal audio engine uses float
/// instead of double, and so there are rounding errors.
void setVolume(SoundHandle handle, double volume) {
if (!isInitialized) {
throw const SoLoudNotInitializedException();
}
SoLoudController().soLoudFFI.setVolume(handle, volume);
}

/// Get a sound's current pan setting.
///
/// [handle] the sound handle.
/// Returns the range of the pan values is -1 to 1, where -1 is left, 0 is
/// middle and and 1 is right.
///
/// Throws [SoLoudNotInitializedException] if the engine is not initialized.
///
/// Note that if you `setPan()` to `0.8` and then `getPan()`, you might
/// get a slightly different number, such as `0.800000042353`.
/// This is expected since the internal audio engine uses float
/// instead of double, and so there are rounding errors.
double getPan(SoundHandle handle) {
if (!isInitialized) {
throw const SoLoudNotInitializedException();
}
return SoLoudController().soLoudFFI.getPan(handle.id);
}

/// Set a sound's current pan setting.
///
/// [handle] the sound handle.
/// [pan] the range of the pan values is -1 to 1, where -1 is left, 0 is
/// middle and and 1 is right.
///
/// Throws [SoLoudNotInitializedException] if the engine is not initialized.
///
/// Note that if you `setPan()` to `0.8` and then `getPan()`, you might
/// get a slightly different number, such as `0.800000042353`.
/// This is expected since the internal audio engine uses float
/// instead of double, and so there are rounding errors.
void setPan(SoundHandle handle, double pan) {
alnitak marked this conversation as resolved.
Show resolved Hide resolved
if (!isInitialized) {
throw const SoLoudNotInitializedException();
}
assert(
pan >= -1 && pan <= 1,
'The pan argument must be in range -1 to 1 inclusive!',
);
return SoLoudController().soLoudFFI.setPan(handle.id, pan.clamp(-1, 1));
}

/// Set the left/right volumes directly.
/// Note that this does not affect the value returned by getPan.
///
/// [handle] the sound handle.
/// [panLeft] value for the left pan. Must be >= -1 and <= 1.
/// [panRight] value for the right pan. Must be >= -1 and <= 1.
///
/// Throws [SoLoudNotInitializedException] if the engine is not initialized.
void setPanAbsolute(SoundHandle handle, double panLeft, double panRight) {
if (!isInitialized) {
throw const SoLoudNotInitializedException();
}
assert(
panLeft >= -1 && panLeft <= 1,
'The panLeft argument must be in range -1 to 1 inclusive!',
);
assert(
panRight >= -1 && panRight <= 1,
'The panRight argument must be in range -1 to 1 inclusive!',
);
return SoLoudController().soLoudFFI.setPanAbsolute(
handle.id,
panLeft.clamp(-1, 1),
panRight.clamp(-1, 1),
);
}

/// Check if the [handle] is still valid.
///
/// Returns `true` if the sound instance identified by its [handle] is
Expand Down
37 changes: 37 additions & 0 deletions src/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,43 @@ extern "C"
return noError;
}

/// Get a sound's current pan setting.
///
/// [handle] the sound handle.
/// Returns the range of the pan values is -1 to 1, where -1 is left, 0 is middle and and 1 is right.
FFI_PLUGIN_EXPORT double getPan(unsigned int handle)
{
if (player.get() == nullptr || !player.get()->isInited())
return 0.0f;

return player.get()->getPan(handle);
}

/// Set a sound's current pan setting.
///
/// [handle] the sound handle.
/// [pan] the range of the pan values is -1 to 1, where -1 is left, 0 is middle and and 1 is right.
FFI_PLUGIN_EXPORT void setPan(unsigned int handle, double pan)
{
if (player.get() == nullptr || !player.get()->isInited())
return;
// Rounding to 6 decimal to work around the float to double precision.
player.get()->setPan(handle, pan);
}

/// Set the left/right volumes directly.
/// Note that this does not affect the value returned by getPan.
///
/// [handle] the sound handle.
/// [panLeft] value for the left pan.
/// [panRight] value for the right pan.
FFI_PLUGIN_EXPORT void setPanAbsolute(unsigned int handle, double panLeft, double panRight)
{
if (player.get() == nullptr || !player.get()->isInited())
return;
player.get()->setPanAbsolute(handle, panLeft, panRight);
}

/// Check if a handle is still valid.
///
/// [handle] handle to check
Expand Down
24 changes: 24 additions & 0 deletions src/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,30 @@ void Player::setVolume(SoLoud::handle handle, float volume)
return soloud.setVolume(handle, volume);
}

float Player::getPan(SoLoud::handle handle)
{
return soloud.getPan(handle);
}

void Player::setPan(SoLoud::handle handle, float pan)
{
if (pan > 1.0f)
pan = 1.0f;
if (pan < -1.0f)
pan = -1.0f;
Comment on lines +453 to +456
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see that we're sanitizing the input here in C code. Since this is likely not a performance bottleneck, I'd rather sanitize it in Dart land, since that's closer to the user's debugger.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right! Better to let the dev know what he is trying to do :)

soloud.setPan(handle, pan);
}

void Player::setPanAbsolute(SoLoud::handle handle, float panLeft, float panRight)
{
if (panLeft > 1.0f) panLeft = 1.0f;
if (panLeft < -1.0f) panLeft = -1.0f;
if (panRight > 1.0f) panRight = 1.0f;
if (panRight < -1.0f) panRight = -1.0f;
soloud.setPanAbsolute(handle, panLeft, panRight);
Comment on lines +462 to +466
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here for panAbsolute: ideally sanitize (and assert) in Dart-land

}


bool Player::isValidVoiceHandle(SoLoud::handle handle)
{
return soloud.isValidVoiceHandle(handle);
Expand Down
19 changes: 19 additions & 0 deletions src/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,25 @@ class Player
/// @param volume the new volume to set.
void setVolume(SoLoud::handle handle, float volume);

/// @brief Get a sound's current pan setting.
/// @param handle the sound handle.
/// @return the range of the pan values is -1 to 1, where -1 is left, 0 is middle and and 1 is right.
float getPan(SoLoud::handle handle);

/// @brief Set a sound's current pan setting.
/// @param handle the sound handle.
/// @param pan the range of the pan values is -1 to 1, where -1 is left, 0 is middle and and 1 is right.
void setPan(SoLoud::handle handle, float pan);

/// @brief Set the left/right volumes directly.
/// Note that this does not affect the value returned by getPan.
/// @param handle the sound handle.
/// @param panLeft value for the left pan.
/// @param panRight value for the right pan.
void setPanAbsolute(SoLoud::handle handle, float panLeft, float panRight);



/// @brief Check if a handle is still valid.
/// @param handle handle to check.
/// @return true if it still exists.
Expand Down