fabric icon indicating copy to clipboard operation
fabric copied to clipboard

Allow sound instances to play custom audio streams

Open SquidDev opened this issue 1 year ago • 11 comments

There's a couple of mods out there which play dynamically generated sounds, rather than reading from an ogg file. For instance, Etched allows streaming audio from an arbitrary URL and CC: Restitched allows players to generate music data and stream it to the client.

Minecraft's sound engine supports custom audio streams. However, as there is no API exposed for this, mods must use mixins. Unfortunately the sound code is fairly complex and, due to its multi-threaded and callback-based nature, awkward to mixin to. For instance (note, most of these examples are using MojMap):

Given several mods want to provide custom AudioStreams, but doing so in a compatible way is tricky to get right, it makes sense for Fabric to provide an API everyone can hook into.

This PR adds a interface FabricSoundInstance, injected into SoundInstance. This provides a new method getAudioStream, which can be overridden to return a custom audio stream. The default implementation implements the current behaviour of just reading an .ogg file from resource packs.

Further notes

  • The obvious alternative here would be to add an event, instead of providing a new interface. The current approach felt more natural, as whenever you want this functionality, you're going to be sub-classing SoundInstance anyway. Happy to go for either.

  • This does still require SoundInstance.getSound to resolve to an actual file, even if that file isn't read by the audio stream. This is definitely a little clunky; I'm just not sure if "fixing" this is worth the additional implementation cost. Thoughts welcome!

  • I've added a new module fabric-sound-api-v1 as nowhere else felt natural. Obviously let me know if I've got anything wrong with adding the new module :).

  • It appears that injected interfaces only work when registered in the main source set. I'm not sure if this is intentional or not?

SquidDev avatar Sep 29 '22 12:09 SquidDev

It appears that injected interfaces only work when registered in the main source set.

I dont think thats intentional, most likely something I can fix in loom. Ideally client only modules should have everything in the client sourceset as this will prevent the need to split the jar in a mod dev env. I will take a look into this.

Had a quick look at the PR, it looks great at first glance. I will take a deeper look soon. 👍

modmuss50 avatar Sep 29 '22 12:09 modmuss50

This should be done by default, something I can fix in a future loom version, for now you should be able to add the following to the sub projects build.gradle:

loom {
	interfaceInjection {
		interfaceInjectionSourceSets.add sourceSets.client
	}
}

modmuss50 avatar Sep 29 '22 13:09 modmuss50

Also why are you shipping the empty ogg farm as part of the API (and not testmod)?

As every mod that provides a custom audio stream needs to reference a dummy sound it made sense to provide it in the API, rather than every mod having to bundle their own - much like fabric-gametest-api-v1's empty structure.

SquidDev avatar Sep 29 '22 15:09 SquidDev

@SquidDev Then the existence should be documented somewhere, along with Identifier of the empty sound exposed as a field.

apple502j avatar Sep 29 '22 16:09 apple502j

Then the existence should be documented somewhere, along with Identifier of the empty sound exposed as a field.

:+1: Is it acceptable just to add it as a constant to FabricSoundInstance? Not sure there's a more sensible location until #2503 is resolved.

SquidDev avatar Sep 29 '22 16:09 SquidDev

Is it acceptable just to add it as a constant to FabricSoundInstance?

Yes, I think that would be a good idea 👍

modmuss50 avatar Oct 08 '22 12:10 modmuss50

I felt it was probably worth while adding an example, as the need for sounds.json is a little awkward and thus worth being explicit in. Happy to revert if it's Too Much :).

SquidDev avatar Oct 10 '22 08:10 SquidDev

Hah, I should know better than to commit without running checkstyle!

I don't know if there's any interest in adding a pre-commit hook which runs it automatically? Or is that just going to get too irritatingly slow for people?

SquidDev avatar Oct 10 '22 08:10 SquidDev

More documentation is always welcome. :)

Not sure about the pre-commit hook as I've never used them myself, but it looks like it might end up being rather annoying ? We always wait for CI to pass before merging anything.

Technici4n avatar Oct 10 '22 09:10 Technici4n

IMO it's too slow since running gradlew checkstyleMain checkstyleTestmod (or even just the main one) builds all modules and that takes a while even with --parallel.

Juuxel avatar Oct 10 '22 09:10 Juuxel

I agree, spotless would also need to be ran making commits very slow. I would reccomend using the checkstyle plugin in your IDE to make it clear when you have an issue.

Added to last call, thanks 👍

modmuss50 avatar Oct 10 '22 12:10 modmuss50

Thank you everyone :).

SquidDev avatar Oct 16 '22 15:10 SquidDev