-
Notifications
You must be signed in to change notification settings - Fork 4
Patch Notes
Two user-requested features were added in this release.
The JukeBox was missing a global volume setting that has been added now.
Previously, you had to register the TuningFork specific loader on libGDX's AssetManager like this:
assetManager.setLoader(SoundBuffer.class, new SoundBufferLoader(new InternalFileHandleResolver()));
That's fine and you can still do it this way, if you prefer. Since this version, the library auto registers its loaders when provided with an AssetManager in the AudioConfig
, or alternatively via audio.registerAssetManagerLoaders(assetManager)
.
The reason for this change is that a second loader now exists. Yeah I know it's just a second line to add, but that also requires users to know about the loaders and carefully reading patch notes or the wiki each time a new loader gets added in the future. I think it's easier for everybody to just let the library do the job.
I received a feature request to provide an API for rendering the waveform of an audio file. Rendering isn't really the job of this library, and API-wise, nothing was preventing users from doing it themselves. Raw audio samples could already be fetched from the low-level decoder InputStreams. However, there's always room for improvement, and that's what I'm aiming for with this addition to the API. In short: you still need to handle the rendering yourself, but I have integrated the necessary low-level components into the library. This should greatly simplify the entire process. An example is available here: WaveFormTest.
- Added
PcmUtil.averageSample()
- Added
PcmFormat.getChannels()
- Added
PcmFormat.getBitsPerSample()
- Added
PcmFormat.getDataType()
- Added
AudioConfig.setAssetManager()
- Added
Audio.registerAssetManagerLoaders()
- Added
ReadableSoundBuffer
- Added
ReadableSoundBufferLoader
- Added
ReadableSoundBuffer
load methods to all loaders - Added
SoundLoader.load(AudioStream)
- Added
JukeBox.setVolume()
andJukeBox.getVolume()
Click to expand!
A new audio format and a new OpenAL extension made it into this release.
QOA is a lossy audio compression format. It decodes audio 3x faster than Ogg-Vorbis, while offering better quality and compression than ADPCM. TuningFork got its own decoder and fully supports the format.
Since the OpenAL extension AL_SOFT_source_start_delay made it into libGDXs lwjgl version, we can finally support it.
This allows SoundBuffer
s and BufferedSoundSource
s to delay playback, or in other words start playback at a specific point in time. The internal clock of the audio device will be used, so this will allow to sync multiple sounds perfectly.
- Added
SoundBuffer.playAtTime(long)
- Added
BufferedSoundSource.playAtTime(long)
- Added support for the Quite Ok Audio (QOA) format https://qoaformat.org/
- Fixed timing inaccuracy when using loop points on MS ADPCM containing buffers
- Improved panning quality
- Improved JavaDocs
- When using
JukeBoxObserver
, events will now be emitted in proper order
Click to expand!
A small maintenance release with some bugfixes backported to 4.2 from the master branch.
- fixes a crash when calling AudioDevice.disableHrtf()
- fixes AudioDeviceRerouter not being informed about new context attributes when calling AudioDevice.disableHrtf()
Click to expand!
This version of TuningFork requires at least libGDX version 1.12.1!
A small maintenance release.
- Fixed inaccuracy in MS ADPCM duration resolution
- Fixed
PcmSoundSource.queuedBuffers()
possibly returning outdated results - Added float versions of the queue samples methods in
PcmSoundSource
Click to expand!
This version adds some user requested features.
If you're loading audio files directly via a loader (e.g. not using the AssetManager), it used to work like this:
SoundBuffer sound = WaveLoader.load(Gdx.files.internal("sound.wav"));
SoundBuffer sound = FlacLoader.load(Gdx.files.internal("sound.flac"));
SoundBuffer sound = OggLoader.load(Gdx.files.internal("sound.ogg"));
SoundBuffer sound = Mp3Loader.load(Gdx.files.internal("sound.mp3"));
SoundBuffer sound = AiffLoader.load(Gdx.files.internal("sound.aiff"));
All these different loaders will remain the same. However, there is now a new loader that can handle all formats and forwards them to the respective loader implementations:
SoundBuffer sound = SoundLoader.load(Gdx.files.internal("sound.wav"));
SoundBuffer sound = SoundLoader.load(Gdx.files.internal("sound.flac"));
// etc.
So, you can make your life easier and forget about all the different loaders and just use the new universal SoundLoader
. The specific loader implementations are still not redundant since many of them contain special functions to enable loading from other sources or with different decoders that are not accessible through the universal SoundLoader.
I have added support for reversed playback of SoundBuffer
s.
Here's a runnable example that demonstrates sync and async "reverse loading".
If you're using the AssetManager, there's a problem in its design when loading the same file twice but in different modes (normally and in reverse mode). In order to make that work, see this example.
Previously, you had to create a new effect, attach it and dispose the old effect, if you wanted to change an effect slightly. With this change, it is now possible to keep the old effect and alter it at runtime, even when sources are actively using the effect. That's great for reverberation effects in general (psssht secret knowledge, check out SoundEffect.setEnvironmental()
). But there's another use-case: effective SoundEffect
pooling and re-usage.
- Added function
loadReverse(FileHandle)
inWaveLoader
,OggLoader
,Mp3Loader
,FlacLoader
,AiffLoader
- Added function
Mp3Loader.load(InputStream)
- Added method
SoundEffect.updateEffect(SoundEffectData)
- Added method
SoundEffect.isAttached()
- Added method
SoundEffect.getAttachedSources(Array<SoundSource>)
- Added attribute
SoundBufferLoaderParameter.reverse
- Added attribute
SoundBufferLoaderParameter.file
- Added the
SoundLoader
class - Changed visibility of
BufferedSoundSource.getBuffer()
to public - If the file extension (like .ogg) is missing, TuningFork now attempts to identify a file by its header
- Changed
SoundEffectData
class visibility to public (caused issues before when using Kotlin) - Increased the limits for setPitch(pitch) on any SoundSource to 0.0 - unlimited (up from 0.5 - 2.0)
Click to expand!
This version of TuningFork requires at least libGDX version 1.12.0!
There is now support for loop points. You can specify a range by supplying the start and end time of the loop. When the end
is reached, the source will start over at start
.
StreamedSoundSource source = ...
source.setLoopPoints(1f, 10f); // loop starts at 1 sec and ends at 10 sec
source.setLooping(true);
source.play();
// and for sound buffers it's almost the same
SoundBuffer sound = ...
sound.setLoopPoints(1f, 10f);
BufferedSoundSource source = audio.obtainSource(sound);
source.setLooping(true);
source.play();
I replaced the ogg and IMA ADPCM decoder with native ones, which means that loading is much faster now:
- ogg about 40% - 60% faster
- IMA ADPCM about 30% faster
TuningFork can now load and play aiff files. The file format is more common in the Apple world, but basically the counterpart to a wav. Additionally, both the wav decoder and the aiff decoder do support u-law and a-law encoded files now. New but limited to wav is the MS-ADPCM codec.
See Supported-audio-formats-and-codecs for a detailed list.
This version introduces a new concept: Device Rerouters.
In previous versions, you had to either restart the app or manually shut down and reinitialize TuningFork, including reloading all assets, if you lost the connection to the audio device and wanted to resume playback on a new device. Thanks to a new OpenAL extension, you can now seamlessly and effortlessly switch to another device. Rerouters do the job automatically and run in a background thread. You're free to provide your own implementation or use one of the two rerouters included:
- KeepAliveDeviceRerouter
- SmartDeviceRerouter
The first one just makes sure that you are always connected to a device.
The latter tries to be a bit smarter as the name suggests. It checks in a configurable interval whether the connection to the audio device still exists and if it is the preferred connection. If not, it tries to establish a connection with the following prioritization:
- desired device (chosen by you)
- current default device
It is also able to restore a previously lost connection to the desired device when it becomes available again. When not connected to the desired device, the router will establish a connection to the default device and also keep track of it, so when the user selects a new default device in the OS, the router will do the same accordingly.
According to semantic versioning, breaking changes should be displayed with an increase of the major version. Since the major version had to be increased this time anyway due to the change to a new libgdx version, I take this as an opportunity to also do some minor changes to the api of TuningFork, which have accumulated for a few versions.
As a result of the above API changes, we can get rid of the Filter object completely. The functionality is the same, but you only need to provide two floats instead of managing the lifetime of a Filter object.
Instead of two options (on / off) for channel virtualization, there's a third option now: OFF_REMIX_CHANNELS. Input channels are routed to output channels as-is (not virtualized) but instead of dropping non-matching input channels, they're mixed into the closest output channel. Applies only when playing non-mono audio.
An example: You're playing a 4-channel sound file on a stereo system. With this setting, channel 1 and 3 are mapped to the left speaker, channel 2 and 4 to the right speaker.
This property was set to AUTO by default in earlier versions and wasn't configurable. The new default is ON, which could be a breaking change for you, but shouldn't be. Now you can configure it in AudioConfig
or at runtime in Audio
, or on a per-source-basis on any SoundSource
. Here's what the different modes do:
- ON - spatialization is always available
- AUTO - spatialization is available for mono sounds but not for stereo or multi-channel sounds
- OFF - nothing's spatialized
I recommend setting this to AUTO if you care about your software, or OFF if you don't need spatialization at all. It'll remind you to only use mono sources for spatialized sounds, which saves memory, bandwith and cpu usage as multi-channel sounds would get down-mixed to mono anyway. Though, I decided to set ON as the default as that is what people expect. It should just work out of the box.
- New filter API
-
AudioDeviceConfig
setters/getters instead of public fields - Moved static method
availableDevices()
fromAudio
toAudioDevice
- Removed all methods marked @Deprecated
- The
WavDecoder
interface requires one additional method to be implemented - Replaced
Audio.isVirtualizationEnabled()
byAudio.getDefaultVirtualization()
- Replaced
SoundSource.enableVirtualization(boolean)
bySoundSource.setVirtualization(Virtualization)
- Replaced
SoundSource.isVirtualized()
bySoundSource.getVirtualization()
- All places in
AudioConfig
that required a boolean for the virtualization setting expect an enum value fromVirtualization
now - The spatialization mode changed from AUTO to ON
-
OggLoader
is much faster now - Added support for aiff files
- Added support for MS-ADPCM encoded wav files
- Added support for u-law and a-law encoded wav and aiff files
- Added
AudioDevice.switchToDevice()
- Added auto switching devices when the connection gets lost
- Added
SmartDeviceRerouter
as an alternative to the defaultKeepAliveDeviceRerouter
- Added
AudioConfig.setVirtualization(Virtualization)
- Added
Audio.setDefaultVirtualization(Virtualization)
- Added
AudioConfig.setSpatilization(Spatialization)
- Added
Audio.setDefaultSpatialization(Spatialization)
- Added constructor
AudioConfig(AudioDeviceConfig)
- Added
WaveLoader.loadFastImaAdpcm(String)
- Added
SoundBuffer.setLoopPoints(float start, float end)
- Added
StreamedSoundSource.setLoopPoints(float start, float end)
- Added methods to access the audio device clock and latency in
AudioDevice
- Added
SoundSource.setRadius(float)
Click to expand!
This version brings some minor improvements and fixes to the music player as well as let's you attach as many effects to a sound source as you want and your audio device allows. Also, I added a few presets for sound effects.
Mp3 is now fully supported by TuningFork, including duration resolution. This doesn't mean I've magically fixed the problems inherent in the file format itself, so don't expect things like perfect looping, sample-perfect duration resolution and so on. If you can avoid mp3, I recommend you do so.
-
AudioDeviceConfig
got a new property (effectSlots
) that allows you to attach more than 2 effects to a sound source. -
ThemePlayListProvider
was missing some getters/setters previously. - Added method
softStopAndResume
toJukeBox
. This will allow you to fade out of the currentPlayList
and start a new one right after in basically just one line of code. - Added methods
isRelative
,getVolume
,getPitch
,hasFilter
toSoundSource
- Added new presets to all sound effect types
-
SoundBufferLoader
accepts mp3 -
StreamedSoundSource
accepts mp3 -
Mp3InputStream
supports actual duration resolution
Click to expand!
3.2.0 adds a music player that supports playlists, fading and functions to switch playlists/songs smoothly.
The code is extensively tested, but due to the complexity of the test cases it is also easy to miss something. So please report bugs if you stumble across one, I will try to fix it as soon as possible.
- Rare concurrency issue fixed in
StreamedSoundSource
- Duration measurement issue fixed in
StreamedSoundSource
- Added a music player system
Click to expand!
All "fire & forget" methods in the Audio
class were set to @Deprecated. New methods with the same signature (minus the SoundBuffer
parameter) have been created in the SoundBuffer
class, this way you no longer need to pass the Audio
instance around.
// Instead of
audio.play(sound);
// it's now
sound.play();
Much better, isn't it?
But no worries, the old methods will remain in the code and will work until the next major version release.
A second big point is the update of FLAC-library-Java, a custom fork (that I maintain) of nayukis flac library. With the updated dependency, it is no longer necessary to put flac files outside of the jar.
It was on the TODO for a long time and I finally found the time to clean some things up regarding sound streaming extensibility. There's an interface called AudioStream
that you can implement to feed a StreamedSoundSource
. This way, you're able to implement custom decoders or just hook up some audio data from other sources like over the network. The same was possible before via the detour of having to use a PcmSoundSource
, which is a lot more tedious due to its low-level nature from a technical point of view.
I know a few of you have been dying to play mp3. Now it is possible, with minimal glue code. Mp3 still has and will continue to have the status "not officially supported, use at your own risk".
Unfortunately, OpenAL does not provide a nice way to change the playback speed without also changing the pitch of the sound. But there's a little workaround we can use:
- Set the pitch on a
SoundSource
- Create a
PitchShifter
effect that corrects the pitch back to normal and attach it to the source - Mute the direct sound path of the source with a
Filter
A little cloggy but not so hard to do. Though step 2 required you to do the conversion from the pitch float value to a 2-grade semitone/cent system manually. Now it's built-in, so just specify the pitch you want to correct and TuningFork does the conversion for you.
-
SoundBuffer
provides "fire & forget"-methods, no more need to passAudio
around -
StreamedSoundSource
can be fed by anyAudioStream
- Added
Mp3InputStream
andMp3Loader
- FLAC-library-Java updated
- The
PitchShifter
provides a method to correct a specific pitch
Click to expand!
This version further extends the support of file formats. The following formats are now supported in addition to those already covered by TuningFork:
- quad channel surround sound
- 5.1 surround sound
- 6.1 surround sound
- 7.1 surround sound
- 32/64 bit float wav files (mono/stereo only)
- 24/32 bit signed int wav files
- IMA ADPCM encoded wav files (compressed format 4:1 ratio, only allows mono/stereo)
Note that TuningFork downsamples 24 and 32 bit integer pcm wav files to 16 bit due to a lack of support from OpenAL to play these formats. However, in most cases, it's not necessary to ship such high bit depth audio files with your game/app anyway. But convenient to have the support in development as you can just throw unfinalized hq assets at TuningFork and it won't complain.
You'll also be able to set a default resampler globally or on a per-source basis. Resampling is necessary when the sampling rate of a sound file doesn't match the sampling rate of the hardware that it is played on (often the case). The ability to choose the resampling technique allows you to tweak the performance vs. quality slider a bit.
-
PcmFormat.getBySampleDepthAndChannels
has been renamed toPcmFormat.determineFormat
and requires an additional parameter - Minimum supported version of libGDX is now 1.9.12
- 32/64 bit float mono/stereo wav file support
- Support for quad, 5.1, 6.1, 7.1 surround sound
- New API for setting the resampling technique to be used
- The quality of panning has been improved
-
PCMSoundSource
provides the number of queued buffers -
PCMSoundSource
exposes a method to unqueue processed buffers manually - Extended
AudioConfig
to hook up custom wav decoders
Click to expand!
There was a problem with the playback of 8-bit FLAC files, which has been fixed in this release.
Click to expand!
I'm happy to announce that TuningFork now supports 8 and 16-Bit flac audio files. FLAC is a great format as it's open source, royalty-free and compresses lossless.
Support for 8-Bit wav files has been added as well, which allows for faster loading times and a smaller memory footprint. To make this possible I had to introduce a minor breaking change. Don't worry, most people won't be affected by it and can simply upgrade to this version.
So what else do we got?
OpenAL automatically routes input channels to output channels, which may include down-mixing multiple input channels to one output channel or splitting up one input channel to multiple output channels. This happens for example if you got a mono sound file but two speakers (stereo). Or a stereo sound file but a 7.1 sound system. This works great and I recommend to not disable this behavior. However, you can now do so on a per-source-basis or for all sources. The method is called enableVirtualization(true/false)
, available on any SoundSource. Or if you want to set it globally, have a look at AudioConfig#setVirtualizationEnabled(true/false)
. Virtualization is enabled by default.
Anything else?
Yep. Previously the app intentionally crashed when the maximum capacity of sound sources was reached (for example: you're calling Audio.play(sound)
when all sources are busy). Now, TuningFork creates new sound sources on the fly, preventing the crash. Though you should still set a reasonable initial capacity in AudioConfig#setSimultaneousSources(number)
for maximum performance.
-
SoundBuffer
changed
The constructor requires an additional parameter: sampleDepth. -
PcmSoundSource
attenuation fixed
The default attenuation settings weren't applied to this source. Now they are.
- 8 and 16-Bit flac support
- additional 8-Bit wav support
- Enable/Disable channel virtualization on any
SoundSource
and globally - Creation of
SoundSources
on the fly
Click to expand!
This version adds features for sound recording. For games, this seems to be primarily relevant for ingame voice. Also in combination with the PcmSoundSource from the 1.0.0 release, cool things are now possible, such as putting sound effects on your own voice in real time.
-
CaptureDevice
added
This class allows you to record audio. Check out the related wiki entry: Recording Audio
Click to expand!
In the last few days I have prepared everything for a 1.0.0 release. At this point the API should be stable enough to call it like that.
-
StreamedSoundSource
Previously you instantiated a StreamedSoundSource by a call tocreateStreamedSoundSource(FileHandle)
in theAudio
class. This method was removed.
You can create an instance via the constructor now.
StreamedSoundSource source = new StreamedSoundSource(myFileHandle);
- Play method on all sound sources
Previously, callingplay()
on an already playing BufferedSoundSource also rewinded it, while calling the same on a StreamedSoundSource did nothing. This was never intended and only due to the native behavior of OpenAL Soft, which I haven't noticed until now. The new behavior of TuningFork is the latter, it does nothing on all sources.
-
PcmSoundSource
added
This sound source is fed by raw PCM data to provide low level control over sound.
A few possible applications: creation of live sounds/effects, playback of sounds from other sources (streams over network, microphone inputs, etc.) -
SoundBuffer
constructor visibility changed to public