mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-15 03:35:07 -05:00
Add Sound API to allow sound instances to play custom audio streams (#2558)
* Allow sound instances to play custom audio streams Adds a new interface FabricSoundInstance, which is injected into vanilla's SoundInstance interface. When loading an audio stream, the SoundSystem now calls FabricSoundInstance.getAudioStream, allowing mods to provide their own audio streams. * Some post-review cleanup - Manually add the client sources as an interface injection source set, allowing us to put everything in the src/client dir (<3 modmuss50). - Apply some formatting changes from apple502j. * Document the empty sound and its usage in sounds.json * Fix one remaining @literal -> @code * Fix checkstyle issues
This commit is contained in:
parent
704e47e9d7
commit
c4f28df547
15 changed files with 389 additions and 0 deletions
14
fabric-sound-api-v1/build.gradle
Normal file
14
fabric-sound-api-v1/build.gradle
Normal file
|
@ -0,0 +1,14 @@
|
|||
archivesBaseName = "fabric-sound-api-v1"
|
||||
version = getSubprojectVersion(project)
|
||||
|
||||
loom {
|
||||
interfaceInjection {
|
||||
interfaceInjectionSourceSets.add sourceSets.client
|
||||
}
|
||||
}
|
||||
|
||||
testDependencies(project, [
|
||||
':fabric-api-base',
|
||||
':fabric-resource-loader-v0',
|
||||
':fabric-command-api-v2'
|
||||
])
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.client.sound.v1;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import net.minecraft.client.sound.AudioStream;
|
||||
import net.minecraft.client.sound.SoundInstance;
|
||||
import net.minecraft.client.sound.SoundLoader;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* General purpose Fabric-provided extensions to {@link SoundInstance}.
|
||||
*
|
||||
* <p>This interface is implicitly implemented on all {@link SoundInstance}s via a mixin and interface injection.
|
||||
*/
|
||||
public interface FabricSoundInstance {
|
||||
/**
|
||||
* An empty sound, which may be used as a placeholder in your {@code sounds.json} file for sounds with custom audio
|
||||
* streams.
|
||||
*
|
||||
* @see #getAudioStream(SoundLoader, Identifier, boolean)
|
||||
*/
|
||||
Identifier EMPTY_SOUND = new Identifier("fabric-sound-api-v1", "empty");
|
||||
|
||||
/**
|
||||
* Loads the audio stream for this sound.
|
||||
*
|
||||
* <p>By default this will load {@code .ogg} files from active resource packs. It may be overridden to provide a
|
||||
* custom {@link AudioStream} implementation which provides audio from another source, such as over the network or
|
||||
* driven from user input.
|
||||
*
|
||||
* <h3>Usage Example</h3>
|
||||
*
|
||||
* <p>Creating a sound with a custom audio stream requires the following:
|
||||
*
|
||||
* <p>Firstly, an entry in {@code sounds.json}. The name can be set to any sound (though it is recommended to use
|
||||
* the dummy {@link #EMPTY_SOUND}), and the "stream" property set to true:
|
||||
*
|
||||
* <pre>{@code
|
||||
* {
|
||||
* "custom_sound": {"sounds": [{"name": "fabric-sound-api-v1:empty", "stream": true}]}
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>You should then define your own implementation of {@link AudioStream}, which provides audio data to the sound
|
||||
* engine.
|
||||
*
|
||||
* <p>Finally, you'll need an implementation of {@link SoundInstance} which overrides {@link #getAudioStream} to
|
||||
* return your custom implementation. {@link SoundInstance#getSound()} should return the newly-added entry in
|
||||
* {@code sounds.json}.
|
||||
*
|
||||
* <pre>{@code
|
||||
* class CustomSound extends AbstractSoundInstance {
|
||||
* CustomSound() {
|
||||
* // Use the sound defined in sounds.json
|
||||
* super(new Identifier("mod_id", "custom_sound"), SoundCategory.BLOCKS, SoundInstance.createRandom());
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public CompletableFuture<AudioStream> getAudioStream(SoundLoader loader, Identifier id, boolean repeatInstantly) {
|
||||
* // Return your custom AudioStream implementation.
|
||||
* return CompletableFuture.completedFuture(new CustomStream());
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param loader The default sound loader, capable of loading {@code .ogg} files.
|
||||
* @param id The resolved sound ID, equal to {@link SoundInstance#getSound()}'s location.
|
||||
* @param repeatInstantly Whether this sound should loop. This is true when the sound
|
||||
* {@linkplain SoundInstance#isRepeatable() is repeatable} and has
|
||||
* {@linkplain SoundInstance#getRepeatDelay() no delay}.
|
||||
* @return the loaded audio stream
|
||||
*/
|
||||
default CompletableFuture<AudioStream> getAudioStream(SoundLoader loader, Identifier id, boolean repeatInstantly) {
|
||||
return loader.loadStreamed(id, repeatInstantly);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.client.sound;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import net.minecraft.client.sound.SoundInstance;
|
||||
|
||||
import net.fabricmc.fabric.api.client.sound.v1.FabricSoundInstance;
|
||||
|
||||
@Mixin(SoundInstance.class)
|
||||
public interface SoundInstanceMixin extends FabricSoundInstance {
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.client.sound;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import net.minecraft.client.sound.SoundInstance;
|
||||
import net.minecraft.client.sound.SoundLoader;
|
||||
import net.minecraft.client.sound.SoundSystem;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
@Mixin(SoundSystem.class)
|
||||
public class SoundSystemMixin {
|
||||
@Redirect(
|
||||
method = "play(Lnet/minecraft/client/sound/SoundInstance;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/sound/SoundLoader;loadStreamed(Lnet/minecraft/util/Identifier;Z)Ljava/util/concurrent/CompletableFuture;"
|
||||
)
|
||||
)
|
||||
private CompletableFuture<?> getStream(SoundLoader loader, Identifier id, boolean looping, SoundInstance sound) {
|
||||
return sound.getAudioStream(loader, id, looping);
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.mixin.client.sound",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"client": [
|
||||
"SoundInstanceMixin",
|
||||
"SoundSystemMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
35
fabric-sound-api-v1/src/client/resources/fabric.mod.json
Normal file
35
fabric-sound-api-v1/src/client/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-sound-api-v1",
|
||||
"name": "Fabric Sound API (v1)",
|
||||
"version": "${version}",
|
||||
"environment": "client",
|
||||
"license": "Apache-2.0",
|
||||
"icon": "assets/fabric-sound-api-v1/icon.png",
|
||||
"contact": {
|
||||
"homepage": "https://fabricmc.net",
|
||||
"irc": "irc://irc.esper.net:6667/fabric",
|
||||
"issues": "https://github.com/FabricMC/fabric/issues",
|
||||
"sources": "https://github.com/FabricMC/fabric"
|
||||
},
|
||||
"authors": [
|
||||
"FabricMC"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.14.9",
|
||||
"minecraft": ">=1.19.2"
|
||||
},
|
||||
"description": "Hooks for modifying Minecraft's sound system.",
|
||||
"mixins": [
|
||||
{
|
||||
"config": "fabric-sound-api-v1.mixins.json",
|
||||
"environment": "client"
|
||||
}
|
||||
],
|
||||
"custom": {
|
||||
"fabric-api:module-lifecycle": "stable",
|
||||
"loom:injected_interfaces": {
|
||||
"net/minecraft/class_1113": ["net/fabricmc/fabric/api/client/sound/v1/FabricSoundInstance"]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.sound.client;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
|
||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||
|
||||
/**
|
||||
* Plays a sine wave when the {@code /sine} client command is run.
|
||||
*/
|
||||
public class ClientSoundTest implements ClientModInitializer {
|
||||
public static final String MOD_ID = "fabric-sound-api-v1-testmod";
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientCommandRegistrationCallback.EVENT.register((dispatcher, access) -> {
|
||||
dispatcher.register(ClientCommandManager.literal("sine").executes(o -> {
|
||||
MinecraftClient client = o.getSource().getClient();
|
||||
client.getSoundManager().play(new SineSound(client.player.getPos()));
|
||||
return 0;
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.sound.client;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import net.minecraft.client.sound.AbstractSoundInstance;
|
||||
import net.minecraft.client.sound.AudioStream;
|
||||
import net.minecraft.client.sound.SoundInstance;
|
||||
import net.minecraft.client.sound.SoundLoader;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
class SineSound extends AbstractSoundInstance {
|
||||
SineSound(Vec3d pos) {
|
||||
super(new Identifier(ClientSoundTest.MOD_ID, "sine_wave"), SoundCategory.BLOCKS, SoundInstance.createRandom());
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
z = pos.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AudioStream> getAudioStream(SoundLoader loader, Identifier id, boolean repeatInstantly) {
|
||||
return CompletableFuture.completedFuture(new SineStream());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.sound.client;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
import net.minecraft.client.sound.AudioStream;
|
||||
|
||||
/**
|
||||
* An audio stream which plays a sine wave.
|
||||
*/
|
||||
class SineStream implements AudioStream {
|
||||
private static final AudioFormat FORMAT = new AudioFormat(44100, 8, 1, false, false);
|
||||
private static final double DT = 2 * Math.PI * 220 / 44100;
|
||||
|
||||
private static double value = 0;
|
||||
|
||||
@Override
|
||||
public AudioFormat getFormat() {
|
||||
return FORMAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getBuffer(int capacity) {
|
||||
ByteBuffer buffer = BufferUtils.createByteBuffer(capacity);
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
buffer.put(i, (byte) (Math.sin(value) * 127));
|
||||
value = (value + DT) % Math.PI;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"sine_wave": {
|
||||
"sounds": [
|
||||
{
|
||||
"name": "fabric-sound-api-v1:empty",
|
||||
"stream": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
17
fabric-sound-api-v1/src/testmod/resources/fabric.mod.json
Normal file
17
fabric-sound-api-v1/src/testmod/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-sound-api-v1-testmod",
|
||||
"name": "Fabric Sound API (v1) Test Mod",
|
||||
"version": "1.0.0",
|
||||
"environment": "client",
|
||||
"license": "Apache-2.0",
|
||||
"depends": {
|
||||
"fabric-sound-api-v1": "*",
|
||||
"fabric-command-api-v2": "*"
|
||||
},
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
"net.fabricmc.fabric.test.sound.client.ClientSoundTest"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@ fabric-resource-conditions-api-v1-version=2.0.12
|
|||
fabric-resource-loader-v0-version=0.7.0
|
||||
fabric-screen-api-v1-version=1.0.27
|
||||
fabric-screen-handler-api-v1-version=1.3.1
|
||||
fabric-sound-api-v1-version=1.0.0
|
||||
fabric-textures-v0-version=1.0.21
|
||||
fabric-transfer-api-v1-version=2.1.1
|
||||
fabric-transitive-access-wideners-v1-version=1.3.1
|
||||
|
|
|
@ -47,6 +47,7 @@ include 'fabric-resource-conditions-api-v1'
|
|||
include 'fabric-resource-loader-v0'
|
||||
include 'fabric-screen-api-v1'
|
||||
include 'fabric-screen-handler-api-v1'
|
||||
include 'fabric-sound-api-v1'
|
||||
include 'fabric-textures-v0'
|
||||
include 'fabric-transfer-api-v1'
|
||||
include 'fabric-convention-tags-v1'
|
||||
|
|
Loading…
Reference in a new issue