Repository navigation
Sound effects system Part 1: Assets, Stereo sound #3281
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
Changes from all commits
6d7e9e3
f6596ce
4023a02
ba74fc8
cae0ac4
986af32
d364d90
00fa770
369f237
b082e90
66ea594
59a71a6
a819070
090cdde
40e76aa
a469cf3
d4ad470
4449ef9
7f154fc
d7e745b
7d8ed9b
731974b
70b9356
eb74150
be2f60c
1d8cf49
d2da43a
92fcc3f
f0781a4
943129d
22329c6
5a12f1b
5f209d7
0de8631
8de387a
99ab682
0f6fc28
5c64c2d
368835a
adf1f8b
e108ca9
c902189
79dee6c
e97295b
ee0e6db
20d8d60
241bf5b
99b9fa0
72087ec
d5714c0
d6d4fbd
5c8d86f
092ceb7
7247c70
09083b0
f6dfb8e
35a4ed4
7516242
f5e5c18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| .{ | ||
| .audio = "cubyz:block_break", | ||
| .volume = 1.25, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| .{ | ||
| .audio = "cubyz:block_hit", | ||
| .volume = 0.25, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| .{ | ||
| .audio = "cubyz:jump", | ||
| .volume = 0.35, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| const std = @import("std"); | ||
|
|
||
| const main = @import("main"); | ||
| const vec = main.vec; | ||
| const Vec3f = vec.Vec3f; | ||
| const utils = main.utils; | ||
| const ZonElement = @import("zon.zig").ZonElement; | ||
|
|
||
| const c = @import("c"); | ||
|
|
||
|
|
@@ -241,6 +244,23 @@ pub fn deinit() void { | |
| preferredMusic.len = 0; | ||
| main.globalAllocator.free(activeMusicId); | ||
| activeMusicId.len = 0; | ||
|
|
||
| audioIdMap.deinit(main.globalAllocator.allocator); | ||
| audios.deinit(main.globalAllocator); | ||
| soundDataIdMap.deinit(main.globalAllocator.allocator); | ||
| soundDatas.deinit(main.globalAllocator); | ||
| activeSounds.deinit(main.globalAllocator); | ||
| } | ||
|
|
||
| pub fn reset() void { | ||
| audioIdMap.clearRetainingCapacity(); | ||
| for (audios.items) |s| { | ||
| s.deinit(); | ||
| } | ||
| audios.clearRetainingCapacity(); | ||
| activeSounds.clearRetainingCapacity(); | ||
| soundDataIdMap.clearRetainingCapacity(); | ||
| soundDatas.clearRetainingCapacity(); | ||
| } | ||
|
|
||
| const currentMusic = struct { | ||
|
|
@@ -286,6 +306,79 @@ pub fn setMusic(music: []const u8) void { | |
| preferredMusic = main.globalAllocator.dupe(u8, music); | ||
| } | ||
|
|
||
| const SoundData = struct { // MARK: Sounds | ||
| audioIndex: u32, | ||
| volume: f32 = 1, | ||
| }; | ||
|
|
||
| const PlayingSound = struct { | ||
| pos: Vec3f = @splat(0), | ||
| audioIndex: u32, | ||
| bufPos: u32 = 0, | ||
|
|
||
| volume: f32 = 1, | ||
| maxDistance: f32 = 10, | ||
| isSpatial: bool = false, | ||
| }; | ||
|
|
||
| var audioIdMap: std.StringHashMapUnmanaged(u32) = .{}; | ||
| var audios: main.List(*AudioData) = .empty; | ||
| var soundDataIdMap: std.StringHashMapUnmanaged(u32) = .{}; | ||
| var soundDatas: main.List(SoundData) = .empty; | ||
| var activeSounds: main.List(PlayingSound) = .empty; | ||
|
|
||
| pub fn getActiveSoundCount() u32 { | ||
| return @intCast(activeSounds.items.len); | ||
| } | ||
|
|
||
| pub fn registerAudioData(_: []const u8, id: []const u8) void { | ||
| const audio = AudioData.init(id, "sounds/audio"); | ||
| audioIdMap.put(main.globalAllocator.allocator, id, @intCast(audios.items.len)) catch unreachable; | ||
| audios.append(main.globalAllocator, audio); | ||
| std.log.debug("Registered sound audio: {s}", .{id}); | ||
| } | ||
|
|
||
| pub fn registerSound(assetsFolder: []const u8, id: []const u8, zon: ZonElement) void { | ||
| const audioId = zon.get([]const u8, "audio") orelse { | ||
| std.log.err("Sound Data audio was not specified: {s} ({s})", .{id, assetsFolder}); | ||
| return; | ||
| }; | ||
|
|
||
| soundDataIdMap.put(main.globalAllocator.allocator, id, @intCast(soundDatas.items.len)) catch unreachable; | ||
| soundDatas.append(main.globalAllocator, SoundData{ | ||
| .audioIndex = audioIdMap.get(audioId) orelse { | ||
| std.log.err("Sound Data audio could not be found: {s} ({s}) audio ID: {s}", .{id, assetsFolder, audioId}); | ||
| return; | ||
| }, | ||
| .volume = zon.get(f32, "volume") orelse 1.0, | ||
| }); | ||
|
|
||
| std.log.debug("Registered sound data: {s}", .{id}); | ||
| } | ||
|
|
||
| fn addSound(id: []const u8, pos: Vec3f, maxDistance: f32, isSpatial: bool) void { | ||
|
IntegratedQuantum marked this conversation as resolved.
|
||
| mutex.lock(); | ||
| defer mutex.unlock(); | ||
|
|
||
| const idx = soundDataIdMap.get(id) orelse return; | ||
| const soundData = soundDatas.items[idx]; | ||
| activeSounds.append(main.globalAllocator, PlayingSound{ | ||
|
IntegratedQuantum marked this conversation as resolved.
|
||
| .audioIndex = soundData.audioIndex, | ||
| .volume = soundData.volume, | ||
| .pos = pos, | ||
| .isSpatial = isSpatial, | ||
| .maxDistance = maxDistance, | ||
| }); | ||
| } | ||
|
|
||
| pub fn playSound(id: []const u8) void { | ||
| addSound(id, @splat(0), 0, false); | ||
| } | ||
|
|
||
| pub fn playSpatialSound(id: []const u8, pos: Vec3f, maxDistance: f32) void { | ||
| addSound(id, pos, maxDistance, true); | ||
| } | ||
|
|
||
| fn mixMusic(buffer: []f32) void { | ||
| mutex.lock(); | ||
| defer mutex.unlock(); | ||
|
|
@@ -333,6 +426,76 @@ fn mixMusic(buffer: []f32) void { | |
| } | ||
| } | ||
|
|
||
| fn mixSound(buffer: []f32) void { | ||
| mutex.lock(); | ||
| defer mutex.unlock(); | ||
|
|
||
| if (activeSounds.items.len == 0) return; | ||
|
|
||
| const playerPos: Vec3f = @floatCast(main.game.Player.getPosBlocking()); | ||
| const playerForward = main.game.camera.direction; | ||
| const playerRight = vec.normalize(vec.cross(playerForward, Vec3f{0, 0, 1})); | ||
|
|
||
| var i: u32 = 0; | ||
| var soundCount = activeSounds.items.len; | ||
| main: while (i < soundCount) { | ||
| var sound = activeSounds.items[i]; | ||
| const audioData = audios.items[sound.audioIndex]; | ||
| const soundBuffer = audioData.data; | ||
|
|
||
| const notMonoInt: u32 = @intFromBool(audioData.channelType != .mono); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we forbid stereo tracks entirely? Ideally enforced through the conversion script in Cubyz-Assets.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ikabod-kee do we want stereo sounds? (not the directional spatial stuff in the game but the audio itself being stereo), having no stereo sounds would make things simplier
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setro tracks are just interpereted as mono with the first channel only being read
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't just blindly reinterpret stuff, log an error instead and discard the track.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok throws a error and returns a null now |
||
| const bufferStep: u32 = 1 + notMonoInt; | ||
|
|
||
| var leftVol: f32 = 1; | ||
| var rightVol: f32 = 1; | ||
|
|
||
| if (sound.isSpatial) { | ||
| const toSound = sound.pos - playerPos; | ||
| const distance: f32 = vec.length(toSound); | ||
|
|
||
| if (distance > sound.maxDistance) { | ||
| sound.bufPos += @intCast(if (audioData.channelType == .mono) @divFloor(buffer.len, 2) else buffer.len); | ||
| if (sound.bufPos >= soundBuffer.len) { | ||
| soundCount -= 1; | ||
| activeSounds.items[i] = activeSounds.items[soundCount]; | ||
|
IntegratedQuantum marked this conversation as resolved.
|
||
| continue :main; | ||
| } | ||
| } | ||
|
|
||
| const pan: f32 = vec.dot(toSound/@as(Vec3f, @splat(distance)), playerRight); | ||
|
|
||
| const angle = (pan + 1)*0.25*std.math.pi; | ||
| leftVol = @cos(angle); | ||
| rightVol = @sin(angle); | ||
|
|
||
| var volume: f32 = 1 - distance/sound.maxDistance; | ||
|
|
||
| volume = if (volume < 0) 0 else volume; | ||
| leftVol *= volume; | ||
| rightVol *= volume; | ||
| } | ||
|
|
||
| var j: usize = 0; | ||
| while (j < buffer.len) : (j += 2) { | ||
| const amplitude: f32 = main.settings.soundVolume*sound.volume; | ||
|
|
||
| buffer[j] += soundBuffer[sound.bufPos]*amplitude*leftVol; | ||
| buffer[j + 1] += soundBuffer[sound.bufPos + notMonoInt]*amplitude*rightVol; | ||
| sound.bufPos += bufferStep; | ||
|
|
||
| if (sound.bufPos >= soundBuffer.len) { | ||
| soundCount -= 1; | ||
| activeSounds.items[i] = activeSounds.items[soundCount]; | ||
| continue :main; | ||
| } | ||
| } | ||
| activeSounds.items[i] = sound; | ||
| i += 1; | ||
| } | ||
|
|
||
| activeSounds.items.len = soundCount; | ||
| } | ||
|
|
||
| fn miniaudioCallback( | ||
| maDevice: ?*anyopaque, | ||
| output: ?*anyopaque, | ||
|
|
@@ -345,4 +508,5 @@ fn miniaudioCallback( | |
| const buffer = @as([*]f32, @ptrCast(@alignCast(output)))[0..valuesPerBuffer]; | ||
| @memset(buffer, 0); | ||
| mixMusic(buffer); | ||
| mixSound(buffer); | ||
| } | ||
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.
Sound effects should be in https://github.com/PixelGuys/Cubyz-Assets/ together with the music
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.
PixelGuys/Cubyz-Assets#39
i have a pr for it im just waiting for it to get added before i can add that functionality
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.
Ah, it's waiting for artistic review
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.
ok i think i implemented it
its giving a error but i beleive its because the assets folder is not in yet