-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
OpenAL Audio effects: reverb, chorus, delay #1708
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
Conversation
|
Thanks a lot for your contribution! 🙂 A few remarks regarding the API:
There are a few smaller things in the code, but I think they can be discussed at a later stage. |
|
The pointer registration mechanism is copied from No, users can't implement their own effects with I don't recall where I used the |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice PR, great job. A lot of work was clearly done here.
I have a few questions/remarks, though, see them in the review.
|
|
||
| // Loop while the sound is playing | ||
| while (sound.getStatus() == sf::Sound::Playing) | ||
| for (int i = 0; i < 2; ++i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe change "2" to some constant like "NUM_TIMES_SOUND_PLAYED"?
But I'm also not sure why we need to play it 2 times...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first loop the audio is played without the effect applied, then the second loop the audio is played with the effect, to demonstrate the difference in output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good to have a visual indicator for this. E.g. show "No effect" for the first play and "Chorus Effect" for the second. Ideally the code would be something like this:
playWithoutEffect(...);
playWithEffect(...);|
|
||
| // Loop while the music is playing | ||
| while (music.getStatus() == sf::Music::Playing) | ||
| for (int i = 0; i < 2; ++i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same applies here, consider replacing "2" by a constant or removing this.
| /// magnitudes the sample will repeat and fade out over time. Use this parameter to | ||
| /// create a "cascading" chorus effect. | ||
| /// | ||
| /// \param amount new feedback amount to set, between -1.f and 1.f. Defaults to 0.25f |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This review contains a lot of magic variables like "0.25f" or "0.404f". Can you please explain where they come from? I didn't find why they're significant.
(It's especially not clear why parameter ranges are the way they are: [some_magic_number_1;some_magic_number_2])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the default values as laid out in the OpenAL specification. I guess the comments should be at least updated to state that fact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, reference to OpenAL spec would be enough
src/SFML/Audio/SoundSource.cpp
Outdated
| return Stopped; | ||
| } | ||
|
|
||
| void SoundSource::setEffect(const SoundEffect* effect) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still not clear to me why effect can't be a const ref here, same as sf::SoundBuffer is passed by const ref to sf::Sound::setBuffer. To remove effect, we can add removeEffect function, which will be an explicit way of saying "I don't want this effect anymore".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been addresed in 1b41eb0 🙂
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
137bbaa to
d9ee97e
Compare
Maybe @LaurentGomila has some insight on why he chose the public design for SoundBuffer over private + friend. |
I think this requires an additional API discussion.
Anyone else's opinion on this design? |
|
|
|
The ranges are currently silently clamped to the min or max allowed value. That way even if you pass in like Question is, should we silently clamp the values or should we assert them instead or assert and then still clamp? |
This is an oversight on my part - I'd expect it to be 0 - 100 the same as the rest of SFML. If I'm honest this has always confused me slightly as OpenAL uses 0 - 1, so I'm not sure why SFML would differ, particularly when it has to be converted, but I'm happy to roll with it. As for asserting vs clamping - I'd personally prefer assertions because it's more obvious when something is wrong rather than silently hiding an error, although I'd expect OpenAL to print some kind of range error in these cases too. Ultimately though clamping the range has proven to be adequate enough in the existing functions with no adverse side effects that I'm aware of so I'd probably stick with that for consistency. |
|
On further examination |
mini code-tidy
update documentation of SoundSource to reflect setting an effect as nullptr removes existing effects add copy/swap assignment operator to SoundEffect class to ensure in-use SoundSources are tidied up
Clarify source of constant parameter values in Effect headers Add missing parameter name in SoundEffect.hpp
… to better describe its effect
530f96d to
934b9d8
Compare
|
Hi! I just wanted to say that this project looks very promising. Nicely done! :D If anyone involved has a minute to spare, I am curious about the following things: I've been working on a game engine on and off for the past 4 years which has SFML as its base. Would be nice if I could incorporate these features when I get around to further developing my SoundManager. (: |
|
Hi! To address your questions:
|
…const pointer rename SoundSource::resetEffect() to SoundSource::removeEffect() to better express its intended use
I've updated |
|
Closed because #2749 gets rid of OpenAL entirely |
|
Would've really loved to see this in an earlier version of SFML, but with us moving to miniaudio, it doesn't quite fit in. Thanks @fallahn for all the work, hopefully it can still be useful to someone. 🙂 |
Description
Implements OpenAL sound effects for reverb, chorus and delay. Can be easily extended to more types supported by OpenAL.
This was discussed some time (10 years!) ago here: https://en.sfml-dev.org/forums/index.php?topic=2245.0 the conclusion being that available OpenAL implementations were not capable. Thankfully things have changed somewhat in the last decade. I've tested this on platforms using openal-soft (windows and ubuntu) and on macOS using Apple's OpenAL library. I was pleasantly surprised to find that the effects are supported by the Apple library, although they are audibly rendered differently - particularly the delay which sounds much softer. However it seems openal-soft is supported on macOS and building SFML against that makes the effects sound consistent across tested platforms. I'm unable to test on mobile platforms.
I'm submitting this mostly because I find the effects useful, and it would be preferable not to have to maintain my own fork of SFML. I'd also like to gauge interest to potentially develop further with features such as low/high pass filters for example.
Tasks
API Overview
sf::SoundSource- Base class of e.g.sf::Musicorsf::Soundvoid setEffect(const SoundEffect* effect);- Set or remove (withNULL) effects to anything implementing SoundSourceconst SoundEffect* getEffect() const;- Get the currently set effect orNULLif none is setvoid resetEffect();- Internal use to reset the effect when thesf::SoundEffectinstance is destroyedsf::SoundEffect- Sound effect base classstatic bool isAvailable();- Not all platforms support effects (e.g. iOS)void setVolume(float volume);- Volume of the effect in the range or 0 to 1float getVolume() const;- Retrieve set volumesf::ChorusEffectvoid setWaveform(Waveform waveform);- Set the LFO waveform shape, either Sine or Trianglevoid setPhase(sf::Int32 angle);- Set the LFO phasevoid setRate(float rate);- Set the LFO modulation ratevoid setDepth(float depth);- Set the amount by which the delay time is modulatedvoid setFeedback(float amount);- Set the amount of processed signal that is fed back to the inputvoid setDelay(float delay);- Set the average amount of time the sample is delayedWaveform getWaveform() const;sf::Int32 getPhase() const;float getRate() const;float getDepth() const;float getFeedback() const;float getDelay() const;sf::DelayEffectvoid setDelay(float delay);- Set the delay between the original sound and the first 'tap', or echo instancevoid setLRDelay(float delay);- Set the delay between the first 'tap' and the second 'tap'void setDamping(float damping);- Set the amount of high frequency damping applied to each echovoid setFeedback(float feedback);- Set the amount of feedback the output signal fed back into the inputvoid setSpread(float spread);- Set how hard panned the individual echoes arefloat getDelay() const;float getLRDelay() const;float getDamping() const;float getFeedback() const;float getSpread() const;sf::ReverbEffectvoid setDensity(float density);- Set the coloration of the late reverbvoid setDiffusion(float diffusion);- Set the echo density in the reverberation decayvoid setGain(float gain);- Sets the max amount of reflections and reverberation added to the final sound mixvoid setDecayTime(float decay);- Set the reverberation decay timevoid setReflectionGain(float gain);- Set the overall amount of initial reflections relative to the Gain propertyvoid setReflectionDelay(float delay);- Set the amount of delay between the arrival time of the direct path from the source to the first reflection from the sourcevoid setLateReverbGain(float delay);- Set the overall amount of later reverberation relative to the Gain propertyvoid setLateReverbDelay(float delay);- Set the begin time of the late reverberation relative to the time of the initial reflectionvoid setRoomRolloff(float rolloff);- Set the attenuation of the reflected soundfloat getDensity() const;float getDiffusion() const;float getGain() const;float getDecayTime() const;float getReflectionGain() const;float getReflectionDelay() const;float getLateReverbGain() const;float getLateReverbDelay() const;float getRoomRolloff() const;How to test this PR?
I've updated the 'sound' example in the included examples directory to demonstrate the use of effects. The syntax is similar to that of
sf::SoundBuffertosf::Sound: