Skip to content

Patch Notes

Hangman edited this page Jul 28, 2024 · 103 revisions

4.4.0 (not released yet)

Click to expand!

This version is still in development.

JukeBox master volume

The JukeBox was missing a global volume setting that has been added now.

AssetManager auto-registration

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.

Rendering waveforms

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.

Non-breaking changes

  • 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() and JukeBox.getVolume()

4.3.0

A new audio format and a new OpenAL extension made it into this release.

QOA - Quite Ok Audio Format

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.

Delayed Playback

Since the OpenAL extension AL_SOFT_source_start_delay made it into libGDXs lwjgl version, we can finally support it.
This allows SoundBuffers and BufferedSoundSources 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.

Non-breaking changes

  • 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

4.2.1

Click to expand!

A small maintenance release with some bugfixes backported to 4.2 from the master branch.

Non-breaking changes

  • fixes a crash when calling AudioDevice.disableHrtf()
  • fixes AudioDeviceRerouter not being informed about new context attributes when calling AudioDevice.disableHrtf()

4.2.0

Click to expand!

This version of TuningFork requires at least libGDX version 1.12.1!
A small maintenance release.

Non-breaking changes

  • Fixed inaccuracy in MS ADPCM duration resolution
  • Fixed PcmSoundSource.queuedBuffers() possibly returning outdated results
  • Added float versions of the queue samples methods in PcmSoundSource

4.1.0

Click to expand!

This version adds some user requested features.

Convenient Loader

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.

Reversed playback

I have added support for reversed playback of SoundBuffers. 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.

Change sound effects at runtime

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.

Non-breaking changes

  • Added function loadReverse(FileHandle) in WaveLoader, 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)

4.0.0

Click to expand!

This version of TuningFork requires at least libGDX version 1.12.0!

Loop points

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();

Loading speeeeeeeeeeed

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

New audio file formats

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.

Audio Device Rerouters

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:

  1. desired device (chosen by you)
  2. 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.

API changes

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.

Filters

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.

Virtualization

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.

Spatialization

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 AudioConfigor 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.

Breaking changes

  • New filter API
  • AudioDeviceConfig setters/getters instead of public fields
  • Moved static method availableDevices() from Audio to AudioDevice
  • Removed all methods marked @Deprecated
  • The WavDecoder interface requires one additional method to be implemented
  • Replaced Audio.isVirtualizationEnabled() by Audio.getDefaultVirtualization()
  • Replaced SoundSource.enableVirtualization(boolean) by SoundSource.setVirtualization(Virtualization)
  • Replaced SoundSource.isVirtualized() by SoundSource.getVirtualization()
  • All places in AudioConfig that required a boolean for the virtualization setting expect an enum value from Virtualization now
  • The spatialization mode changed from AUTO to ON

Non-breaking changes

  • 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 default KeepAliveDeviceRerouter
  • 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)

3.3.0

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.

Non-breaking changes

  • 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 to JukeBox. This will allow you to fade out of the current PlayList and start a new one right after in basically just one line of code.
  • Added methods isRelative, getVolume, getPitch, hasFilter to SoundSource
  • Added new presets to all sound effect types
  • SoundBufferLoader accepts mp3
  • StreamedSoundSource accepts mp3
  • Mp3InputStream supports actual duration resolution

3.2.0

Click to expand!

Music Player

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.

Non-breaking changes

  • Rare concurrency issue fixed in StreamedSoundSource
  • Duration measurement issue fixed in StreamedSoundSource
  • Added a music player system

3.1.0

Click to expand!

New fire & forget API

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.

Flac files in jars

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.

Custom decoders and mp3

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".

Change playback speed

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:

  1. Set the pitch on a SoundSource
  2. Create a PitchShifter effect that corrects the pitch back to normal and attach it to the source
  3. 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.


Non-breaking changes

  • SoundBuffer provides "fire & forget"-methods, no more need to pass Audio around
  • StreamedSoundSource can be fed by any AudioStream
  • Added Mp3InputStream and Mp3Loader
  • FLAC-library-Java updated
  • The PitchShifter provides a method to correct a specific pitch

3.0.0

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.


Breaking changes

  • PcmFormat.getBySampleDepthAndChannels has been renamed to PcmFormat.determineFormat and requires an additional parameter
  • Minimum supported version of libGDX is now 1.9.12

Non-breaking changes

  • 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

2.0.1

Click to expand!

There was a problem with the playback of 8-bit FLAC files, which has been fixed in this release.

2.0.0

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.


Breaking changes

  • 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.

Non-breaking changes

  • 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

1.1.0

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.


Non-breaking changes

  • CaptureDevice added
    This class allows you to record audio. Check out the related wiki entry: Recording Audio

New examples

1.0.0

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.


Breaking changes

  • StreamedSoundSource
    Previously you instantiated a StreamedSoundSource by a call to createStreamedSoundSource(FileHandle) in the Audio 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, calling play() 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.

Non-breaking changes

  • 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

New examples

PcmSoundSource Test

Clone this wiki locally