Compare commits

...

178 Commits
main ... main

Author SHA1 Message Date
cosmonaut bb7e45b9a3 expose Params on ReverbEffect 2024-02-09 13:53:33 -08:00
cosmonaut 4cedf768f7 fix AttackHoldRelease timing 2024-02-03 00:40:29 -08:00
cosmonaut d986b3013f fix TextBatch index buffers being created as vertex buffers 2024-01-27 17:34:26 -08:00
cosmonaut 42e3ac91af Vertex instance input shortcut (#53)
Reviewed-on: MoonsideGames/MoonWorks#53
2024-01-27 03:44:19 +00:00
cosmonaut 0df2944ccf CommandBuffer is now a pooled class + more state validation 2024-01-18 12:27:34 -08:00
cosmonaut e50fb472b1 Debug mode sample count and depth assertions 2024-01-15 23:16:29 -08:00
cosmonaut df3f38a67b Debug mode bounds checks for buffer and texture upload 2024-01-15 22:19:59 -08:00
Evan Hemsley eaa9266521 remove Marshal call from KeyboardButton.CheckPressed 2023-12-28 18:54:21 -08:00
cosmonaut 4dbd5a2cbe MSDF font rendering + improved resource tracking (#52)
This is a major rewrite of the Font system. MoonWorks now uses MSDF font rendering, which allows high quality rendering of fonts at arbitrary sizes.

We now ship default embedded shader binaries for Video and Font. If you replace them with shader binaries of the same name located in your base directory, those will be used instead.

Many improvements have been made to resource tracking to prevent memory corruption, particularly on shutdown.

You must be careful not to leak AudioResource classes in particular, as there isn't much we can automatically do to recover from this without potentially crashing your game.

Reviewed-on: MoonsideGames/MoonWorks#52
2023-12-15 18:46:43 +00:00
cosmonaut 2e890fd696 Remove x64 specification 2023-12-13 11:19:27 -08:00
cosmonaut 385783a846 restructure audio cleanup 2023-12-08 16:33:52 -08:00
cosmonaut 450b08cbd8 Atomically call graphics resource destroy function 2023-12-08 16:07:38 -08:00
cosmonaut 528fb7ac7c fix potential heap corruption on audio shutdown 2023-12-08 15:06:17 -08:00
cosmonaut fcd08fe231 .NET 8 2023-11-21 15:23:52 -08:00
cosmonaut e961a18a83 UpdatingSourceVoice + warn on audio leak 2023-11-20 18:56:22 -08:00
cosmonaut 772a0378bb avoid calling Thread.Join from finalizer 2023-11-20 17:59:26 -08:00
cosmonaut 40fb313d12 change AudioResource WeakRef to GCHandle 2023-11-20 17:52:44 -08:00
cosmonaut a736ed031d clean up graphics resource code 2023-11-20 17:09:22 -08:00
cosmonaut b2a0ca3515 replace WeakReference with weak GCHandle 2023-11-20 15:18:06 -08:00
cosmonaut 36a88afe52 add no-op delegate to gamepad connect events 2023-11-14 11:19:21 -08:00
cosmonaut 6c93350f7f Add OnGamepadConnected and OnGamepadDisconnected events (#51)
Reviewed-on: MoonsideGames/MoonWorks#51
2023-11-13 19:10:29 +00:00
cosmonaut 352bb34f82 update dll config for major versions 2023-10-18 12:15:30 -07:00
Evan Hemsley de7d76c03d update dll.config for macOS 2023-10-16 11:20:55 -07:00
cosmonaut 18d92aeec8 remove Fix64 dependency on System.Random 2023-10-13 13:38:26 -07:00
cosmonaut e616b0fa62 resource management and logging improvements 2023-10-04 14:45:17 -07:00
cosmonaut 1d27a9e4a4 update README with API docs and Discord 2023-09-26 09:44:32 -07:00
cosmonaut 514a0bed29 update FAudio and SDL 2023-09-25 10:03:40 -07:00
cosmonaut 78252d1f6c doxygen generator config 2023-09-19 17:55:17 -07:00
cosmonaut daae1a34b9 fix a few more compile errors 2023-09-19 17:14:48 -07:00
cosmonaut 2e5657789c fix a Conversions issue 2023-09-19 17:11:14 -07:00
cosmonaut 0c76c568a4 document the Game class 2023-09-19 17:04:28 -07:00
cosmonaut abdcac1608 change AudioDevice constructor to internal 2023-09-19 17:04:03 -07:00
cosmonaut d8064862bf move PackedVector classes to MoonWorks.Graphics.PackedVector 2023-09-19 16:50:08 -07:00
cosmonaut b223c31c8b even more frame limiter clarification 2023-09-19 13:48:50 -07:00
cosmonaut dd79090028 update frame limiter docs some more 2023-09-19 13:47:19 -07:00
cosmonaut 653f90c29f correct frame limiter documentation mistake 2023-09-19 13:46:41 -07:00
cosmonaut 402c26131d fix erroneous GetData length warning 2023-09-19 13:40:48 -07:00
cosmonaut b026b9e81f add lots more doc comments 2023-09-19 13:19:41 -07:00
cosmonaut e0f05881b0 new MoonWorks.Graphics.Fence API 2023-09-18 23:18:21 -07:00
cosmonaut 7e18764942 add doc comments for the Input namespace 2023-09-14 11:23:04 -07:00
cosmonaut 1bff459be6 move default reverb params to a static var 2023-09-12 15:24:45 -07:00
cosmonaut be77e8bad1 add exponentiation functions to Fix64 2023-09-07 17:30:35 -07:00
cosmonaut 7f6b6a7bae fix voices not respecting the faux mastering voice 2023-08-10 10:46:19 -07:00
cosmonaut 1de3c73bb7 fix voices being created in the Playing state 2023-08-09 16:10:00 -07:00
cosmonaut e77c87c772 register new SourceVoices as active 2023-08-09 15:58:18 -07:00
cosmonaut 088e7c4b6f add debug check for zero length buffer copy 2023-08-07 10:12:46 -07:00
cosmonaut f298a5ec11 fix StreamingVoice loop behavior 2023-08-04 12:14:14 -07:00
cosmonaut 0cd2c799ee Audio Restructuring (#50)
This is a complete redesign of the MoonWorks Audio API.

Voices are the new major concept. All Voices can be configured with volume, pitch, filters, panning and reverb. SourceVoices take in AudioBuffers and use them to play sound. They contain their own playback state.

There are multiple kinds of SourceVoices:
TransientVoice: Used for short sound effects where the client will not be keeping track of a reference over multiple frames.
PersistentVoice: Used when the client needs to hold on to a Voice reference long-term.
StreamingVoice: Used for playing back AudioDataStreamable objects.
SoundSequence: Used to play back a series of AudioBuffers in sequence. They have a callback so that AudioBuffers can be added dynamically by the client.

SourceVoices are intended to be pooled. You can obtain one from the AudioDevice pool by calling AudioDevice.Obtain<T> where T is the type of SourceVoice you wish to obtain. When you call Return on the voice it will be returned to the pool. TransientVoices are automatically returned to the pool when they have finished playing back their AudioBuffer.

SourceVoices can send audio to SubmixVoices. This is a convenient way to manage categories of audio. For example the client could have a MusicSubmix that all music-related voices send to. Then the volume of all music can be changed at once without the client having to manage all the individual music voices.

By default all voices send audio to AudioDevice.MasteringVoice. This is also a SubmixVoice that can be controlled like any other voice.

AudioDataStreamable is used in conjunction with a StreamingVoice to play back streaming audio from an ogg or qoa file.

AudioDataWav, AudioDataOgg, and AudioDataQoa all have a static CreateBuffer method that can be used to create an AudioBuffer from an audio file.

Reviewed-on: MoonsideGames/MoonWorks#50
2023-08-03 19:54:02 +00:00
cosmonaut 81cd397013 fix AudioDevice crash on shutdown 2023-07-28 15:02:20 -07:00
cosmonaut e73c7ede55 rename SoundQueue to SoundSequence 2023-07-28 13:21:50 -07:00
cosmonaut 83f1cc24db rename OnBufferNeeded to OnSoundNeeded 2023-07-28 13:10:01 -07:00
cosmonaut 1d86d0c210 add SoundQueue 2023-07-28 13:02:50 -07:00
cosmonaut dbbd6540ab fix StaticSoundInstance.Pause not actually pausing 2023-06-30 11:51:20 -07:00
cosmonaut 74ae295036 fix audio thread race condition on StaticAudio.GetInstance 2023-06-29 15:01:22 -07:00
cosmonaut 36ddb03d8f only check video exception on thread fault 2023-06-29 12:14:46 -07:00
cosmonaut bf3ad0c8b0 add Game.ShowRuntimeError method 2023-06-29 11:30:32 -07:00
cosmonaut f761d4f76e re-throw exceptions from video thread 2023-06-29 11:30:16 -07:00
cosmonaut 4c731401ff fix vertical axis on axis buttons being backwards 2023-06-28 18:19:34 -07:00
cosmonaut 071518732e set axis button threshold default to 0.5 2023-06-28 17:17:57 -07:00
cosmonaut 1adb76d5c7 exception handlers for video decoder threads 2023-06-28 13:20:33 -07:00
cosmonaut 5ff7da927a try-catch inside AudioThreadMain 2023-06-28 13:20:18 -07:00
cosmonaut 0fd3365d1d update dll.config 2023-06-23 16:17:44 -07:00
cosmonaut affb592c15 add dav1dfile dependency to README 2023-06-19 16:19:36 -07:00
cosmonaut 7e79e4a11d fix validator warnings on Quit 2023-06-15 17:45:26 -07:00
cosmonaut fc0937b2ff fix crash caused by audio weak references 2023-06-14 18:22:49 -07:00
cosmonaut c83997609f fix some scenarios where video pointers should not be replaced 2023-06-13 19:18:23 -07:00
cosmonaut b65d4e391c public Fix64 RawValue 2023-06-12 09:53:44 -07:00
cosmonaut 56bab545ba add MouseButton.Button lookup 2023-06-09 18:15:15 -07:00
cosmonaut 2ae116c72b fix window dimensions when starting in fullscreen 2023-06-09 16:27:43 -07:00
cosmonaut bd3e70b096 make KeyboardButton.KeyCode public 2023-06-09 16:17:41 -07:00
cosmonaut b1fe7f96b2 recenter window on windowed mode change 2023-06-09 11:42:20 -07:00
cosmonaut 00366cc9d4 fix dav1dfile submodule 2023-06-07 15:35:07 -07:00
cosmonaut 3bc25bc3a1 remove theorafile from dll config 2023-06-07 15:29:45 -07:00
cosmonaut 496eb670ab AV1 Video instead of Theora (#49)
VideoPlayer now takes AV1 video instead of Ogg Theora. This brings a significant decode speed improvement. The decoder now also operates in a threaded manner, which should prevent runtime stalls when fetching video frames.

Reviewed-on: MoonsideGames/MoonWorks#49
2023-06-07 21:18:44 +00:00
cosmonaut 00f4bfdeae optimize StreamingSoundQoa.Create 2023-05-22 19:24:26 -07:00
cosmonaut adeba633e5 csproj tweaks to support app publish 2023-05-22 18:28:13 -07:00
cosmonaut 300ef9f88e fix unnecessary copy in PackFontRanges 2023-05-22 11:41:19 -07:00
cosmonaut 76684eaa33 fix memory leak in StreamingSoundQoa.Create 2023-05-11 19:45:07 -07:00
cosmonaut c037b4cb69 fix StaticSoundInstance race condition and state 2023-05-11 18:59:26 -07:00
cosmonaut 5df08727c1 Sound instancing rework 2023-05-11 17:56:40 -07:00
cosmonaut 537517afb9 remove buffer size log 2023-05-10 15:13:19 -07:00
cosmonaut bd405dfbf0 QOA Support (#48)
Reviewed-on: MoonsideGames/MoonWorks#48
2023-05-05 22:26:32 +00:00
cosmonaut 2d7bb24b5c rename Texture load methods for clarity 2023-04-19 00:50:59 -07:00
cosmonaut 0ea60a376b new image loader API 2023-04-19 00:41:18 -07:00
cosmonaut e3c2f0e119 fix controller hot swapping 2023-04-05 16:52:36 -07:00
cosmonaut 3584e670ee add array overloads to avoid explicit generic parameter 2023-04-05 12:40:34 -07:00
cosmonaut 5a2f7eadb8 change array param to span in Buffer.GetData 2023-04-05 11:32:12 -07:00
cosmonaut 1e3f04235e remove array parameters from API functions 2023-04-05 11:07:16 -07:00
cosmonaut dd06205399 stop static sound instance on Free 2023-04-05 01:18:13 -07:00
cosmonaut 1cf04a7279 assets stream data directly into unmanaged memory 2023-04-05 00:47:02 -07:00
cosmonaut 3bd435746b StaticSound external byte buffer constructor 2023-04-04 17:12:03 -07:00
cosmonaut 8134761e44 load images from memory + QOI support 2023-04-03 17:28:00 -07:00
cosmonaut 8209051a3c remove non-static Normalize + return identity on zero vector 2023-03-29 10:06:37 -07:00
cosmonaut 80f3711f4c add Quit method to Game 2023-03-16 16:48:52 -07:00
cosmonaut 3a6b73e637 add int clamp to MathHelper 2023-03-14 14:42:13 -07:00
cosmonaut 12e7e6b9c1 add Color.FromHSV 2023-03-09 15:14:16 -08:00
cosmonaut 455f4048df MathHelper.Quantize uses round instead of floor 2023-03-09 13:52:45 -08:00
cosmonaut 1f0e3b5040 Audio Improvements (#47)
- Audio is now processed on a background thread instead of the main thread
- Audio tick rate is now ~200Hz
- MoonWorks.Math.Easings class completely rewritten to be easier to understand and use
- SoundInstance properties no longer call into FAudio unless the value actually changed
- SoundInstance property values can now be interpolated over time (tweens)
- SoundInstance tweens can be delayed
- SoundInstance sets a sane filter frequency default when switching filter type
- StreamingSound classes can be designated to update automatically on the audio thread or manually
- StreamingSound buffer consumption should now set Stopped state in a more sane way
- Added ReverbEffect, which creates a submix voice for a reverb effect
- SoundInstance can apply a ReverbEffect, which enables the Reverb property
- Audio resource tracking improvements
- Some tweaks to VideoPlayer to make its behavior more consistent

Reviewed-on: MoonsideGames/MoonWorks#47
2023-03-07 23:28:57 +00:00
cosmonaut f8b14ea94f add ReverbEffect 2023-03-01 17:47:09 -08:00
cosmonaut 472da0edd2 add Hidden property to Mouse 2023-03-01 13:47:25 -08:00
cosmonaut bd825b6c91 allow pushing raw uniform data 2023-02-23 16:59:34 -08:00
cosmonaut 515c2ebbca fix controller double open 2023-02-23 16:59:19 -08:00
cosmonaut 86322e9373 log controller open errors 2023-02-21 16:00:16 -08:00
cosmonaut e9aacb44da Add synchronized audio playback mechanism 2023-02-16 15:12:35 -08:00
cosmonaut 5baa1d7b40 Fix video shader base path lookup 2023-02-08 12:44:36 -08:00
cosmonaut f673803c37 FAudio 23.02 2023-02-07 12:31:37 -08:00
cosmonaut 0f78cd1a0c Refresh 1.11.0 2023-02-07 12:28:52 -08:00
cosmonaut 36ce74b58a tweak video shader filenames 2023-02-03 15:07:32 -08:00
cosmonaut 40d12357c0 Remove MoonWorks.Collision (#46)
After months of tweaking and refactoring I have realized that collision is like rendering - it's so fundamental to the structure of your game that making broad decisions about how it should work from a library level is too restrictive and difficult to optimize. Anyone skilled enough to use MoonWorks should be easily able to roll their own collision detection.

Reviewed-on: MoonsideGames/MoonWorks#46
2023-02-03 19:51:36 +00:00
evan e52fe60657 update RefreshCS 2023-01-31 12:29:17 -08:00
TheSpydog b39526ca90 Textures now have a sample count, not render passes (#45)
Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#45
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2023-01-31 20:27:26 +00:00
TheSpydog 88d9119830 Remove warning from DrawInstancedPrimitives doc comment (#44)
Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#44
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2023-01-24 00:14:46 +00:00
TheSpydog 05de9a4066 Make video shaders optional and search for them in the root output directory (#42)
Whenever the video shaders change, they can be rebuilt with refreshc and distributed alongside the moonlibs.

Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#42
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2023-01-21 23:37:01 +00:00
TheSpydog b1d30a9e6c Remove isFixed param from AcquireCommandBuffer call (#41)
Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#41
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2023-01-14 18:04:53 +00:00
TheSpydog 030745361b Fix BC7 loading for textures without DDSD_CAPS/FMT (#40)
Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#40
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2023-01-07 01:46:59 +00:00
cosmonaut c43df10c2a vertex binding API improvements 2023-01-05 13:41:48 -08:00
cosmonaut 95981f0f03 rework vertex input state creation to avoid reflection 2023-01-04 17:34:01 -08:00
cosmonaut 703b694bf6 Refresh 1.10.0 2023-01-04 11:35:31 -08:00
cosmonaut fe561b61ef update libs 2023-01-04 11:34:54 -08:00
TheSpydog 230c1b41b4 Add IndirectDrawCommand struct (#39)
Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#39
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2023-01-04 18:44:56 +00:00
cosmonaut 1fb3e5adf0 update RefreshCS 2022-12-28 19:24:08 -08:00
TheSpydog 6f8858c8b7 RasterizerState / BlendFactor ABI break (#38)
Updates the APIs to match [this Refresh PR](MoonsideGames/Refresh#27).

Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#38
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-12-29 03:22:47 +00:00
TheSpydog f96298f991 Add validation checks for AcquireSwapchainTexture and BindVertexBuffers (#37)
This replaces the Refresh-side check for window claim status in AcquireSwapchainTexture, and adds validation to ensure that BindVertexBuffers is not called before BindGraphicsPipeline.

Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#37
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-12-29 03:18:36 +00:00
cosmonaut d76633bdfc change sizeof calls to Marshal.SizeOf in interop situations 2022-12-13 19:01:40 -08:00
cosmonaut debb76f62a validate scissor dimensions 2022-12-13 16:09:32 -08:00
cosmonaut ca61e94b13 optimize mouse update 2022-12-13 15:13:43 -08:00
cosmonaut 36e6c6f332 optimize AcquireSwapchainTexture 2022-12-13 00:52:35 -08:00
cosmonaut 916962da6c garbage collection optimizations 2022-12-13 00:34:16 -08:00
cosmonaut cfd52b00bd add FIXME note for spatial hash boxing 2022-12-08 13:29:04 -08:00
cosmonaut b0e1ad3cf8 add note about disposing swapchain texture 2022-12-01 13:20:11 -08:00
cosmonaut 2c4e1b972a .NET 7 2022-11-30 10:01:09 -08:00
cosmonaut 751b8310ce Fix crash when no audio device found 2022-11-30 09:43:49 -08:00
cosmonaut 474b8fe37d only check depth format if there is a depth attachment 2022-11-29 13:06:03 -08:00
cosmonaut bc41d2c079 add IsIdle and IsUp to Keyboard 2022-11-28 10:19:03 -08:00
cosmonaut d5ddd44bd3 add IsIdle and IsUp to VirtualButton 2022-11-28 00:48:48 -08:00
cosmonaut de9d13757a distinguish between idle and released on buttons 2022-11-27 12:41:09 -08:00
cosmonaut ca9fb45536 fix spacing 2022-11-18 10:34:32 -08:00
cosmonaut faf81bceed add support for mouse X1 and X2 buttons (#36)
Reviewed-on: MoonsideGames/MoonWorks#36
2022-11-18 18:33:15 +00:00
cosmonaut fa992652b1 fix mouse button updating twice 2022-11-17 18:16:26 -08:00
marpe c4bae77408 Fix infinite recursion on certain Easing functions 2022-11-17 14:40:24 -08:00
cosmonaut ecf1a8ed55 Optimize CommandBuffer (#35)
- remove params methods in favor of overloads and last-resort `in Span<T>` overloads
- add `in` to params that take structs to reduce unnecessary copying
- Buffer can now implicitly cast to BufferBinding
- add render pass / graphics pipeline format match validation

Reviewed-on: MoonsideGames/MoonWorks#35
2022-11-17 20:35:21 +00:00
cosmonaut 970517c3c2 add window size change callback support 2022-11-15 22:53:37 -08:00
cosmonaut f3c5bbf902 rename BorderlessWindow to BorderlessFullscreen 2022-11-15 22:49:07 -08:00
cosmonaut 02b20f79f5 allow sampler binding calls to take array segment 2022-11-15 17:53:30 -08:00
TheSpydog c2ef78d136 Add validation check that all work group dimension counts are >= 1 (#34)
Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#34
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-11-14 18:21:03 +00:00
TheSpydog 5533eeb2fd Fix bad pointer arithmetic in SetBufferData (#33)
Fixes a bug where SetBufferData was writing garbage to the buffer if `startElement` was non-zero.

Rather than fixing the pointer addition (it should have been `ptr + startElement`) I opted to remove it and instead pass the index explicitly when grabbing the address of the array element. I think that's a little easier to understand, and it's slightly safer too -- if you pass a startElement beyond the array bounds it will now throw an IndexOutOfRangeException instead of silently reading from outside the array bounds.

Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#33
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-11-12 03:42:35 +00:00
cosmonaut 934f3fd623 Refresh 1.9.0 2022-11-09 12:14:33 -08:00
TheSpydog 02b0c12ad8 Bunch of Refresh API changes, plus more validation (#31)
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-11-09 18:49:53 +00:00
TheSpydog 1b38f8606b Added more graphics validation, misc api tweaks (#30)
* Refactored render pass attachment validation to avoid copy-pasting the asserts
* Added validation for texture usage flags and null textures/samplers
* Added an exception for when GraphicsPipeline/ComputePipeline creation fails
* Changed TextureSamplerBinding so that it holds references to the Texture and Sampler classes, rather than just their handles
* Removed the CommandBuffer.BindVertex/FragmentSamplers overloads that took a length parameter

Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#30
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-11-08 19:29:05 +00:00
cosmonaut db38ada410 update dependency libraries 2022-11-01 16:33:26 -07:00
cosmonaut bd98ae8441 fix validation error when TextBatch is empty 2022-10-24 17:22:24 -07:00
cosmonaut 59190e619d fix Mouse.Wheel calculation 2022-10-21 11:39:06 -07:00
cosmonaut ea86212199 add AudioDevice.MasteringVolume property 2022-10-20 15:00:25 -07:00
cosmonaut 66c6ceec04 hide main window until Game.Run is called 2022-09-30 13:03:05 -07:00
cosmonaut dfddc24d0e add PresentMode to WindowCreateInfo 2022-09-30 13:02:51 -07:00
cosmonaut b66e077376 GraphicsDevice throw if backend invalid 2022-09-29 19:21:47 -07:00
cosmonaut 07c0b1b9a2 Window API revision + Framerate limiter (#27)
- add `Backend` enum
- `GraphicsDevice` can now take a preferred backend at creation

- add `GraphicsDevice.ClaimWindow`
- add `GraphicsDevice.UnclaimWindow`
- add `GraphicsDevice.SetPresentMode`

- add `Window.Claimed`
- add `Window.SwapchainFormat`
- fix certain odd behaviors in multi-window scenarios

- rename `FramerateSettings` to `FrameLimiterSettings`
- add `Game.SetFrameLimiter`

Reviewed-on: MoonsideGames/MoonWorks#27
2022-09-29 22:22:50 +00:00
TheSpydog 3ffdf8a929 Specify readonly access when creating ShaderModule stream (#26)
Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#26
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-09-28 23:52:29 +00:00
cosmonaut bace9f570d add Fix64.Step 2022-09-22 20:17:13 -07:00
cosmonaut 32f80282a0 clean up Video.cs 2022-09-21 15:16:34 -07:00
cosmonaut 23252a149f ignore currently loaded video on VideoPlayer.Load 2022-09-20 11:31:27 -07:00
cosmonaut 80f19e4521 Refresh 1.7.0 2022-09-13 13:58:09 -07:00
cosmonaut 7ea9a6aea3 bump FAudio version 2022-09-07 13:11:24 -07:00
cosmonaut d5a7daa524 add graphics state validation in debug mode 2022-09-07 13:07:17 -07:00
cosmonaut 5c080a4c42 add another DepthStencilAttachment constructor 2022-09-07 12:38:05 -07:00
TheSpydog eb8c350a47 Fix debug assertion in AudioDevice constructor (#25)
Let's try this again...

Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#25
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-08-30 17:09:32 +00:00
cosmonaut 0933b8e70f reset static audio on free + rework filter API 2022-08-29 22:45:28 -07:00
cosmonaut 1af16231ce Refresh 1.6.0 2022-08-25 12:32:49 -07:00
cosmonaut 0c6bb538fb fix depth-only render pass invocation + throw instead of return 2022-08-22 19:30:44 -07:00
cosmonaut db66fbf115 update to latest SDL2 and FAudio 2022-08-22 17:52:21 -07:00
cosmonaut f93be99545 https links in README 2022-08-22 17:49:43 -07:00
TheSpydog ebfd4fd457 Ignore dllmap logic when running under NativeAOT (#23)
Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#23
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-08-22 16:43:19 +00:00
cosmonaut b380707462 Video Optimization (#22)
- Videos are now shoved into memory when created to avoid disk latency issues
- Added VideoPlayer class to avoid redundant texture creation on videos
- Most Video functions are now on VideoPlayer

Reviewed-on: MoonsideGames/MoonWorks#22
2022-08-18 20:45:34 +00:00
cosmonaut 491eafac76 fix default Video playback speed being 0 2022-08-18 00:57:22 -07:00
cosmonaut 0dddf2a0af fix default pitch 2022-08-17 18:45:35 -07:00
cosmonaut 15aefc2212 add MoonWorks.Math.Easing 2022-08-16 18:24:46 -07:00
cosmonaut a427b79510 add Fix64-int arithmetic 2022-08-16 18:24:34 -07:00
TheSpydog 49f852a822 Combine all dependencies into the main csproj (#21)
This removes the need for per-library dllmap logic and simplifies the build process.

Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Reviewed-on: MoonsideGames/MoonWorks#21
Co-authored-by: TheSpydog <thespydog@noreply.example.org>
Co-committed-by: TheSpydog <thespydog@noreply.example.org>
2022-08-14 20:44:55 +00:00
179 changed files with 10993 additions and 5745 deletions

6
.gitmodules vendored
View File

@ -10,6 +10,6 @@
[submodule "lib/WellspringCS"]
path = lib/WellspringCS
url = https://gitea.moonside.games/MoonsideGames/WellspringCS.git
[submodule "lib/Theorafile"]
path = lib/Theorafile
url = https://github.com/FNA-XNA/Theorafile.git
[submodule "lib/dav1dfile"]
path = lib/dav1dfile
url = https://github.com/MoonsideGames/dav1dfile.git

2862
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Platforms>x64</Platforms>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>11</LangVersion>
</PropertyGroup>
<PropertyGroup>
@ -11,25 +11,32 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include=".\lib\SDL2-CS\SDL2-CS.Core.csproj" />
<ProjectReference Include=".\lib\RefreshCS\RefreshCS.csproj" />
<ProjectReference Include=".\lib\FAudio\csharp\FAudio-CS.Core.csproj" />
<ProjectReference Include=".\lib\WellspringCS\WellspringCS.csproj" />
<ProjectReference Include=".\lib\Theorafile\csharp\Theorafile-CS.Core.csproj" />
<Compile Include="lib\FAudio\csharp\FAudio.cs" />
<Compile Include="lib\RefreshCS\src\Refresh.cs" />
<Compile Include="lib\SDL2-CS\src\SDL2.cs" />
<Compile Include="lib\WellspringCS\WellspringCS.cs" />
<Compile Include="lib\dav1dfile\csharp\dav1dfile.cs" />
</ItemGroup>
<ItemGroup>
<None Include="MoonWorks.dll.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="src\Video\Shaders\Compiled\FullscreenVert.spv">
<LogicalName>MoonWorks.Shaders.FullscreenVert.spv</LogicalName>
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\video_fullscreen.vert.refresh">
<LogicalName>MoonWorks.Graphics.StockShaders.VideoFullscreen.vert.refresh</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="src\Video\Shaders\Compiled\YUV2RGBAFrag.spv">
<LogicalName>MoonWorks.Shaders.YUV2RGBAFrag.spv</LogicalName>
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\video_yuv2rgba.frag.refresh">
<LogicalName>MoonWorks.Graphics.StockShaders.VideoYUV2RGBA.frag.refresh</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\text_transform.vert.refresh">
<LogicalName>MoonWorks.Graphics.StockShaders.TextTransform.vert.refresh</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\text_msdf.frag.refresh">
<LogicalName>MoonWorks.Graphics.StockShaders.TextMSDF.frag.refresh</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -5,18 +5,18 @@
<dllmap dll="SDL2" os="linux,freebsd,netbsd" target="libSDL2-2.0.so.0"/>
<dllmap dll="Refresh" os="windows" target="Refresh.dll"/>
<dllmap dll="Refresh" os="osx" target="libRefresh.0.dylib"/>
<dllmap dll="Refresh" os="linux,freebsd,netbsd" target="libRefresh.so.0"/>
<dllmap dll="Refresh" os="osx" target="libRefresh.1.dylib"/>
<dllmap dll="Refresh" os="linux,freebsd,netbsd" target="libRefresh.so.1"/>
<dllmap dll="FAudio" os="windows" target="FAudio.dll"/>
<dllmap dll="FAudio" os="osx" target="libFAudio.0.dylib"/>
<dllmap dll="FAudio" os="linux,freebsd,netbsd" target="libFAudio.so.0"/>
<dllmap dll="Wellspring" os="windows" target="Wellspring.dll"/>
<dllmap dll="Wellspring" os="osx" target="libWellspring.0.dylib"/>
<dllmap dll="Wellspring" os="linux,freebsd,netbsd" target="libWellspring.so.0"/>
<dllmap dll="Wellspring" os="osx" target="libWellspring.1.dylib"/>
<dllmap dll="Wellspring" os="linux,freebsd,netbsd" target="libWellspring.so.1"/>
<dllmap dll="Theorafile" os="windows" target="libtheorafile.dll"/>
<dllmap dll="Theorafile" os="osx" target="libtheorafile.dylib"/>
<dllmap dll="Theorafile" os="linux,freebsd,netbsd" target="libtheorafile.so"/>
<dllmap dll="dav1dfile" os="windows" target="dav1dfile.dll"/>
<dllmap dll="dav1dfile" os="osx" target="libdav1dfile.1.dylib"/>
<dllmap dll="dav1dfile" os="linux,freebsd,netbsd,openbsd" target="libdav1dfile.so.1"/>
</configuration>

View File

@ -4,19 +4,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
VisualStudioVersion = 16.0.30717.126
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MoonWorks", "MoonWorks.csproj", "{DDC9BA9B-4440-4CB3-BDB4-D5F91DE1686B}"
ProjectSection(ProjectDependencies) = postProject
{608AA31D-F163-4096-B4EF-B9C7D21D52BB} = {608AA31D-F163-4096-B4EF-B9C7D21D52BB}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SDL2-CS.Core", "lib\SDL2-CS\SDL2-CS.Core.csproj", "{0929F2D8-8FE4-4452-AD1E-50760A1A19A5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FAudio-CS.Core", "lib\FAudio\csharp\FAudio-CS.Core.csproj", "{608AA31D-F163-4096-B4EF-B9C7D21D52BB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RefreshCS", "lib\RefreshCS\RefreshCS.csproj", "{AD7C94E4-0AFA-44CA-889C-110142369893}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{69D3788D-6C57-44F7-A912-B201AE6D7C04}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WellspringCS", "lib\WellspringCS\WellspringCS.csproj", "{0DD7B866-773C-4A86-8580-F436DAA28989}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -28,22 +15,6 @@ Global
{DDC9BA9B-4440-4CB3-BDB4-D5F91DE1686B}.Debug|x64.Build.0 = Debug|x64
{DDC9BA9B-4440-4CB3-BDB4-D5F91DE1686B}.Release|x64.ActiveCfg = Release|x64
{DDC9BA9B-4440-4CB3-BDB4-D5F91DE1686B}.Release|x64.Build.0 = Release|x64
{0929F2D8-8FE4-4452-AD1E-50760A1A19A5}.Debug|x64.ActiveCfg = Debug|x64
{0929F2D8-8FE4-4452-AD1E-50760A1A19A5}.Debug|x64.Build.0 = Debug|x64
{0929F2D8-8FE4-4452-AD1E-50760A1A19A5}.Release|x64.ActiveCfg = Release|x64
{0929F2D8-8FE4-4452-AD1E-50760A1A19A5}.Release|x64.Build.0 = Release|x64
{608AA31D-F163-4096-B4EF-B9C7D21D52BB}.Debug|x64.ActiveCfg = Debug|x64
{608AA31D-F163-4096-B4EF-B9C7D21D52BB}.Debug|x64.Build.0 = Debug|x64
{608AA31D-F163-4096-B4EF-B9C7D21D52BB}.Release|x64.ActiveCfg = Release|x64
{608AA31D-F163-4096-B4EF-B9C7D21D52BB}.Release|x64.Build.0 = Release|x64
{AD7C94E4-0AFA-44CA-889C-110142369893}.Debug|x64.ActiveCfg = Debug|x64
{AD7C94E4-0AFA-44CA-889C-110142369893}.Debug|x64.Build.0 = Debug|x64
{AD7C94E4-0AFA-44CA-889C-110142369893}.Release|x64.ActiveCfg = Release|x64
{AD7C94E4-0AFA-44CA-889C-110142369893}.Release|x64.Build.0 = Release|x64
{0DD7B866-773C-4A86-8580-F436DAA28989}.Debug|x64.ActiveCfg = Debug|x64
{0DD7B866-773C-4A86-8580-F436DAA28989}.Debug|x64.Build.0 = Debug|x64
{0DD7B866-773C-4A86-8580-F436DAA28989}.Release|x64.ActiveCfg = Release|x64
{0DD7B866-773C-4A86-8580-F436DAA28989}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -51,7 +22,4 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3D68FAA-3165-43C7-95B3-D845F0DAA918}
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{0DD7B866-773C-4A86-8580-F436DAA28989} = {69D3788D-6C57-44F7-A912-B201AE6D7C04}
EndGlobalSection
EndGlobal

View File

@ -12,9 +12,13 @@ MoonWorks uses strictly Free Open Source Software. It will never have any kind o
## Documentation
High-level documentation is provided here: http://moonside.games/docs/moonworks/
API Reference: https://moonside.games/docs/moonworksapi/
For an actual API reference, the source is documented in doc comments that your preferred IDE can read.
High-level documentation is provided here: https://moonside.games/docs/moonworks/
The source is documented in doc comments that your preferred IDE can read.
Join our Discord! https://discord.gg/ujhwdkHmhN
## Dependencies
@ -22,9 +26,9 @@ For an actual API reference, the source is documented in doc comments that your
* [Refresh](https://gitea.moonside.games/MoonsideGames/Refresh) - Graphics
* [FAudio](https://github.com/FNA-XNA/FAudio) - Audio
* [Wellspring](https://gitea.moonside.games/MoonsideGames/Wellspring) - Font Rendering
* [Theorafile](https://github.com/FNA-XNA/Theorafile) - Compressed Video
* [dav1dfile](https://github.com/MoonsideGames/dav1dfile) - Compressed Video
Prebuilt dependencies can be obtained here: http://moonside.games/files/moonlibs.tar.bz2
Prebuilt dependencies can be obtained here: https://moonside.games/files/moonlibs.tar.bz2
## License

@ -1 +1 @@
Subproject commit 0b6d5dabbf428633482fe3a956fbdb53228fcf35
Subproject commit 60480416bda930bf7544e6abe31b937f0daa0256

@ -1 +1 @@
Subproject commit 98c590ae77c3b6a64a370bac439f20728959a8b6
Subproject commit b5325e6d0329eeb35b074091a569a5f679852d28

@ -1 +1 @@
Subproject commit b35aaa494e44d08242788ff0ba2cb7a508f4d8f0
Subproject commit e4afbb848586fca530b6538320f799f81a18b941

@ -1 +0,0 @@
Subproject commit dd8c7fa69e678b6182cdaa71458ad08dd31c65da

@ -1 +1 @@
Subproject commit f8872bae59e394b0f8a35224bb39ab8fd041af97
Subproject commit 074f2afc833b221906bb2468735041ce78f2cb89

1
lib/dav1dfile Submodule

@ -0,0 +1 @@
Subproject commit 5065e2cd4662dbe023b77a45ef967f975170dfff

79
src/Audio/AudioBuffer.cs Normal file
View File

@ -0,0 +1,79 @@
using System;
using System.Runtime.InteropServices;
namespace MoonWorks.Audio
{
/// <summary>
/// Contains raw audio data in a specified Format. <br/>
/// Submit this to a SourceVoice to play audio.
/// </summary>
public class AudioBuffer : AudioResource
{
IntPtr BufferDataPtr;
uint BufferDataLength;
private bool OwnsBufferData;
public Format Format { get; }
/// <summary>
/// Create a new AudioBuffer.
/// </summary>
/// <param name="ownsBufferData">If true, the buffer data will be destroyed when this AudioBuffer is destroyed.</param>
public AudioBuffer(
AudioDevice device,
Format format,
IntPtr bufferPtr,
uint bufferLengthInBytes,
bool ownsBufferData) : base(device)
{
Format = format;
BufferDataPtr = bufferPtr;
BufferDataLength = bufferLengthInBytes;
OwnsBufferData = ownsBufferData;
}
/// <summary>
/// Create another AudioBuffer from this audio buffer.
/// It will not own the buffer data.
/// </summary>
/// <param name="offset">Offset in bytes from the top of the original buffer.</param>
/// <param name="length">Length in bytes of the new buffer.</param>
/// <returns></returns>
public AudioBuffer Slice(int offset, uint length)
{
return new AudioBuffer(Device, Format, BufferDataPtr + offset, length, false);
}
/// <summary>
/// Create an FAudioBuffer struct from this AudioBuffer.
/// </summary>
/// <param name="loop">Whether we should set the FAudioBuffer to loop.</param>
public FAudio.FAudioBuffer ToFAudioBuffer(bool loop = false)
{
return new FAudio.FAudioBuffer
{
Flags = FAudio.FAUDIO_END_OF_STREAM,
pContext = IntPtr.Zero,
pAudioData = BufferDataPtr,
AudioBytes = BufferDataLength,
PlayBegin = 0,
PlayLength = 0,
LoopBegin = 0,
LoopLength = 0,
LoopCount = loop ? FAudio.FAUDIO_LOOP_INFINITE : 0
};
}
protected override unsafe void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (OwnsBufferData)
{
NativeMemory.Free((void*) BufferDataPtr);
}
}
base.Dispose(disposing);
}
}
}

147
src/Audio/AudioDataOgg.cs Normal file
View File

@ -0,0 +1,147 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace MoonWorks.Audio
{
/// <summary>
/// Streamable audio in Ogg format.
/// </summary>
public class AudioDataOgg : AudioDataStreamable
{
private IntPtr FileDataPtr = IntPtr.Zero;
private IntPtr VorbisHandle = IntPtr.Zero;
private string FilePath;
public override bool Loaded => VorbisHandle != IntPtr.Zero;
public override uint DecodeBufferSize => 32768;
public AudioDataOgg(AudioDevice device, string filePath) : base(device)
{
FilePath = filePath;
var handle = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
if (error != 0)
{
throw new InvalidOperationException("Error loading file!");
}
var info = FAudio.stb_vorbis_get_info(handle);
Format = new Format
{
Tag = FormatTag.IEEE_FLOAT,
BitsPerSample = 32,
Channels = (ushort) info.channels,
SampleRate = info.sample_rate
};
FAudio.stb_vorbis_close(handle);
}
public override unsafe void Decode(void* buffer, int bufferLengthInBytes, out int filledLengthInBytes, out bool reachedEnd)
{
var lengthInFloats = bufferLengthInBytes / sizeof(float);
/* NOTE: this function returns samples per channel, not total samples */
var samples = FAudio.stb_vorbis_get_samples_float_interleaved(
VorbisHandle,
Format.Channels,
(IntPtr) buffer,
lengthInFloats
);
var sampleCount = samples * Format.Channels;
reachedEnd = sampleCount < lengthInFloats;
filledLengthInBytes = sampleCount * sizeof(float);
}
/// <summary>
/// Prepares the Ogg data for streaming.
/// </summary>
public override unsafe void Load()
{
if (!Loaded)
{
var fileStream = new FileStream(FilePath, FileMode.Open, FileAccess.Read);
FileDataPtr = (nint) NativeMemory.Alloc((nuint) fileStream.Length);
var fileDataSpan = new Span<byte>((void*) FileDataPtr, (int) fileStream.Length);
fileStream.ReadExactly(fileDataSpan);
fileStream.Close();
VorbisHandle = FAudio.stb_vorbis_open_memory(FileDataPtr, fileDataSpan.Length, out int error, IntPtr.Zero);
if (error != 0)
{
NativeMemory.Free((void*) FileDataPtr);
Logger.LogError("Error opening OGG file!");
Logger.LogError("Error: " + error);
throw new InvalidOperationException("Error opening OGG file!");
}
}
}
public override void Seek(uint sampleFrame)
{
FAudio.stb_vorbis_seek(VorbisHandle, sampleFrame);
}
/// <summary>
/// Unloads the Ogg data, freeing resources.
/// </summary>
public override unsafe void Unload()
{
if (Loaded)
{
FAudio.stb_vorbis_close(VorbisHandle);
NativeMemory.Free((void*) FileDataPtr);
VorbisHandle = IntPtr.Zero;
FileDataPtr = IntPtr.Zero;
}
}
/// <summary>
/// Loads an entire ogg file into an AudioBuffer. Useful for static audio.
/// </summary>
public static unsafe AudioBuffer CreateBuffer(AudioDevice device, string filePath)
{
var filePointer = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
if (error != 0)
{
throw new InvalidOperationException("Error loading file!");
}
var info = FAudio.stb_vorbis_get_info(filePointer);
var lengthInFloats =
FAudio.stb_vorbis_stream_length_in_samples(filePointer) * info.channels;
var lengthInBytes = lengthInFloats * Marshal.SizeOf<float>();
var buffer = NativeMemory.Alloc((nuint) lengthInBytes);
FAudio.stb_vorbis_get_samples_float_interleaved(
filePointer,
info.channels,
(nint) buffer,
(int) lengthInFloats
);
FAudio.stb_vorbis_close(filePointer);
var format = new Format
{
Tag = FormatTag.IEEE_FLOAT,
BitsPerSample = 32,
Channels = (ushort) info.channels,
SampleRate = info.sample_rate
};
return new AudioBuffer(
device,
format,
(nint) buffer,
(uint) lengthInBytes,
true);
}
}
}

164
src/Audio/AudioDataQoa.cs Normal file
View File

@ -0,0 +1,164 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace MoonWorks.Audio
{
/// <summary>
/// Streamable audio in QOA format.
/// </summary>
public class AudioDataQoa : AudioDataStreamable
{
private IntPtr QoaHandle = IntPtr.Zero;
private IntPtr FileDataPtr = IntPtr.Zero;
private string FilePath;
private const uint QOA_MAGIC = 0x716f6166; /* 'qoaf' */
public override bool Loaded => QoaHandle != IntPtr.Zero;
private uint decodeBufferSize;
public override uint DecodeBufferSize => decodeBufferSize;
public AudioDataQoa(AudioDevice device, string filePath) : base(device)
{
FilePath = filePath;
using var stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read);
using var reader = new BinaryReader(stream);
UInt64 fileHeader = ReverseEndianness(reader.ReadUInt64());
if ((fileHeader >> 32) != QOA_MAGIC)
{
throw new InvalidOperationException("Specified file is not a QOA file.");
}
uint totalSamplesPerChannel = (uint) (fileHeader & (0xFFFFFFFF));
if (totalSamplesPerChannel == 0)
{
throw new InvalidOperationException("Specified file is not a valid QOA file.");
}
UInt64 frameHeader = ReverseEndianness(reader.ReadUInt64());
uint channels = (uint) ((frameHeader >> 56) & 0x0000FF);
uint samplerate = (uint) ((frameHeader >> 32) & 0xFFFFFF);
uint samplesPerChannelPerFrame = (uint) ((frameHeader >> 16) & 0x00FFFF);
Format = new Format
{
Tag = FormatTag.PCM,
BitsPerSample = 16,
Channels = (ushort) channels,
SampleRate = samplerate
};
decodeBufferSize = channels * samplesPerChannelPerFrame * sizeof(short);
}
public override unsafe void Decode(void* buffer, int bufferLengthInBytes, out int filledLengthInBytes, out bool reachedEnd)
{
var lengthInShorts = bufferLengthInBytes / sizeof(short);
// NOTE: this function returns samples per channel!
var samples = FAudio.qoa_decode_next_frame(QoaHandle, (short*) buffer);
var sampleCount = samples * Format.Channels;
reachedEnd = sampleCount < lengthInShorts;
filledLengthInBytes = (int) (sampleCount * sizeof(short));
}
/// <summary>
/// Prepares qoa data for streaming.
/// </summary>
public override unsafe void Load()
{
if (!Loaded)
{
var fileStream = new FileStream(FilePath, FileMode.Open, FileAccess.Read);
FileDataPtr = (nint) NativeMemory.Alloc((nuint) fileStream.Length);
var fileDataSpan = new Span<byte>((void*) FileDataPtr, (int) fileStream.Length);
fileStream.ReadExactly(fileDataSpan);
fileStream.Close();
QoaHandle = FAudio.qoa_open_from_memory((char*) FileDataPtr, (uint) fileDataSpan.Length, 0);
if (QoaHandle == IntPtr.Zero)
{
NativeMemory.Free((void*) FileDataPtr);
Logger.LogError("Error opening QOA file!");
throw new InvalidOperationException("Error opening QOA file!");
}
}
}
public override void Seek(uint sampleFrame)
{
FAudio.qoa_seek_frame(QoaHandle, (int) sampleFrame);
}
/// <summary>
/// Unloads the qoa data, freeing resources.
/// </summary>
public override unsafe void Unload()
{
if (Loaded)
{
FAudio.qoa_close(QoaHandle);
NativeMemory.Free((void*) FileDataPtr);
QoaHandle = IntPtr.Zero;
FileDataPtr = IntPtr.Zero;
}
}
/// <summary>
/// Loads the entire qoa file into an AudioBuffer. Useful for static audio.
/// </summary>
public unsafe static AudioBuffer CreateBuffer(AudioDevice device, string filePath)
{
using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
var fileDataPtr = NativeMemory.Alloc((nuint) fileStream.Length);
var fileDataSpan = new Span<byte>(fileDataPtr, (int) fileStream.Length);
fileStream.ReadExactly(fileDataSpan);
fileStream.Close();
var qoaHandle = FAudio.qoa_open_from_memory((char*) fileDataPtr, (uint) fileDataSpan.Length, 0);
if (qoaHandle == 0)
{
NativeMemory.Free(fileDataPtr);
Logger.LogError("Error opening QOA file!");
throw new InvalidOperationException("Error opening QOA file!");
}
FAudio.qoa_attributes(qoaHandle, out var channels, out var samplerate, out var samples_per_channel_per_frame, out var total_samples_per_channel);
var bufferLengthInBytes = total_samples_per_channel * channels * sizeof(short);
var buffer = NativeMemory.Alloc(bufferLengthInBytes);
FAudio.qoa_decode_entire(qoaHandle, (short*) buffer);
FAudio.qoa_close(qoaHandle);
NativeMemory.Free(fileDataPtr);
var format = new Format
{
Tag = FormatTag.PCM,
BitsPerSample = 16,
Channels = (ushort) channels,
SampleRate = samplerate
};
return new AudioBuffer(device, format, (nint) buffer, bufferLengthInBytes, true);
}
private static unsafe UInt64 ReverseEndianness(UInt64 value)
{
byte* bytes = (byte*) &value;
return
((UInt64)(bytes[0]) << 56) | ((UInt64)(bytes[1]) << 48) |
((UInt64)(bytes[2]) << 40) | ((UInt64)(bytes[3]) << 32) |
((UInt64)(bytes[4]) << 24) | ((UInt64)(bytes[5]) << 16) |
((UInt64)(bytes[6]) << 8) | ((UInt64)(bytes[7]) << 0);
}
}
}

View File

@ -0,0 +1,49 @@
namespace MoonWorks.Audio
{
/// <summary>
/// Use this in conjunction with a StreamingVoice to play back streaming audio data.
/// </summary>
public abstract class AudioDataStreamable : AudioResource
{
public Format Format { get; protected set; }
public abstract bool Loaded { get; }
public abstract uint DecodeBufferSize { get; }
protected AudioDataStreamable(AudioDevice device) : base(device)
{
}
/// <summary>
/// Loads the raw audio data into memory to prepare it for stream decoding.
/// </summary>
public abstract void Load();
/// <summary>
/// Unloads the raw audio data from memory.
/// </summary>
public abstract void Unload();
/// <summary>
/// Seeks to the given sample frame.
/// </summary>
public abstract void Seek(uint sampleFrame);
/// <summary>
/// Attempts to decodes data of length bufferLengthInBytes into the provided buffer.
/// </summary>
/// <param name="buffer">The buffer that decoded bytes will be placed into.</param>
/// <param name="bufferLengthInBytes">Requested length of decoded audio data.</param>
/// <param name="filledLengthInBytes">How much data was actually filled in by the decode.</param>
/// <param name="reachedEnd">Whether the end of the data was reached on this decode.</param>
public abstract unsafe void Decode(void* buffer, int bufferLengthInBytes, out int filledLengthInBytes, out bool reachedEnd);
protected override void Dispose(bool disposing)
{
if (!IsDisposed)
{
Unload();
}
base.Dispose(disposing);
}
}
}

100
src/Audio/AudioDataWav.cs Normal file
View File

@ -0,0 +1,100 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace MoonWorks.Audio
{
public static class AudioDataWav
{
/// <summary>
/// Create an AudioBuffer containing all the WAV audio data in a file.
/// </summary>
/// <returns></returns>
public unsafe static AudioBuffer CreateBuffer(AudioDevice device, string filePath)
{
// mostly borrowed from https://github.com/FNA-XNA/FNA/blob/b71b4a35ae59970ff0070dea6f8620856d8d4fec/src/Audio/SoundEffect.cs#L385
// WaveFormatEx data
ushort wFormatTag;
ushort nChannels;
uint nSamplesPerSec;
uint nAvgBytesPerSec;
ushort nBlockAlign;
ushort wBitsPerSample;
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var reader = new BinaryReader(stream);
// RIFF Signature
string signature = new string(reader.ReadChars(4));
if (signature != "RIFF")
{
throw new NotSupportedException("Specified stream is not a wave file.");
}
reader.ReadUInt32(); // Riff Chunk Size
string wformat = new string(reader.ReadChars(4));
if (wformat != "WAVE")
{
throw new NotSupportedException("Specified stream is not a wave file.");
}
// WAVE Header
string format_signature = new string(reader.ReadChars(4));
while (format_signature != "fmt ")
{
reader.ReadBytes(reader.ReadInt32());
format_signature = new string(reader.ReadChars(4));
}
int format_chunk_size = reader.ReadInt32();
wFormatTag = reader.ReadUInt16();
nChannels = reader.ReadUInt16();
nSamplesPerSec = reader.ReadUInt32();
nAvgBytesPerSec = reader.ReadUInt32();
nBlockAlign = reader.ReadUInt16();
wBitsPerSample = reader.ReadUInt16();
// Reads residual bytes
if (format_chunk_size > 16)
{
reader.ReadBytes(format_chunk_size - 16);
}
// data Signature
string data_signature = new string(reader.ReadChars(4));
while (data_signature.ToLowerInvariant() != "data")
{
reader.ReadBytes(reader.ReadInt32());
data_signature = new string(reader.ReadChars(4));
}
if (data_signature != "data")
{
throw new NotSupportedException("Specified wave file is not supported.");
}
int waveDataLength = reader.ReadInt32();
var waveDataBuffer = NativeMemory.Alloc((nuint) waveDataLength);
var waveDataSpan = new Span<byte>(waveDataBuffer, waveDataLength);
stream.ReadExactly(waveDataSpan);
var format = new Format
{
Tag = (FormatTag) wFormatTag,
BitsPerSample = wBitsPerSample,
Channels = nChannels,
SampleRate = nSamplesPerSec
};
return new AudioBuffer(
device,
format,
(nint) waveDataBuffer,
(uint) waveDataLength,
true
);
}
}
}

View File

@ -1,31 +1,54 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
namespace MoonWorks.Audio
{
/// <summary>
/// AudioDevice manages all audio-related concerns.
/// </summary>
public class AudioDevice : IDisposable
{
public IntPtr Handle { get; }
public byte[] Handle3D { get; }
public IntPtr MasteringVoice { get; }
public FAudio.FAudioDeviceDetails DeviceDetails { get; }
public IntPtr ReverbVoice { get; }
private IntPtr trueMasteringVoice;
// this is a fun little trick where we use a submix voice as a "faux" mastering voice
// this lets us maintain API consistency for effects like panning and reverb
private SubmixVoice fauxMasteringVoice;
public SubmixVoice MasteringVoice => fauxMasteringVoice;
public float CurveDistanceScalar = 1f;
public float DopplerScale = 1f;
public float SpeedOfSound = 343.5f;
internal FAudio.FAudioVoiceSends ReverbSends;
private readonly HashSet<GCHandle> resourceHandles = new HashSet<GCHandle>();
private readonly HashSet<UpdatingSourceVoice> updatingSourceVoices = new HashSet<UpdatingSourceVoice>();
private readonly List<WeakReference<AudioResource>> resources = new List<WeakReference<AudioResource>>();
private readonly List<WeakReference<StreamingSound>> streamingSounds = new List<WeakReference<StreamingSound>>();
private AudioTweenManager AudioTweenManager;
private bool IsDisposed;
private SourceVoicePool VoicePool;
private List<SourceVoice> VoicesToReturn = new List<SourceVoice>();
public unsafe AudioDevice()
private const int Step = 200;
private TimeSpan UpdateInterval;
private System.Diagnostics.Stopwatch TickStopwatch = new System.Diagnostics.Stopwatch();
private long previousTickTime;
private Thread Thread;
private AutoResetEvent WakeSignal;
internal readonly object StateLock = new object();
private bool Running;
public bool IsDisposed { get; private set; }
internal unsafe AudioDevice()
{
FAudio.FAudioCreate(out var handle, 0, 0);
UpdateInterval = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / Step);
FAudio.FAudioCreate(out var handle, 0, FAudio.FAUDIO_DEFAULT_PROCESSOR);
Handle = handle;
/* Find a suitable device */
@ -35,8 +58,8 @@ namespace MoonWorks.Audio
if (devices == 0)
{
Logger.LogError("No audio devices found!");
Handle = IntPtr.Zero;
FAudio.FAudio_Release(Handle);
Handle = IntPtr.Zero;
return;
}
@ -69,25 +92,24 @@ namespace MoonWorks.Audio
}
/* Init Mastering Voice */
IntPtr masteringVoice;
if (FAudio.FAudio_CreateMasteringVoice(
var result = FAudio.FAudio_CreateMasteringVoice(
Handle,
out masteringVoice,
out trueMasteringVoice,
FAudio.FAUDIO_DEFAULT_CHANNELS,
FAudio.FAUDIO_DEFAULT_SAMPLERATE,
0,
i,
IntPtr.Zero
) != 0)
);
if (result != 0)
{
Logger.LogError("No mastering voice found!");
Handle = IntPtr.Zero;
FAudio.FAudio_Release(Handle);
Logger.LogError("Failed to create a mastering voice!");
Logger.LogError("Audio device creation failed!");
return;
}
MasteringVoice = masteringVoice;
fauxMasteringVoice = SubmixVoice.CreateFauxMasteringVoice(this);
/* Init 3D Audio */
@ -98,144 +120,169 @@ namespace MoonWorks.Audio
Handle3D
);
/* Init reverb */
AudioTweenManager = new AudioTweenManager();
VoicePool = new SourceVoicePool(this);
IntPtr reverbVoice;
WakeSignal = new AutoResetEvent(true);
IntPtr reverb;
FAudio.FAudioCreateReverb(out reverb, 0);
Thread = new Thread(ThreadMain);
Thread.IsBackground = true;
Thread.Start();
IntPtr chainPtr;
chainPtr = Marshal.AllocHGlobal(
sizeof(FAudio.FAudioEffectChain)
);
Running = true;
FAudio.FAudioEffectChain* reverbChain = (FAudio.FAudioEffectChain*) chainPtr;
reverbChain->EffectCount = 1;
reverbChain->pEffectDescriptors = Marshal.AllocHGlobal(
sizeof(FAudio.FAudioEffectDescriptor)
);
FAudio.FAudioEffectDescriptor* reverbDescriptor =
(FAudio.FAudioEffectDescriptor*) reverbChain->pEffectDescriptors;
reverbDescriptor->InitialState = 1;
reverbDescriptor->OutputChannels = (uint) (
(DeviceDetails.OutputFormat.Format.nChannels == 6) ? 6 : 1
);
reverbDescriptor->pEffect = reverb;
FAudio.FAudio_CreateSubmixVoice(
Handle,
out reverbVoice,
1, /* omnidirectional reverb */
DeviceDetails.OutputFormat.Format.nSamplesPerSec,
0,
0,
IntPtr.Zero,
chainPtr
);
FAudio.FAPOBase_Release(reverb);
Marshal.FreeHGlobal(reverbChain->pEffectDescriptors);
Marshal.FreeHGlobal(chainPtr);
ReverbVoice = reverbVoice;
/* Init reverb params */
// Defaults based on FAUDIOFX_I3DL2_PRESET_GENERIC
IntPtr reverbParamsPtr = Marshal.AllocHGlobal(
sizeof(FAudio.FAudioFXReverbParameters)
);