diff --git a/README.md b/README.md index fa09d8e..a22cd66 100644 --- a/README.md +++ b/README.md @@ -72,21 +72,21 @@ package main import "github.com/gen2brain/raylib-go/raylib" func main() { - raylib.InitWindow(800, 450, "raylib [core] example - basic window") + rl.InitWindow(800, 450, "raylib [core] example - basic window") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("Congrats! You created your first window!", 190, 200, 20, raylib.LightGray) + rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LightGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } ``` diff --git a/easings/easings.go b/easings/easings.go index 6959c23..5004e88 100644 --- a/easings/easings.go +++ b/easings/easings.go @@ -10,21 +10,25 @@ import ( // Linear Easing functions // LinearNone easing +// t: current time, b: begInnIng value, c: change In value, d: duration func LinearNone(t, b, c, d float32) float32 { return c*t/d + b } // LinearIn easing +// t: current time, b: begInnIng value, c: change In value, d: duration func LinearIn(t, b, c, d float32) float32 { return c*t/d + b } // LinearOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func LinearOut(t, b, c, d float32) float32 { return c*t/d + b } // LinearInOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func LinearInOut(t, b, c, d float32) float32 { return c*t/d + b } @@ -32,16 +36,19 @@ func LinearInOut(t, b, c, d float32) float32 { // Sine Easing functions // SineIn easing +// t: current time, b: begInnIng value, c: change In value, d: duration func SineIn(t, b, c, d float32) float32 { return -c*float32(math.Cos(float64(t/d)*(math.Pi/2))) + c + b } // SineOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func SineOut(t, b, c, d float32) float32 { return c*float32(math.Sin(float64(t/d)*(math.Pi/2))) + b } // SineInOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func SineInOut(t, b, c, d float32) float32 { return -c/2*(float32(math.Cos(math.Pi*float64(t/d)))-1) + b } @@ -49,17 +56,20 @@ func SineInOut(t, b, c, d float32) float32 { // Circular Easing functions // CircIn easing +// t: current time, b: begInnIng value, c: change In value, d: duration func CircIn(t, b, c, d float32) float32 { t = t / d return -c*(float32(math.Sqrt(float64(1-t*t)))-1) + b } // CircOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func CircOut(t, b, c, d float32) float32 { return c*float32(math.Sqrt(1-float64((t/d-1)*t))) + b } // CircInOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func CircInOut(t, b, c, d float32) float32 { t = t / d * 2 @@ -74,18 +84,21 @@ func CircInOut(t, b, c, d float32) float32 { // Cubic Easing functions // CubicIn easing +// t: current time, b: begInnIng value, c: change In value, d: duration func CubicIn(t, b, c, d float32) float32 { t = t / d return c*t*t*t + b } // CubicOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func CubicOut(t, b, c, d float32) float32 { t = t/d - 1 return c*(t*t*t+1) + b } // CubicInOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func CubicInOut(t, b, c, d float32) float32 { t = t / d * 2 if t < 1 { @@ -99,18 +112,21 @@ func CubicInOut(t, b, c, d float32) float32 { // Quadratic Easing functions // QuadIn easing +// t: current time, b: begInnIng value, c: change In value, d: duration func QuadIn(t, b, c, d float32) float32 { t = t / d return c*t*t + b } // QuadOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func QuadOut(t, b, c, d float32) float32 { t = t / d return (-c*t*(t-2) + b) } // QuadInOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func QuadInOut(t, b, c, d float32) float32 { t = t / d * 2 if t < 1 { @@ -123,6 +139,7 @@ func QuadInOut(t, b, c, d float32) float32 { // Exponential Easing functions // ExpoIn easing +// t: current time, b: begInnIng value, c: change In value, d: duration func ExpoIn(t, b, c, d float32) float32 { if t == 0 { return b @@ -132,6 +149,7 @@ func ExpoIn(t, b, c, d float32) float32 { } // ExpoOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func ExpoOut(t, b, c, d float32) float32 { if t == d { return (b + c) @@ -141,6 +159,7 @@ func ExpoOut(t, b, c, d float32) float32 { } // ExpoInOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func ExpoInOut(t, b, c, d float32) float32 { if t == 0 { return b @@ -162,6 +181,7 @@ func ExpoInOut(t, b, c, d float32) float32 { // Back Easing functions // BackIn easing +// t: current time, b: begInnIng value, c: change In value, d: duration func BackIn(t, b, c, d float32) float32 { s := float32(1.70158) t = t / d @@ -169,6 +189,7 @@ func BackIn(t, b, c, d float32) float32 { } // BackOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func BackOut(t, b, c, d float32) float32 { s := float32(1.70158) t = t/d - 1 @@ -176,6 +197,7 @@ func BackOut(t, b, c, d float32) float32 { } // BackInOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func BackInOut(t, b, c, d float32) float32 { s := float32(1.70158) s = s * 1.525 @@ -192,11 +214,13 @@ func BackInOut(t, b, c, d float32) float32 { // Bounce Easing functions // BounceIn easing +// t: current time, b: begInnIng value, c: change In value, d: duration func BounceIn(t, b, c, d float32) float32 { return (c - BounceOut(d-t, 0, c, d) + b) } // BounceOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func BounceOut(t, b, c, d float32) float32 { t = t / d if t < (1 / 2.75) { @@ -214,6 +238,7 @@ func BounceOut(t, b, c, d float32) float32 { } // BounceInOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func BounceInOut(t, b, c, d float32) float32 { if t < d/2 { return BounceIn(t*2, 0, c, d)*0.5 + b @@ -225,6 +250,7 @@ func BounceInOut(t, b, c, d float32) float32 { // Elastic Easing functions // ElasticIn easing +// t: current time, b: begInnIng value, c: change In value, d: duration func ElasticIn(t, b, c, d float32) float32 { if t == 0 { return b @@ -245,6 +271,7 @@ func ElasticIn(t, b, c, d float32) float32 { } // ElasticOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func ElasticOut(t, b, c, d float32) float32 { if t == 0 { return b @@ -264,6 +291,7 @@ func ElasticOut(t, b, c, d float32) float32 { } // ElasticInOut easing +// t: current time, b: begInnIng value, c: change In value, d: duration func ElasticInOut(t, b, c, d float32) float32 { if t == 0 { return b diff --git a/examples/audio/module_playing/main.go b/examples/audio/module_playing/main.go index 18d18c3..bf03840 100644 --- a/examples/audio/module_playing/main.go +++ b/examples/audio/module_playing/main.go @@ -7,27 +7,27 @@ import ( const maxCircles = 64 type circleWave struct { - Position raylib.Vector2 + Position rl.Vector2 Radius float32 Alpha float32 Speed float32 - Color raylib.Color + Color rl.Color } func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.SetConfigFlags(raylib.FlagMsaa4xHint) // NOTE: Try to enable MSAA 4X + rl.SetConfigFlags(rl.FlagMsaa4xHint) // NOTE: Try to enable MSAA 4X - raylib.InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing (streaming)") + rl.InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing (streaming)") - raylib.InitAudioDevice() + rl.InitAudioDevice() - colors := []raylib.Color{ - raylib.Orange, raylib.Red, raylib.Gold, raylib.Lime, raylib.Blue, - raylib.Violet, raylib.Brown, raylib.LightGray, raylib.Pink, - raylib.Yellow, raylib.Green, raylib.SkyBlue, raylib.Purple, raylib.Beige, + colors := []rl.Color{ + rl.Orange, rl.Red, rl.Gold, rl.Lime, rl.Blue, + rl.Violet, rl.Brown, rl.LightGray, rl.Pink, + rl.Yellow, rl.Green, rl.SkyBlue, rl.Purple, rl.Beige, } circles := make([]circleWave, maxCircles) @@ -36,47 +36,47 @@ func main() { c := circleWave{} c.Alpha = 0 - c.Radius = float32(raylib.GetRandomValue(10, 40)) + c.Radius = float32(rl.GetRandomValue(10, 40)) - x := raylib.GetRandomValue(int32(c.Radius), screenWidth-int32(c.Radius)) - y := raylib.GetRandomValue(int32(c.Radius), screenHeight-int32(c.Radius)) - c.Position = raylib.NewVector2(float32(x), float32(y)) + x := rl.GetRandomValue(int32(c.Radius), screenWidth-int32(c.Radius)) + y := rl.GetRandomValue(int32(c.Radius), screenHeight-int32(c.Radius)) + c.Position = rl.NewVector2(float32(x), float32(y)) - c.Speed = float32(raylib.GetRandomValue(1, 100)) / 20000.0 - c.Color = colors[raylib.GetRandomValue(0, int32(len(colors)-1))] + c.Speed = float32(rl.GetRandomValue(1, 100)) / 20000.0 + c.Color = colors[rl.GetRandomValue(0, int32(len(colors)-1))] circles[i] = c } - xm := raylib.LoadMusicStream("mini1111.xm") - raylib.PlayMusicStream(xm) + xm := rl.LoadMusicStream("mini1111.xm") + rl.PlayMusicStream(xm) pause := false - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.UpdateMusicStream(xm) // Update music buffer with new stream data + for !rl.WindowShouldClose() { + rl.UpdateMusicStream(xm) // Update music buffer with new stream data // Restart music playing (stop and play) - if raylib.IsKeyPressed(raylib.KeySpace) { - raylib.StopMusicStream(xm) - raylib.PlayMusicStream(xm) + if rl.IsKeyPressed(rl.KeySpace) { + rl.StopMusicStream(xm) + rl.PlayMusicStream(xm) } // Pause/Resume music playing - if raylib.IsKeyPressed(raylib.KeyP) { + if rl.IsKeyPressed(rl.KeyP) { pause = !pause if pause { - raylib.PauseMusicStream(xm) + rl.PauseMusicStream(xm) } else { - raylib.ResumeMusicStream(xm) + rl.ResumeMusicStream(xm) } } // Get timePlayed scaled to bar dimensions - timePlayed := int32(raylib.GetMusicTimePlayed(xm)/raylib.GetMusicTimeLength(xm)*float32(screenWidth-40)) * 2 + timePlayed := int32(rl.GetMusicTimePlayed(xm)/rl.GetMusicTimeLength(xm)*float32(screenWidth-40)) * 2 // Color circles animation for i := maxCircles - 1; (i >= 0) && !pause; i-- { @@ -89,33 +89,33 @@ func main() { if circles[i].Alpha <= 0.0 { circles[i].Alpha = 0.0 - circles[i].Radius = float32(raylib.GetRandomValue(10, 40)) - circles[i].Position.X = float32(raylib.GetRandomValue(int32(circles[i].Radius), screenWidth-int32(circles[i].Radius))) - circles[i].Position.Y = float32(raylib.GetRandomValue(int32(circles[i].Radius), screenHeight-int32(circles[i].Radius))) - circles[i].Color = colors[raylib.GetRandomValue(0, 13)] - circles[i].Speed = float32(raylib.GetRandomValue(1, 100)) / 20000.0 + circles[i].Radius = float32(rl.GetRandomValue(10, 40)) + circles[i].Position.X = float32(rl.GetRandomValue(int32(circles[i].Radius), screenWidth-int32(circles[i].Radius))) + circles[i].Position.Y = float32(rl.GetRandomValue(int32(circles[i].Radius), screenHeight-int32(circles[i].Radius))) + circles[i].Color = colors[rl.GetRandomValue(0, 13)] + circles[i].Speed = float32(rl.GetRandomValue(1, 100)) / 20000.0 } } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) for i := maxCircles - 1; i >= 0; i-- { - raylib.DrawCircleV(circles[i].Position, float32(circles[i].Radius), raylib.Fade(circles[i].Color, circles[i].Alpha)) + rl.DrawCircleV(circles[i].Position, float32(circles[i].Radius), rl.Fade(circles[i].Color, circles[i].Alpha)) } // Draw time bar - raylib.DrawRectangle(20, screenHeight-20-12, screenWidth-40, 12, raylib.LightGray) - raylib.DrawRectangle(20, screenHeight-20-12, timePlayed, 12, raylib.Maroon) - raylib.DrawRectangleLines(20, screenHeight-20-12, screenWidth-40, 12, raylib.Gray) + rl.DrawRectangle(20, screenHeight-20-12, screenWidth-40, 12, rl.LightGray) + rl.DrawRectangle(20, screenHeight-20-12, timePlayed, 12, rl.Maroon) + rl.DrawRectangleLines(20, screenHeight-20-12, screenWidth-40, 12, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadMusicStream(xm) + rl.UnloadMusicStream(xm) - raylib.CloseAudioDevice() + rl.CloseAudioDevice() - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/audio/music_stream/main.go b/examples/audio/music_stream/main.go index a4af68f..5b5f27e 100644 --- a/examples/audio/music_stream/main.go +++ b/examples/audio/music_stream/main.go @@ -5,56 +5,56 @@ import ( ) func main() { - raylib.InitWindow(800, 450, "raylib [audio] example - music playing (streaming)") - raylib.InitAudioDevice() + rl.InitWindow(800, 450, "raylib [audio] example - music playing (streaming)") + rl.InitAudioDevice() - music := raylib.LoadMusicStream("guitar_noodling.ogg") + music := rl.LoadMusicStream("guitar_noodling.ogg") pause := false - raylib.PlayMusicStream(music) + rl.PlayMusicStream(music) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.UpdateMusicStream(music) // Update music buffer with new stream data + for !rl.WindowShouldClose() { + rl.UpdateMusicStream(music) // Update music buffer with new stream data // Restart music playing (stop and play) - if raylib.IsKeyPressed(raylib.KeySpace) { - raylib.StopMusicStream(music) - raylib.PlayMusicStream(music) + if rl.IsKeyPressed(rl.KeySpace) { + rl.StopMusicStream(music) + rl.PlayMusicStream(music) } // Pause/Resume music playing - if raylib.IsKeyPressed(raylib.KeyP) { + if rl.IsKeyPressed(rl.KeyP) { pause = !pause if pause { - raylib.PauseMusicStream(music) + rl.PauseMusicStream(music) } else { - raylib.ResumeMusicStream(music) + rl.ResumeMusicStream(music) } } // Get timePlayed scaled to bar dimensions (400 pixels) - timePlayed := raylib.GetMusicTimePlayed(music) / raylib.GetMusicTimeLength(music) * 100 * 4 + timePlayed := rl.GetMusicTimePlayed(music) / rl.GetMusicTimeLength(music) * 100 * 4 - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) - raylib.DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, raylib.LightGray) + rl.ClearBackground(rl.RayWhite) + rl.DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, rl.LightGray) - raylib.DrawRectangle(200, 200, 400, 12, raylib.LightGray) - raylib.DrawRectangle(200, 200, int32(timePlayed), 12, raylib.Maroon) - raylib.DrawRectangleLines(200, 200, 400, 12, raylib.Gray) + rl.DrawRectangle(200, 200, 400, 12, rl.LightGray) + rl.DrawRectangle(200, 200, int32(timePlayed), 12, rl.Maroon) + rl.DrawRectangleLines(200, 200, 400, 12, rl.Gray) - raylib.DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, raylib.LightGray) - raylib.DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, raylib.LightGray) + rl.DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, rl.LightGray) + rl.DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, rl.LightGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadMusicStream(music) // Unload music stream buffers from RAM - raylib.CloseAudioDevice() // Close audio device (music streaming is automatically stopped) + rl.UnloadMusicStream(music) // Unload music stream buffers from RAM + rl.CloseAudioDevice() // Close audio device (music streaming is automatically stopped) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/audio/raw_stream/main.go b/examples/audio/raw_stream/main.go index 39870f9..6fd7afa 100644 --- a/examples/audio/raw_stream/main.go +++ b/examples/audio/raw_stream/main.go @@ -12,34 +12,34 @@ const ( ) func main() { - raylib.InitWindow(800, 450, "raylib [audio] example - raw audio streaming") + rl.InitWindow(800, 450, "raylib [audio] example - raw audio streaming") - raylib.InitAudioDevice() + rl.InitAudioDevice() // Init raw audio stream (sample rate: 22050, sample size: 32bit-float, channels: 1-mono) - stream := raylib.InitAudioStream(22050, 32, 1) + stream := rl.InitAudioStream(22050, 32, 1) //// Fill audio stream with some samples (sine wave) data := make([]float32, maxSamples) for i := 0; i < maxSamples; i++ { - data[i] = float32(math.Sin(float64((2*raylib.Pi*float32(i))/2) * raylib.Deg2rad)) + data[i] = float32(math.Sin(float64((2*rl.Pi*float32(i))/2) * rl.Deg2rad)) } // NOTE: The generated MAX_SAMPLES do not fit to close a perfect loop // for that reason, there is a clip everytime audio stream is looped - raylib.PlayAudioStream(stream) + rl.PlayAudioStream(stream) totalSamples := int32(maxSamples) samplesLeft := int32(totalSamples) - position := raylib.NewVector2(0, 0) + position := rl.NewVector2(0, 0) - raylib.SetTargetFPS(30) + rl.SetTargetFPS(30) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Refill audio stream if required - if raylib.IsAudioBufferProcessed(stream) { + if rl.IsAudioBufferProcessed(stream) { numSamples := int32(0) if samplesLeft >= maxSamplesPerUpdate { numSamples = maxSamplesPerUpdate @@ -47,7 +47,7 @@ func main() { numSamples = samplesLeft } - raylib.UpdateAudioStream(stream, data[totalSamples-samplesLeft:], numSamples) + rl.UpdateAudioStream(stream, data[totalSamples-samplesLeft:], numSamples) samplesLeft -= numSamples @@ -57,25 +57,25 @@ func main() { } } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) - raylib.DrawText("SINE WAVE SHOULD BE PLAYING!", 240, 140, 20, raylib.LightGray) + rl.ClearBackground(rl.RayWhite) + rl.DrawText("SINE WAVE SHOULD BE PLAYING!", 240, 140, 20, rl.LightGray) // NOTE: Draw a part of the sine wave (only screen width) - for i := 0; i < int(raylib.GetScreenWidth()); i++ { + for i := 0; i < int(rl.GetScreenWidth()); i++ { position.X = float32(i) position.Y = 250 + 50*data[i] - raylib.DrawPixelV(position, raylib.Red) + rl.DrawPixelV(position, rl.Red) } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseAudioStream(stream) // Close raw audio stream and delete buffers from RAM + rl.CloseAudioStream(stream) // Close raw audio stream and delete buffers from RAM - raylib.CloseAudioDevice() // Close audio device (music streaming is automatically stopped) + rl.CloseAudioDevice() // Close audio device (music streaming is automatically stopped) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/audio/sound_loading/main.go b/examples/audio/sound_loading/main.go index 64a54a5..c9a8713 100644 --- a/examples/audio/sound_loading/main.go +++ b/examples/audio/sound_loading/main.go @@ -5,37 +5,37 @@ import ( ) func main() { - raylib.InitWindow(800, 450, "raylib [audio] example - sound loading and playing") + rl.InitWindow(800, 450, "raylib [audio] example - sound loading and playing") - raylib.InitAudioDevice() + rl.InitAudioDevice() - fxWav := raylib.LoadSound("weird.wav") - fxOgg := raylib.LoadSound("tanatana.ogg") + fxWav := rl.LoadSound("weird.wav") + fxOgg := rl.LoadSound("tanatana.ogg") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsKeyPressed(raylib.KeySpace) { - raylib.PlaySound(fxWav) + for !rl.WindowShouldClose() { + if rl.IsKeyPressed(rl.KeySpace) { + rl.PlaySound(fxWav) } - if raylib.IsKeyPressed(raylib.KeyEnter) { - raylib.PlaySound(fxOgg) + if rl.IsKeyPressed(rl.KeyEnter) { + rl.PlaySound(fxOgg) } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, raylib.LightGray) - raylib.DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, raylib.LightGray) + rl.DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, rl.LightGray) + rl.DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, rl.LightGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadSound(fxWav) - raylib.UnloadSound(fxOgg) + rl.UnloadSound(fxWav) + rl.UnloadSound(fxOgg) - raylib.CloseAudioDevice() + rl.CloseAudioDevice() - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/2d_camera/main.go b/examples/core/2d_camera/main.go index 82471e7..e0aa19f 100644 --- a/examples/core/2d_camera/main.go +++ b/examples/core/2d_camera/main.go @@ -12,54 +12,54 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera") + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera") - player := raylib.NewRectangle(400, 280, 40, 40) + player := rl.NewRectangle(400, 280, 40, 40) - buildings := make([]raylib.Rectangle, maxBuildings) - buildColors := make([]raylib.Color, maxBuildings) + buildings := make([]rl.Rectangle, maxBuildings) + buildColors := make([]rl.Color, maxBuildings) spacing := float32(0) for i := 0; i < maxBuildings; i++ { - r := raylib.Rectangle{} - r.Width = float32(raylib.GetRandomValue(50, 200)) - r.Height = float32(raylib.GetRandomValue(100, 800)) + r := rl.Rectangle{} + r.Width = float32(rl.GetRandomValue(50, 200)) + r.Height = float32(rl.GetRandomValue(100, 800)) r.Y = float32(screenHeight) - 130 - r.Height r.X = -6000 + spacing spacing += r.Width - c := raylib.NewColor(byte(raylib.GetRandomValue(200, 240)), byte(raylib.GetRandomValue(200, 240)), byte(raylib.GetRandomValue(200, 250)), byte(255)) + c := rl.NewColor(byte(rl.GetRandomValue(200, 240)), byte(rl.GetRandomValue(200, 240)), byte(rl.GetRandomValue(200, 250)), byte(255)) buildings[i] = r buildColors[i] = c } - camera := raylib.Camera2D{} - camera.Target = raylib.NewVector2(float32(player.X+20), float32(player.Y+20)) - camera.Offset = raylib.NewVector2(0, 0) + camera := rl.Camera2D{} + camera.Target = rl.NewVector2(float32(player.X+20), float32(player.Y+20)) + camera.Offset = rl.NewVector2(0, 0) camera.Rotation = 0.0 camera.Zoom = 1.0 - raylib.SetTargetFPS(30) + rl.SetTargetFPS(30) - for !raylib.WindowShouldClose() { - if raylib.IsKeyDown(raylib.KeyRight) { + for !rl.WindowShouldClose() { + if rl.IsKeyDown(rl.KeyRight) { player.X += 2 // Player movement camera.Offset.X -= 2 // Camera displacement with player movement - } else if raylib.IsKeyDown(raylib.KeyLeft) { + } else if rl.IsKeyDown(rl.KeyLeft) { player.X -= 2 // Player movement camera.Offset.X += 2 // Camera displacement with player movement } // Camera target follows player - camera.Target = raylib.NewVector2(float32(player.X+20), float32(player.Y+20)) + camera.Target = rl.NewVector2(float32(player.X+20), float32(player.Y+20)) // Camera rotation controls - if raylib.IsKeyDown(raylib.KeyA) { + if rl.IsKeyDown(rl.KeyA) { camera.Rotation-- - } else if raylib.IsKeyDown(raylib.KeyS) { + } else if rl.IsKeyDown(rl.KeyS) { camera.Rotation++ } @@ -71,7 +71,7 @@ func main() { } // Camera zoom controls - camera.Zoom += float32(raylib.GetMouseWheelMove()) * 0.05 + camera.Zoom += float32(rl.GetMouseWheelMove()) * 0.05 if camera.Zoom > 3.0 { camera.Zoom = 3.0 @@ -80,48 +80,48 @@ func main() { } // Camera reset (zoom and rotation) - if raylib.IsKeyPressed(raylib.KeyR) { + if rl.IsKeyPressed(rl.KeyR) { camera.Zoom = 1.0 camera.Rotation = 0.0 } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode2D(camera) + rl.BeginMode2D(camera) - raylib.DrawRectangle(-6000, 320, 13000, 8000, raylib.DarkGray) + rl.DrawRectangle(-6000, 320, 13000, 8000, rl.DarkGray) for i := 0; i < maxBuildings; i++ { - raylib.DrawRectangleRec(buildings[i], buildColors[i]) + rl.DrawRectangleRec(buildings[i], buildColors[i]) } - raylib.DrawRectangleRec(player, raylib.Red) + rl.DrawRectangleRec(player, rl.Red) - raylib.DrawRectangle(int32(camera.Target.X), -500, 1, screenHeight*4, raylib.Green) - raylib.DrawRectangle(-500, int32(camera.Target.Y), screenWidth*4, 1, raylib.Green) + rl.DrawRectangle(int32(camera.Target.X), -500, 1, screenHeight*4, rl.Green) + rl.DrawRectangle(-500, int32(camera.Target.Y), screenWidth*4, 1, rl.Green) - raylib.EndMode2D() + rl.EndMode2D() - raylib.DrawText("SCREEN AREA", 640, 10, 20, raylib.Red) + rl.DrawText("SCREEN AREA", 640, 10, 20, rl.Red) - raylib.DrawRectangle(0, 0, screenWidth, 5, raylib.Red) - raylib.DrawRectangle(0, 5, 5, screenHeight-10, raylib.Red) - raylib.DrawRectangle(screenWidth-5, 5, 5, screenHeight-10, raylib.Red) - raylib.DrawRectangle(0, screenHeight-5, screenWidth, 5, raylib.Red) + rl.DrawRectangle(0, 0, screenWidth, 5, rl.Red) + rl.DrawRectangle(0, 5, 5, screenHeight-10, rl.Red) + rl.DrawRectangle(screenWidth-5, 5, 5, screenHeight-10, rl.Red) + rl.DrawRectangle(0, screenHeight-5, screenWidth, 5, rl.Red) - raylib.DrawRectangle(10, 10, 250, 113, raylib.Fade(raylib.SkyBlue, 0.5)) - raylib.DrawRectangleLines(10, 10, 250, 113, raylib.Blue) + rl.DrawRectangle(10, 10, 250, 113, rl.Fade(rl.SkyBlue, 0.5)) + rl.DrawRectangleLines(10, 10, 250, 113, rl.Blue) - raylib.DrawText("Free 2d camera controls:", 20, 20, 10, raylib.Black) - raylib.DrawText("- Right/Left to move Offset", 40, 40, 10, raylib.DarkGray) - raylib.DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, raylib.DarkGray) - raylib.DrawText("- A / S to Rotate", 40, 80, 10, raylib.DarkGray) - raylib.DrawText("- R to reset Zoom and Rotation", 40, 100, 10, raylib.DarkGray) + rl.DrawText("Free 2d camera controls:", 20, 20, 10, rl.Black) + rl.DrawText("- Right/Left to move Offset", 40, 40, 10, rl.DarkGray) + rl.DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, rl.DarkGray) + rl.DrawText("- A / S to Rotate", 40, 80, 10, rl.DarkGray) + rl.DrawText("- R to reset Zoom and Rotation", 40, 100, 10, rl.DarkGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/3d_camera_first_person/main.go b/examples/core/3d_camera_first_person/main.go index 24a8170..8ea9c11 100644 --- a/examples/core/3d_camera_first_person/main.go +++ b/examples/core/3d_camera_first_person/main.go @@ -9,61 +9,61 @@ const ( ) func main() { - raylib.InitWindow(800, 450, "raylib [core] example - 3d camera first person") + rl.InitWindow(800, 450, "raylib [core] example - 3d camera first person") - camera := raylib.Camera3D{} - camera.Position = raylib.NewVector3(4.0, 2.0, 4.0) - camera.Target = raylib.NewVector3(0.0, 1.8, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera3D{} + camera.Position = rl.NewVector3(4.0, 2.0, 4.0) + camera.Target = rl.NewVector3(0.0, 1.8, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 60.0 - camera.Type = raylib.CameraPerspective + camera.Type = rl.CameraPerspective // Generates some random columns heights := make([]float32, maxColumns) - positions := make([]raylib.Vector3, maxColumns) - colors := make([]raylib.Color, maxColumns) + positions := make([]rl.Vector3, maxColumns) + colors := make([]rl.Color, maxColumns) for i := 0; i < maxColumns; i++ { - heights[i] = float32(raylib.GetRandomValue(1, 12)) - positions[i] = raylib.NewVector3(float32(raylib.GetRandomValue(-15, 15)), heights[i]/2, float32(raylib.GetRandomValue(-15, 15))) - colors[i] = raylib.NewColor(uint8(raylib.GetRandomValue(20, 255)), uint8(raylib.GetRandomValue(10, 55)), 30, 255) + heights[i] = float32(rl.GetRandomValue(1, 12)) + positions[i] = rl.NewVector3(float32(rl.GetRandomValue(-15, 15)), heights[i]/2, float32(rl.GetRandomValue(-15, 15))) + colors[i] = rl.NewColor(uint8(rl.GetRandomValue(20, 255)), uint8(rl.GetRandomValue(10, 55)), 30, 255) } - raylib.SetCameraMode(camera, raylib.CameraFirstPerson) // Set a first person camera mode + rl.SetCameraMode(camera, rl.CameraFirstPerson) // Set a first person camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.UpdateCamera(&camera) // Update camera + for !rl.WindowShouldClose() { + rl.UpdateCamera(&camera) // Update camera - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawPlane(raylib.NewVector3(0.0, 0.0, 0.0), raylib.NewVector2(32.0, 32.0), raylib.LightGray) // Draw ground - raylib.DrawCube(raylib.NewVector3(-16.0, 2.5, 0.0), 1.0, 5.0, 32.0, raylib.Blue) // Draw a blue wall - raylib.DrawCube(raylib.NewVector3(16.0, 2.5, 0.0), 1.0, 5.0, 32.0, raylib.Lime) // Draw a green wall - raylib.DrawCube(raylib.NewVector3(0.0, 2.5, 16.0), 32.0, 5.0, 1.0, raylib.Gold) // Draw a yellow wall + rl.DrawPlane(rl.NewVector3(0.0, 0.0, 0.0), rl.NewVector2(32.0, 32.0), rl.LightGray) // Draw ground + rl.DrawCube(rl.NewVector3(-16.0, 2.5, 0.0), 1.0, 5.0, 32.0, rl.Blue) // Draw a blue wall + rl.DrawCube(rl.NewVector3(16.0, 2.5, 0.0), 1.0, 5.0, 32.0, rl.Lime) // Draw a green wall + rl.DrawCube(rl.NewVector3(0.0, 2.5, 16.0), 32.0, 5.0, 1.0, rl.Gold) // Draw a yellow wall // Draw some cubes around for i := 0; i < maxColumns; i++ { - raylib.DrawCube(positions[i], 2.0, heights[i], 2.0, colors[i]) - raylib.DrawCubeWires(positions[i], 2.0, heights[i], 2.0, raylib.Maroon) + rl.DrawCube(positions[i], 2.0, heights[i], 2.0, colors[i]) + rl.DrawCubeWires(positions[i], 2.0, heights[i], 2.0, rl.Maroon) } - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawRectangle(10, 10, 220, 70, raylib.Fade(raylib.SkyBlue, 0.5)) - raylib.DrawRectangleLines(10, 10, 220, 70, raylib.Blue) + rl.DrawRectangle(10, 10, 220, 70, rl.Fade(rl.SkyBlue, 0.5)) + rl.DrawRectangleLines(10, 10, 220, 70, rl.Blue) - raylib.DrawText("First person camera default controls:", 20, 20, 10, raylib.Black) - raylib.DrawText("- Move with keys: W, A, S, D", 40, 40, 10, raylib.DarkGray) - raylib.DrawText("- Mouse move to look around", 40, 60, 10, raylib.DarkGray) + rl.DrawText("First person camera default controls:", 20, 20, 10, rl.Black) + rl.DrawText("- Move with keys: W, A, S, D", 40, 40, 10, rl.DarkGray) + rl.DrawText("- Mouse move to look around", 40, 60, 10, rl.DarkGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/3d_camera_free/main.go b/examples/core/3d_camera_free/main.go index dd82774..3ae7e3c 100644 --- a/examples/core/3d_camera_free/main.go +++ b/examples/core/3d_camera_free/main.go @@ -5,53 +5,53 @@ import ( ) func main() { - raylib.InitWindow(800, 450, "raylib [core] example - 3d camera free") + rl.InitWindow(800, 450, "raylib [core] example - 3d camera free") - camera := raylib.Camera3D{} - camera.Position = raylib.NewVector3(10.0, 10.0, 10.0) - camera.Target = raylib.NewVector3(0.0, 0.0, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera3D{} + camera.Position = rl.NewVector3(10.0, 10.0, 10.0) + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - camera.Type = raylib.CameraPerspective + camera.Type = rl.CameraPerspective - cubePosition := raylib.NewVector3(0.0, 0.0, 0.0) + cubePosition := rl.NewVector3(0.0, 0.0, 0.0) - raylib.SetCameraMode(camera, raylib.CameraFree) // Set a free camera mode + rl.SetCameraMode(camera, rl.CameraFree) // Set a free camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.UpdateCamera(&camera) // Update camera + for !rl.WindowShouldClose() { + rl.UpdateCamera(&camera) // Update camera - if raylib.IsKeyDown(raylib.KeyZ) { - camera.Target = raylib.NewVector3(0.0, 0.0, 0.0) + if rl.IsKeyDown(rl.KeyZ) { + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawCube(cubePosition, 2.0, 2.0, 2.0, raylib.Red) - raylib.DrawCubeWires(cubePosition, 2.0, 2.0, 2.0, raylib.Maroon) + rl.DrawCube(cubePosition, 2.0, 2.0, 2.0, rl.Red) + rl.DrawCubeWires(cubePosition, 2.0, 2.0, 2.0, rl.Maroon) - raylib.DrawGrid(10, 1.0) + rl.DrawGrid(10, 1.0) - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawRectangle(10, 10, 320, 133, raylib.Fade(raylib.SkyBlue, 0.5)) - raylib.DrawRectangleLines(10, 10, 320, 133, raylib.Blue) + rl.DrawRectangle(10, 10, 320, 133, rl.Fade(rl.SkyBlue, 0.5)) + rl.DrawRectangleLines(10, 10, 320, 133, rl.Blue) - raylib.DrawText("Free camera default controls:", 20, 20, 10, raylib.Black) - raylib.DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, raylib.DarkGray) - raylib.DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, raylib.DarkGray) - raylib.DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, raylib.DarkGray) - raylib.DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, raylib.DarkGray) - raylib.DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, raylib.DarkGray) + rl.DrawText("Free camera default controls:", 20, 20, 10, rl.Black) + rl.DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, rl.DarkGray) + rl.DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, rl.DarkGray) + rl.DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, rl.DarkGray) + rl.DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, rl.DarkGray) + rl.DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, rl.DarkGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/3d_mode/main.go b/examples/core/3d_mode/main.go index 034c858..0eac794 100644 --- a/examples/core/3d_mode/main.go +++ b/examples/core/3d_mode/main.go @@ -5,39 +5,39 @@ import ( ) func main() { - raylib.InitWindow(800, 450, "raylib [core] example - 3d mode") + rl.InitWindow(800, 450, "raylib [core] example - 3d mode") - camera := raylib.Camera3D{} - camera.Position = raylib.NewVector3(0.0, 10.0, 10.0) - camera.Target = raylib.NewVector3(0.0, 0.0, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera3D{} + camera.Position = rl.NewVector3(0.0, 10.0, 10.0) + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - camera.Type = raylib.CameraPerspective + camera.Type = rl.CameraPerspective - cubePosition := raylib.NewVector3(0.0, 0.0, 0.0) + cubePosition := rl.NewVector3(0.0, 0.0, 0.0) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawCube(cubePosition, 2.0, 2.0, 2.0, raylib.Red) - raylib.DrawCubeWires(cubePosition, 2.0, 2.0, 2.0, raylib.Maroon) + rl.DrawCube(cubePosition, 2.0, 2.0, 2.0, rl.Red) + rl.DrawCubeWires(cubePosition, 2.0, 2.0, 2.0, rl.Maroon) - raylib.DrawGrid(10, 1.0) + rl.DrawGrid(10, 1.0) - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawText("Welcome to the third dimension!", 10, 40, 20, raylib.DarkGray) + rl.DrawText("Welcome to the third dimension!", 10, 40, 20, rl.DarkGray) - raylib.DrawFPS(10, 10) + rl.DrawFPS(10, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/3d_picking/main.go b/examples/core/3d_picking/main.go index fd52bb3..4a544bb 100644 --- a/examples/core/3d_picking/main.go +++ b/examples/core/3d_picking/main.go @@ -8,70 +8,71 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d picking") + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d picking") - camera := raylib.Camera3D{} - camera.Position = raylib.NewVector3(10.0, 10.0, 10.0) - camera.Target = raylib.NewVector3(0.0, 0.0, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera3D{} + camera.Position = rl.NewVector3(10.0, 10.0, 10.0) + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - camera.Type = raylib.CameraPerspective + camera.Type = rl.CameraPerspective - cubePosition := raylib.NewVector3(0.0, 1.0, 0.0) - cubeSize := raylib.NewVector3(2.0, 2.0, 2.0) + cubePosition := rl.NewVector3(0.0, 1.0, 0.0) + cubeSize := rl.NewVector3(2.0, 2.0, 2.0) - var ray raylib.Ray + var ray rl.Ray collision := false - raylib.SetCameraMode(camera, raylib.CameraFree) // Set a free camera mode + rl.SetCameraMode(camera, rl.CameraFree) // Set a free camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.UpdateCamera(&camera) // Update camera + for !rl.WindowShouldClose() { + rl.UpdateCamera(&camera) // Update camera - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { - ray = raylib.GetMouseRay(raylib.GetMousePosition(), camera) + if rl.IsMouseButtonPressed(rl.MouseLeftButton) { + // NOTE: This function is NOT WORKING properly! + ray = rl.GetMouseRay(rl.GetMousePosition(), camera) // Check collision between ray and box - min := raylib.NewVector3(cubePosition.X-cubeSize.X/2, cubePosition.Y-cubeSize.Y/2, cubePosition.Z-cubeSize.Z/2) - max := raylib.NewVector3(cubePosition.X+cubeSize.X/2, cubePosition.Y+cubeSize.Y/2, cubePosition.Z+cubeSize.Z/2) - collision = raylib.CheckCollisionRayBox(ray, raylib.NewBoundingBox(min, max)) + min := rl.NewVector3(cubePosition.X-cubeSize.X/2, cubePosition.Y-cubeSize.Y/2, cubePosition.Z-cubeSize.Z/2) + max := rl.NewVector3(cubePosition.X+cubeSize.X/2, cubePosition.Y+cubeSize.Y/2, cubePosition.Z+cubeSize.Z/2) + collision = rl.CheckCollisionRayBox(ray, rl.NewBoundingBox(min, max)) } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) if collision { - raylib.DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, raylib.Red) - raylib.DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, raylib.Maroon) + rl.DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, rl.Red) + rl.DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, rl.Maroon) - raylib.DrawCubeWires(cubePosition, cubeSize.X+0.2, cubeSize.Y+0.2, cubeSize.Z+0.2, raylib.Green) + rl.DrawCubeWires(cubePosition, cubeSize.X+0.2, cubeSize.Y+0.2, cubeSize.Z+0.2, rl.Green) } else { - raylib.DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, raylib.Gray) - raylib.DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, raylib.DarkGray) + rl.DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, rl.Gray) + rl.DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, rl.DarkGray) } - raylib.DrawRay(ray, raylib.Maroon) + rl.DrawRay(ray, rl.Maroon) - raylib.DrawGrid(10, 1.0) + rl.DrawGrid(10, 1.0) - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawText("Try selecting the box with mouse!", 240, 10, 20, raylib.DarkGray) + rl.DrawText("Try selecting the box with mouse!", 240, 10, 20, rl.DarkGray) if collision { - raylib.DrawText("BOX SELECTED", (screenWidth-raylib.MeasureText("BOX SELECTED", 30))/2, int32(float32(screenHeight)*0.1), 30, raylib.Green) + rl.DrawText("BOX SELECTED", (screenWidth-rl.MeasureText("BOX SELECTED", 30))/2, int32(float32(screenHeight)*0.1), 30, rl.Green) } - raylib.DrawFPS(10, 10) + rl.DrawFPS(10, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/basic_window/main.go b/examples/core/basic_window/main.go index 9931223..d28f846 100644 --- a/examples/core/basic_window/main.go +++ b/examples/core/basic_window/main.go @@ -3,20 +3,20 @@ package main import "github.com/gen2brain/raylib-go/raylib" func main() { - raylib.SetConfigFlags(raylib.FlagVsyncHint) - raylib.InitWindow(800, 450, "raylib [core] example - basic window") + rl.SetConfigFlags(rl.FlagVsyncHint) + rl.InitWindow(800, 450, "raylib [core] example - basic window") - //raylib.SetTargetFPS(60) + //rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("Congrats! You created your first window!", 190, 200, 20, raylib.LightGray) + rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LightGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/color_select/main.go b/examples/core/color_select/main.go index aa813ab..dd107d6 100644 --- a/examples/core/color_select/main.go +++ b/examples/core/color_select/main.go @@ -5,19 +5,19 @@ import ( ) func main() { - raylib.InitWindow(800, 450, "raylib [core] example - color selection (collision detection)") + rl.InitWindow(800, 450, "raylib [core] example - color selection (collision detection)") - colors := [21]raylib.Color{ - raylib.DarkGray, raylib.Maroon, raylib.Orange, raylib.DarkGreen, raylib.DarkBlue, raylib.DarkPurple, - raylib.DarkBrown, raylib.Gray, raylib.Red, raylib.Gold, raylib.Lime, raylib.Blue, raylib.Violet, raylib.Brown, - raylib.LightGray, raylib.Pink, raylib.Yellow, raylib.Green, raylib.SkyBlue, raylib.Purple, raylib.Beige, + colors := [21]rl.Color{ + rl.DarkGray, rl.Maroon, rl.Orange, rl.DarkGreen, rl.DarkBlue, rl.DarkPurple, + rl.DarkBrown, rl.Gray, rl.Red, rl.Gold, rl.Lime, rl.Blue, rl.Violet, rl.Brown, + rl.LightGray, rl.Pink, rl.Yellow, rl.Green, rl.SkyBlue, rl.Purple, rl.Beige, } - colorsRecs := make([]raylib.Rectangle, 21) // Rectangles array + colorsRecs := make([]rl.Rectangle, 21) // Rectangles array // Fills colorsRecs data (for every rectangle) for i := 0; i < 21; i++ { - r := raylib.Rectangle{} + r := rl.Rectangle{} r.X = float32(20 + 100*(i%7) + 10*(i%7)) r.Y = float32(60 + 100*(i/7) + 10*(i/7)) r.Width = 100 @@ -28,18 +28,18 @@ func main() { selected := make([]bool, 21) // Selected rectangles indicator - mousePoint := raylib.Vector2{} + mousePoint := rl.Vector2{} - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - mousePoint = raylib.GetMousePosition() + for !rl.WindowShouldClose() { + mousePoint = rl.GetMousePosition() for i := 0; i < 21; i++ { // Iterate along all the rectangles - if raylib.CheckCollisionPointRec(mousePoint, colorsRecs[i]) { + if rl.CheckCollisionPointRec(mousePoint, colorsRecs[i]) { colors[i].A = 120 - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + if rl.IsMouseButtonPressed(rl.MouseLeftButton) { selected[i] = !selected[i] } } else { @@ -47,24 +47,24 @@ func main() { } } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) for i := 0; i < 21; i++ { // Draw all rectangles - raylib.DrawRectangleRec(colorsRecs[i], colors[i]) + rl.DrawRectangleRec(colorsRecs[i], colors[i]) // Draw four rectangles around selected rectangle if selected[i] { - raylib.DrawRectangle(int32(colorsRecs[i].X), int32(colorsRecs[i].Y), 100, 10, raylib.RayWhite) // Square top rectangle - raylib.DrawRectangle(int32(colorsRecs[i].X), int32(colorsRecs[i].Y), 10, 100, raylib.RayWhite) // Square left rectangle - raylib.DrawRectangle(int32(colorsRecs[i].X+90), int32(colorsRecs[i].Y), 10, 100, raylib.RayWhite) // Square right rectangle - raylib.DrawRectangle(int32(colorsRecs[i].X), int32(colorsRecs[i].Y)+90, 100, 10, raylib.RayWhite) // Square bottom rectangle + rl.DrawRectangle(int32(colorsRecs[i].X), int32(colorsRecs[i].Y), 100, 10, rl.RayWhite) // Square top rectangle + rl.DrawRectangle(int32(colorsRecs[i].X), int32(colorsRecs[i].Y), 10, 100, rl.RayWhite) // Square left rectangle + rl.DrawRectangle(int32(colorsRecs[i].X+90), int32(colorsRecs[i].Y), 10, 100, rl.RayWhite) // Square right rectangle + rl.DrawRectangle(int32(colorsRecs[i].X), int32(colorsRecs[i].Y)+90, 100, 10, rl.RayWhite) // Square bottom rectangle } } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/drop_files/main.go b/examples/core/drop_files/main.go index bca28d3..a84ead6 100644 --- a/examples/core/drop_files/main.go +++ b/examples/core/drop_files/main.go @@ -8,43 +8,43 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - drop files") + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - drop files") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) count := int32(0) var droppedFiles []string - for !raylib.WindowShouldClose() { - if raylib.IsFileDropped() { - droppedFiles = raylib.GetDroppedFiles(&count) + for !rl.WindowShouldClose() { + if rl.IsFileDropped() { + droppedFiles = rl.GetDroppedFiles(&count) } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) if count == 0 { - raylib.DrawText("Drop your files to this window!", 100, 40, 20, raylib.DarkGray) + rl.DrawText("Drop your files to this window!", 100, 40, 20, rl.DarkGray) } else { - raylib.DrawText("Dropped files:", 100, 40, 20, raylib.DarkGray) + rl.DrawText("Dropped files:", 100, 40, 20, rl.DarkGray) for i := int32(0); i < count; i++ { if i%2 == 0 { - raylib.DrawRectangle(0, int32(85+40*i), screenWidth, 40, raylib.Fade(raylib.LightGray, 0.5)) + rl.DrawRectangle(0, int32(85+40*i), screenWidth, 40, rl.Fade(rl.LightGray, 0.5)) } else { - raylib.DrawRectangle(0, int32(85+40*i), screenWidth, 40, raylib.Fade(raylib.LightGray, 0.3)) + rl.DrawRectangle(0, int32(85+40*i), screenWidth, 40, rl.Fade(rl.LightGray, 0.3)) } - raylib.DrawText(droppedFiles[i], 120, int32(100+i*40), 10, raylib.Gray) + rl.DrawText(droppedFiles[i], 120, int32(100), 10, rl.Gray) } - raylib.DrawText("Drop new files...", 100, int32(150+count*40), 20, raylib.DarkGray) + rl.DrawText("Drop new files...", 100, int32(150), 20, rl.DarkGray) } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.ClearDroppedFiles() + rl.ClearDroppedFiles() - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/gestures_detection/main.go b/examples/core/gestures_detection/main.go index bcab437..9dc7371 100644 --- a/examples/core/gestures_detection/main.go +++ b/examples/core/gestures_detection/main.go @@ -12,47 +12,47 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - gestures detection") + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - gestures detection") - touchPosition := raylib.NewVector2(0, 0) - touchArea := raylib.NewRectangle(220, 10, float32(screenWidth)-230, float32(screenHeight)-20) + touchPosition := rl.NewVector2(0, 0) + touchArea := rl.NewRectangle(220, 10, float32(screenWidth)-230, float32(screenHeight)-20) gestureStrings := make([]string, 0) - currentGesture := raylib.GestureNone - lastGesture := raylib.GestureNone + currentGesture := rl.GestureNone + lastGesture := rl.GestureNone - //raylib.SetGesturesEnabled(uint32(raylib.GestureHold | raylib.GestureDrag)) // Enable only some gestures to be detected + //rl.SetGesturesEnabled(uint32(rl.GestureHold | rl.GestureDrag)) // Enable only some gestures to be detected - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { lastGesture = currentGesture - currentGesture = raylib.GetGestureDetected() - touchPosition = raylib.GetTouchPosition(0) + currentGesture = rl.GetGestureDetected() + touchPosition = rl.GetTouchPosition(0) - if raylib.CheckCollisionPointRec(touchPosition, touchArea) && currentGesture != raylib.GestureNone { + if rl.CheckCollisionPointRec(touchPosition, touchArea) && currentGesture != rl.GestureNone { if currentGesture != lastGesture { switch currentGesture { - case raylib.GestureTap: + case rl.GestureTap: gestureStrings = append(gestureStrings, "GESTURE TAP") - case raylib.GestureDoubletap: + case rl.GestureDoubletap: gestureStrings = append(gestureStrings, "GESTURE DOUBLETAP") - case raylib.GestureHold: + case rl.GestureHold: gestureStrings = append(gestureStrings, "GESTURE HOLD") - case raylib.GestureDrag: + case rl.GestureDrag: gestureStrings = append(gestureStrings, "GESTURE DRAG") - case raylib.GestureSwipeRight: + case rl.GestureSwipeRight: gestureStrings = append(gestureStrings, "GESTURE SWIPE RIGHT") - case raylib.GestureSwipeLeft: + case rl.GestureSwipeLeft: gestureStrings = append(gestureStrings, "GESTURE SWIPE LEFT") - case raylib.GestureSwipeUp: + case rl.GestureSwipeUp: gestureStrings = append(gestureStrings, "GESTURE SWIPE UP") - case raylib.GestureSwipeDown: + case rl.GestureSwipeDown: gestureStrings = append(gestureStrings, "GESTURE SWIPE DOWN") - case raylib.GesturePinchIn: + case rl.GesturePinchIn: gestureStrings = append(gestureStrings, "GESTURE PINCH IN") - case raylib.GesturePinchOut: + case rl.GesturePinchOut: gestureStrings = append(gestureStrings, "GESTURE PINCH OUT") } @@ -62,38 +62,38 @@ func main() { } } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawRectangleRec(touchArea, raylib.Gray) - raylib.DrawRectangle(225, 15, screenWidth-240, screenHeight-30, raylib.RayWhite) + rl.DrawRectangleRec(touchArea, rl.Gray) + rl.DrawRectangle(225, 15, screenWidth-240, screenHeight-30, rl.RayWhite) - raylib.DrawText("GESTURES TEST AREA", screenWidth-270, screenHeight-40, 20, raylib.Fade(raylib.Gray, 0.5)) + rl.DrawText("GESTURES TEST AREA", screenWidth-270, screenHeight-40, 20, rl.Fade(rl.Gray, 0.5)) for i := 0; i < len(gestureStrings); i++ { if i%2 == 0 { - raylib.DrawRectangle(10, int32(30+20*i), 200, 20, raylib.Fade(raylib.LightGray, 0.5)) + rl.DrawRectangle(10, int32(30+20*i), 200, 20, rl.Fade(rl.LightGray, 0.5)) } else { - raylib.DrawRectangle(10, int32(30+20*i), 200, 20, raylib.Fade(raylib.LightGray, 0.3)) + rl.DrawRectangle(10, int32(30+20*i), 200, 20, rl.Fade(rl.LightGray, 0.3)) } if i < len(gestureStrings)-1 { - raylib.DrawText(gestureStrings[i], 35, int32(36+20*i), 10, raylib.DarkGray) + rl.DrawText(gestureStrings[i], 35, int32(36+20*i), 10, rl.DarkGray) } else { - raylib.DrawText(gestureStrings[i], 35, int32(36+20*i), 10, raylib.Maroon) + rl.DrawText(gestureStrings[i], 35, int32(36+20*i), 10, rl.Maroon) } } - raylib.DrawRectangleLines(10, 29, 200, screenHeight-50, raylib.Gray) - raylib.DrawText("DETECTED GESTURES", 50, 15, 10, raylib.Gray) + rl.DrawRectangleLines(10, 29, 200, screenHeight-50, rl.Gray) + rl.DrawText("DETECTED GESTURES", 50, 15, 10, rl.Gray) - if currentGesture != raylib.GestureNone { - raylib.DrawCircleV(touchPosition, 30, raylib.Maroon) + if currentGesture != rl.GestureNone { + rl.DrawCircleV(touchPosition, 30, rl.Maroon) } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/input_gamepad/main.go b/examples/core/input_gamepad/main.go index 8c2bcce..d4da677 100644 --- a/examples/core/input_gamepad/main.go +++ b/examples/core/input_gamepad/main.go @@ -12,190 +12,190 @@ const ( ) func main() { - raylib.SetConfigFlags(raylib.FlagMsaa4xHint) // Set MSAA 4X hint before windows creation + rl.SetConfigFlags(rl.FlagMsaa4xHint) // Set MSAA 4X hint before windows creation - raylib.InitWindow(800, 450, "raylib [core] example - gamepad input") + rl.InitWindow(800, 450, "raylib [core] example - gamepad input") - texPs3Pad := raylib.LoadTexture("ps3.png") - texXboxPad := raylib.LoadTexture("xbox.png") + texPs3Pad := rl.LoadTexture("ps3.png") + texXboxPad := rl.LoadTexture("xbox.png") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - if raylib.IsGamepadAvailable(raylib.GamepadPlayer1) { - raylib.DrawText(fmt.Sprintf("GP1: %s", raylib.GetGamepadName(raylib.GamepadPlayer1)), 10, 10, 10, raylib.Black) + if rl.IsGamepadAvailable(rl.GamepadPlayer1) { + rl.DrawText(fmt.Sprintf("GP1: %s", rl.GetGamepadName(rl.GamepadPlayer1)), 10, 10, 10, rl.Black) - if raylib.IsGamepadName(raylib.GamepadPlayer1, xbox360NameID) { - raylib.DrawTexture(texXboxPad, 0, 0, raylib.DarkGray) + if rl.IsGamepadName(rl.GamepadPlayer1, xbox360NameID) { + rl.DrawTexture(texXboxPad, 0, 0, rl.DarkGray) // Draw buttons: xbox home - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonHome) { - raylib.DrawCircle(394, 89, 19, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonHome) { + rl.DrawCircle(394, 89, 19, rl.Red) } // Draw buttons: basic - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonStart) { - raylib.DrawCircle(436, 150, 9, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonStart) { + rl.DrawCircle(436, 150, 9, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonSelect) { - raylib.DrawCircle(352, 150, 9, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonSelect) { + rl.DrawCircle(352, 150, 9, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonX) { - raylib.DrawCircle(501, 151, 15, raylib.Blue) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonX) { + rl.DrawCircle(501, 151, 15, rl.Blue) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonA) { - raylib.DrawCircle(536, 187, 15, raylib.Lime) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonA) { + rl.DrawCircle(536, 187, 15, rl.Lime) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonB) { - raylib.DrawCircle(572, 151, 15, raylib.Maroon) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonB) { + rl.DrawCircle(572, 151, 15, rl.Maroon) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonY) { - raylib.DrawCircle(536, 115, 15, raylib.Gold) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonY) { + rl.DrawCircle(536, 115, 15, rl.Gold) } // Draw buttons: d-pad - raylib.DrawRectangle(317, 202, 19, 71, raylib.Black) - raylib.DrawRectangle(293, 228, 69, 19, raylib.Black) - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonUp) { - raylib.DrawRectangle(317, 202, 19, 26, raylib.Red) + rl.DrawRectangle(317, 202, 19, 71, rl.Black) + rl.DrawRectangle(293, 228, 69, 19, rl.Black) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonUp) { + rl.DrawRectangle(317, 202, 19, 26, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonDown) { - raylib.DrawRectangle(317, 202+45, 19, 26, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonDown) { + rl.DrawRectangle(317, 202+45, 19, 26, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonLeft) { - raylib.DrawRectangle(292, 228, 25, 19, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonLeft) { + rl.DrawRectangle(292, 228, 25, 19, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonRight) { - raylib.DrawRectangle(292+44, 228, 26, 19, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonRight) { + rl.DrawRectangle(292+44, 228, 26, 19, rl.Red) } // Draw buttons: left-right back - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonLb) { - raylib.DrawCircle(259, 61, 20, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonLb) { + rl.DrawCircle(259, 61, 20, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadXboxButtonRb) { - raylib.DrawCircle(536, 61, 20, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadXboxButtonRb) { + rl.DrawCircle(536, 61, 20, rl.Red) } // Draw axis: left joystick - raylib.DrawCircle(259, 152, 39, raylib.Black) - raylib.DrawCircle(259, 152, 34, raylib.LightGray) - raylib.DrawCircle(int32(259+(raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadXboxAxisLeftX)*20)), - int32(152-(raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadXboxAxisLeftY)*20)), 25, raylib.Black) + rl.DrawCircle(259, 152, 39, rl.Black) + rl.DrawCircle(259, 152, 34, rl.LightGray) + rl.DrawCircle(int32(259+(rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadXboxAxisLeftX)*20)), + int32(152-(rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadXboxAxisLeftY)*20)), 25, rl.Black) // Draw axis: right joystick - raylib.DrawCircle(461, 237, 38, raylib.Black) - raylib.DrawCircle(461, 237, 33, raylib.LightGray) - raylib.DrawCircle(int32(461+(raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadXboxAxisRightX)*20)), - int32(237-(raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadXboxAxisRightY)*20)), 25, raylib.Black) + rl.DrawCircle(461, 237, 38, rl.Black) + rl.DrawCircle(461, 237, 33, rl.LightGray) + rl.DrawCircle(int32(461+(rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadXboxAxisRightX)*20)), + int32(237-(rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadXboxAxisRightY)*20)), 25, rl.Black) // Draw axis: left-right triggers - raylib.DrawRectangle(170, 30, 15, 70, raylib.Gray) - raylib.DrawRectangle(604, 30, 15, 70, raylib.Gray) - raylib.DrawRectangle(170, 30, 15, int32(((1.0+raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadXboxAxisLt))/2.0)*70), raylib.Red) - raylib.DrawRectangle(604, 30, 15, int32(((1.0+raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadXboxAxisRt))/2.0)*70), raylib.Red) + rl.DrawRectangle(170, 30, 15, 70, rl.Gray) + rl.DrawRectangle(604, 30, 15, 70, rl.Gray) + rl.DrawRectangle(170, 30, 15, int32(((1.0+rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadXboxAxisLt))/2.0)*70), rl.Red) + rl.DrawRectangle(604, 30, 15, int32(((1.0+rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadXboxAxisRt))/2.0)*70), rl.Red) - } else if raylib.IsGamepadName(raylib.GamepadPlayer1, ps3NameID) { - raylib.DrawTexture(texPs3Pad, 0, 0, raylib.DarkGray) + } else if rl.IsGamepadName(rl.GamepadPlayer1, ps3NameID) { + rl.DrawTexture(texPs3Pad, 0, 0, rl.DarkGray) // Draw buttons: ps - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonPs) { - raylib.DrawCircle(396, 222, 13, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonPs) { + rl.DrawCircle(396, 222, 13, rl.Red) } // Draw buttons: basic - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonSelect) { - raylib.DrawRectangle(328, 170, 32, 13, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonSelect) { + rl.DrawRectangle(328, 170, 32, 13, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonStart) { - raylib.DrawTriangle(raylib.NewVector2(436, 168), raylib.NewVector2(436, 185), raylib.NewVector2(464, 177), raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonStart) { + rl.DrawTriangle(rl.NewVector2(436, 168), rl.NewVector2(436, 185), rl.NewVector2(464, 177), rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonTriangle) { - raylib.DrawCircle(557, 144, 13, raylib.Lime) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonTriangle) { + rl.DrawCircle(557, 144, 13, rl.Lime) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonCircle) { - raylib.DrawCircle(586, 173, 13, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonCircle) { + rl.DrawCircle(586, 173, 13, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonCross) { - raylib.DrawCircle(557, 203, 13, raylib.Violet) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonCross) { + rl.DrawCircle(557, 203, 13, rl.Violet) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonSquare) { - raylib.DrawCircle(527, 173, 13, raylib.Pink) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonSquare) { + rl.DrawCircle(527, 173, 13, rl.Pink) } // Draw buttons: d-pad - raylib.DrawRectangle(225, 132, 24, 84, raylib.Black) - raylib.DrawRectangle(195, 161, 84, 25, raylib.Black) - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonUp) { - raylib.DrawRectangle(225, 132, 24, 29, raylib.Red) + rl.DrawRectangle(225, 132, 24, 84, rl.Black) + rl.DrawRectangle(195, 161, 84, 25, rl.Black) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonUp) { + rl.DrawRectangle(225, 132, 24, 29, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonDown) { - raylib.DrawRectangle(225, 132+54, 24, 30, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonDown) { + rl.DrawRectangle(225, 132+54, 24, 30, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonLeft) { - raylib.DrawRectangle(195, 161, 30, 25, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonLeft) { + rl.DrawRectangle(195, 161, 30, 25, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonRight) { - raylib.DrawRectangle(195+54, 161, 30, 25, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonRight) { + rl.DrawRectangle(195+54, 161, 30, 25, rl.Red) } // Draw buttons: left-right back buttons - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonL1) { - raylib.DrawCircle(239, 82, 20, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonL1) { + rl.DrawCircle(239, 82, 20, rl.Red) } - if raylib.IsGamepadButtonDown(raylib.GamepadPlayer1, raylib.GamepadPs3ButtonR1) { - raylib.DrawCircle(557, 82, 20, raylib.Red) + if rl.IsGamepadButtonDown(rl.GamepadPlayer1, rl.GamepadPs3ButtonR1) { + rl.DrawCircle(557, 82, 20, rl.Red) } // Draw axis: left joystick - raylib.DrawCircle(319, 255, 35, raylib.Black) - raylib.DrawCircle(319, 255, 31, raylib.LightGray) - raylib.DrawCircle(int32(319+(raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadPs3AxisLeftX)*20)), - int32(255+(raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadPs3AxisLeftY)*20)), 25, raylib.Black) + rl.DrawCircle(319, 255, 35, rl.Black) + rl.DrawCircle(319, 255, 31, rl.LightGray) + rl.DrawCircle(int32(319+(rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadPs3AxisLeftX)*20)), + int32(255+(rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadPs3AxisLeftY)*20)), 25, rl.Black) // Draw axis: right joystick - raylib.DrawCircle(475, 255, 35, raylib.Black) - raylib.DrawCircle(475, 255, 31, raylib.LightGray) - raylib.DrawCircle(int32(475+(raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadPs3AxisRightX)*20)), - int32(255+(raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadPs3AxisRightY)*20)), 25, raylib.Black) + rl.DrawCircle(475, 255, 35, rl.Black) + rl.DrawCircle(475, 255, 31, rl.LightGray) + rl.DrawCircle(int32(475+(rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadPs3AxisRightX)*20)), + int32(255+(rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadPs3AxisRightY)*20)), 25, rl.Black) // Draw axis: left-right triggers - raylib.DrawRectangle(169, 48, 15, 70, raylib.Gray) - raylib.DrawRectangle(611, 48, 15, 70, raylib.Gray) - raylib.DrawRectangle(169, 48, 15, int32(((1.0-raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadPs3AxisL2))/2.0)*70), raylib.Red) - raylib.DrawRectangle(611, 48, 15, int32(((1.0-raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, raylib.GamepadPs3AxisR2))/2.0)*70), raylib.Red) + rl.DrawRectangle(169, 48, 15, 70, rl.Gray) + rl.DrawRectangle(611, 48, 15, 70, rl.Gray) + rl.DrawRectangle(169, 48, 15, int32(((1.0-rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadPs3AxisL2))/2.0)*70), rl.Red) + rl.DrawRectangle(611, 48, 15, int32(((1.0-rl.GetGamepadAxisMovement(rl.GamepadPlayer1, rl.GamepadPs3AxisR2))/2.0)*70), rl.Red) } else { - raylib.DrawText("- GENERIC GAMEPAD -", 280, 180, 20, raylib.Gray) + rl.DrawText("- GENERIC GAMEPAD -", 280, 180, 20, rl.Gray) // TODO: Draw generic gamepad } - raylib.DrawText(fmt.Sprintf("DETECTED AXIS [%d]:", raylib.GetGamepadAxisCount(raylib.GamepadPlayer1)), 10, 50, 10, raylib.Maroon) + rl.DrawText(fmt.Sprintf("DETECTED AXIS [%d]:", rl.GetGamepadAxisCount(rl.GamepadPlayer1)), 10, 50, 10, rl.Maroon) - for i := int32(0); i < raylib.GetGamepadAxisCount(raylib.GamepadPlayer1); i++ { - raylib.DrawText(fmt.Sprintf("AXIS %d: %.02f", i, raylib.GetGamepadAxisMovement(raylib.GamepadPlayer1, i)), 20, 70+20*i, 10, raylib.DarkGray) + for i := int32(0); i < rl.GetGamepadAxisCount(rl.GamepadPlayer1); i++ { + rl.DrawText(fmt.Sprintf("AXIS %d: %.02f", i, rl.GetGamepadAxisMovement(rl.GamepadPlayer1, i)), 20, 70+20*i, 10, rl.DarkGray) } - if raylib.GetGamepadButtonPressed() != -1 { - raylib.DrawText(fmt.Sprintf("DETECTED BUTTON: %d", raylib.GetGamepadButtonPressed()), 10, 430, 10, raylib.Red) + if rl.GetGamepadButtonPressed() != -1 { + rl.DrawText(fmt.Sprintf("DETECTED BUTTON: %d", rl.GetGamepadButtonPressed()), 10, 430, 10, rl.Red) } else { - raylib.DrawText("DETECTED BUTTON: NONE", 10, 430, 10, raylib.Gray) + rl.DrawText("DETECTED BUTTON: NONE", 10, 430, 10, rl.Gray) } } else { - raylib.DrawText("GP1: NOT DETECTED", 10, 10, 10, raylib.Gray) + rl.DrawText("GP1: NOT DETECTED", 10, 10, 10, rl.Gray) - raylib.DrawTexture(texXboxPad, 0, 0, raylib.LightGray) + rl.DrawTexture(texXboxPad, 0, 0, rl.LightGray) } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texPs3Pad) - raylib.UnloadTexture(texXboxPad) + rl.UnloadTexture(texPs3Pad) + rl.UnloadTexture(texXboxPad) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/input_keys/main.go b/examples/core/input_keys/main.go index f92a14f..1b6bb65 100644 --- a/examples/core/input_keys/main.go +++ b/examples/core/input_keys/main.go @@ -8,34 +8,34 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard input") + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard input") - ballPosition := raylib.NewVector2(float32(screenWidth)/2, float32(screenHeight)/2) + ballPosition := rl.NewVector2(float32(screenWidth)/2, float32(screenHeight)/2) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsKeyDown(raylib.KeyRight) { + for !rl.WindowShouldClose() { + if rl.IsKeyDown(rl.KeyRight) { ballPosition.X += 0.8 } - if raylib.IsKeyDown(raylib.KeyLeft) { + if rl.IsKeyDown(rl.KeyLeft) { ballPosition.X -= 0.8 } - if raylib.IsKeyDown(raylib.KeyUp) { + if rl.IsKeyDown(rl.KeyUp) { ballPosition.Y -= 0.8 } - if raylib.IsKeyDown(raylib.KeyDown) { + if rl.IsKeyDown(rl.KeyDown) { ballPosition.Y += 0.8 } - raylib.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("move the ball with arrow keys", 10, 10, 20, raylib.DarkGray) - raylib.DrawCircleV(ballPosition, 50, raylib.Maroon) + rl.DrawText("move the ball with arrow keys", 10, 10, 20, rl.DarkGray) + rl.DrawCircleV(ballPosition, 50, rl.Maroon) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/input_mouse/main.go b/examples/core/input_mouse/main.go index c71a88e..94f3a06 100644 --- a/examples/core/input_mouse/main.go +++ b/examples/core/input_mouse/main.go @@ -5,31 +5,31 @@ import ( ) func main() { - raylib.InitWindow(800, 450, "raylib [core] example - mouse input") - raylib.SetTargetFPS(60) + rl.InitWindow(800, 450, "raylib [core] example - mouse input") + rl.SetTargetFPS(60) - ballColor := raylib.DarkBlue + ballColor := rl.DarkBlue - for !raylib.WindowShouldClose() { - ballPosition := raylib.GetMousePosition() + for !rl.WindowShouldClose() { + ballPosition := rl.GetMousePosition() - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { - ballColor = raylib.Maroon - } else if raylib.IsMouseButtonPressed(raylib.MouseMiddleButton) { - ballColor = raylib.Lime - } else if raylib.IsMouseButtonPressed(raylib.MouseRightButton) { - ballColor = raylib.DarkBlue + if rl.IsMouseButtonPressed(rl.MouseLeftButton) { + ballColor = rl.Maroon + } else if rl.IsMouseButtonPressed(rl.MouseMiddleButton) { + ballColor = rl.Lime + } else if rl.IsMouseButtonPressed(rl.MouseRightButton) { + ballColor = rl.DarkBlue } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) - raylib.DrawCircleV(ballPosition, 40, ballColor) + rl.ClearBackground(rl.RayWhite) + rl.DrawCircleV(ballPosition, 40, ballColor) - raylib.DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, raylib.DarkGray) + rl.DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, rl.DarkGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/mouse_wheel/main.go b/examples/core/mouse_wheel/main.go index f39026b..b343589 100644 --- a/examples/core/mouse_wheel/main.go +++ b/examples/core/mouse_wheel/main.go @@ -10,27 +10,27 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - mouse wheel") + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - mouse wheel") boxPositionY := screenHeight/2 - 40 scrollSpeed := int32(4) // Scrolling speed in pixels - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - boxPositionY -= raylib.GetMouseWheelMove() * scrollSpeed + for !rl.WindowShouldClose() { + boxPositionY -= rl.GetMouseWheelMove() * scrollSpeed - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawRectangle(screenWidth/2-40, boxPositionY, 80, 80, raylib.Maroon) + rl.DrawRectangle(screenWidth/2-40, boxPositionY, 80, 80, rl.Maroon) - raylib.DrawText("Use mouse wheel to move the square up and down!", 10, 10, 20, raylib.Gray) - raylib.DrawText(fmt.Sprintf("Box position Y: %d", boxPositionY), 10, 40, 20, raylib.LightGray) + rl.DrawText("Use mouse wheel to move the cube up and down!", 10, 10, 20, rl.Gray) + rl.DrawText(fmt.Sprintf("Box position Y: %d", boxPositionY), 10, 40, 20, rl.LightGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/random_values/main.go b/examples/core/random_values/main.go index 94da27f..bb6f188 100644 --- a/examples/core/random_values/main.go +++ b/examples/core/random_values/main.go @@ -7,32 +7,32 @@ import ( ) func main() { - raylib.InitWindow(800, 450, "raylib [core] example - generate random values") + rl.InitWindow(800, 450, "raylib [core] example - generate random values") framesCounter := 0 // Variable used to count frames - randValue := raylib.GetRandomValue(-8, 5) // Get a random integer number between -8 and 5 (both included) + randValue := rl.GetRandomValue(-8, 5) // Get a random integer number between -8 and 5 (both included) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { framesCounter++ // Every two seconds (120 frames) a new random value is generated if ((framesCounter / 120) % 2) == 1 { - randValue = raylib.GetRandomValue(-8, 5) + randValue = rl.GetRandomValue(-8, 5) framesCounter = 0 } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("Every 2 seconds a new random value is generated:", 130, 100, 20, raylib.Maroon) + rl.DrawText("Every 2 seconds a new random value is generated:", 130, 100, 20, rl.Maroon) - raylib.DrawText(fmt.Sprintf("%d", randValue), 360, 180, 80, raylib.LightGray) + rl.DrawText(fmt.Sprintf("%d", randValue), 360, 180, 80, rl.LightGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/storage_values/main.go b/examples/core/storage_values/main.go index 0898700..2aecc85 100644 --- a/examples/core/storage_values/main.go +++ b/examples/core/storage_values/main.go @@ -12,47 +12,47 @@ const ( ) func main() { - raylib.InitWindow(800, 450, "raylib [core] example - storage save/load values") + rl.InitWindow(800, 450, "raylib [core] example - storage save/load values") score := int32(0) hiscore := int32(0) framesCounter := 0 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsKeyPressed(raylib.KeyR) { - score = raylib.GetRandomValue(1000, 2000) - hiscore = raylib.GetRandomValue(2000, 4000) + for !rl.WindowShouldClose() { + if rl.IsKeyPressed(rl.KeyR) { + score = rl.GetRandomValue(1000, 2000) + hiscore = rl.GetRandomValue(2000, 4000) } - if raylib.IsKeyPressed(raylib.KeyEnter) { - raylib.StorageSaveValue(storageScore, score) - raylib.StorageSaveValue(storageHiscore, hiscore) - } else if raylib.IsKeyPressed(raylib.KeySpace) { + if rl.IsKeyPressed(rl.KeyEnter) { + rl.StorageSaveValue(storageScore, score) + rl.StorageSaveValue(storageHiscore, hiscore) + } else if rl.IsKeyPressed(rl.KeySpace) { // NOTE: If requested position could not be found, value 0 is returned - score = raylib.StorageLoadValue(storageScore) - hiscore = raylib.StorageLoadValue(storageHiscore) + score = rl.StorageLoadValue(storageScore) + hiscore = rl.StorageLoadValue(storageHiscore) } framesCounter++ - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText(fmt.Sprintf("SCORE: %d", score), 280, 130, 40, raylib.Maroon) - raylib.DrawText(fmt.Sprintf("HI-SCORE: %d", hiscore), 210, 200, 50, raylib.Black) + rl.DrawText(fmt.Sprintf("SCORE: %d", score), 280, 130, 40, rl.Maroon) + rl.DrawText(fmt.Sprintf("HI-SCORE: %d", hiscore), 210, 200, 50, rl.Black) - raylib.DrawText(fmt.Sprintf("frames: %d", framesCounter), 10, 10, 20, raylib.Lime) + rl.DrawText(fmt.Sprintf("frames: %d", framesCounter), 10, 10, 20, rl.Lime) - raylib.DrawText("Press R to generate random numbers", 220, 40, 20, raylib.LightGray) - raylib.DrawText("Press ENTER to SAVE values", 250, 310, 20, raylib.LightGray) - raylib.DrawText("Press SPACE to LOAD values", 252, 350, 20, raylib.LightGray) + rl.DrawText("Press R to generate random numbers", 220, 40, 20, rl.LightGray) + rl.DrawText("Press ENTER to SAVE values", 250, 310, 20, rl.LightGray) + rl.DrawText("Press SPACE to LOAD values", 252, 350, 20, rl.LightGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/vr_simulator/main.go b/examples/core/vr_simulator/main.go index 246a3cb..cb33497 100644 --- a/examples/core/vr_simulator/main.go +++ b/examples/core/vr_simulator/main.go @@ -5,50 +5,54 @@ import ( ) func main() { - hmd := raylib.GetVrDeviceInfo(raylib.HmdOculusRiftCv1) // Oculus Rift CV1 - raylib.InitWindow(int32(hmd.HScreenSize), int32(hmd.VScreenSize), "raylib [core] example - vr simulator") + screenWidth := int32(1080) + screenHeight := int32(600) + + // NOTE: screenWidth/screenHeight should match VR device aspect ratio + + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator") // NOTE: default device (simulator) - raylib.InitVrSimulator(hmd) // Init VR device + rl.InitVrSimulator(rl.GetVrDeviceInfo(rl.HmdOculusRiftCv1)) // Init VR device (Oculus Rift CV1) - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(5.0, 2.0, 5.0) // Camera position - camera.Target = raylib.NewVector3(0.0, 2.0, 0.0) // Camera looking at point - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) // Camera up vector (rotation towards target) + camera := rl.Camera{} + camera.Position = rl.NewVector3(5.0, 2.0, 5.0) // Camera position + camera.Target = rl.NewVector3(0.0, 2.0, 0.0) // Camera looking at point + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) // Camera up vector (rotation towards target) camera.Fovy = 60.0 // Camera field-of-view Y - cubePosition := raylib.NewVector3(0.0, 0.0, 0.0) + cubePosition := rl.NewVector3(0.0, 0.0, 0.0) - raylib.SetCameraMode(camera, raylib.CameraFirstPerson) // Set first person camera mode + rl.SetCameraMode(camera, rl.CameraFirstPerson) // Set first person camera mode - raylib.SetTargetFPS(90) + rl.SetTargetFPS(90) - for !raylib.WindowShouldClose() { - raylib.UpdateCamera(&camera) // Update camera (simulator mode) + for !rl.WindowShouldClose() { + rl.UpdateCamera(&camera) // Update camera (simulator mode) - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginVrDrawing() + rl.BeginVrDrawing() - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawCube(cubePosition, 2.0, 2.0, 2.0, raylib.Red) - raylib.DrawCubeWires(cubePosition, 2.0, 2.0, 2.0, raylib.Maroon) + rl.DrawCube(cubePosition, 2.0, 2.0, 2.0, rl.Red) + rl.DrawCubeWires(cubePosition, 2.0, 2.0, 2.0, rl.Maroon) - raylib.DrawGrid(40, 1.0) + rl.DrawGrid(40, 1.0) - raylib.EndMode3D() + rl.EndMode3D() - raylib.EndVrDrawing() + rl.EndVrDrawing() - raylib.DrawFPS(10, 10) + rl.DrawFPS(10, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseVrSimulator() // Close VR simulator + rl.CloseVrSimulator() // Close VR simulator - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/core/world_screen/main.go b/examples/core/world_screen/main.go index c61e81a..2b900d1 100644 --- a/examples/core/world_screen/main.go +++ b/examples/core/world_screen/main.go @@ -8,45 +8,45 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free") + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(10.0, 10.0, 10.0) // Camera position - camera.Target = raylib.NewVector3(0.0, 0.0, 0.0) // Camera looking at point - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) // Camera up vector (rotation towards target) + camera := rl.Camera{} + camera.Position = rl.NewVector3(10.0, 10.0, 10.0) // Camera position + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) // Camera looking at point + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) // Camera up vector (rotation towards target) camera.Fovy = 45.0 // Camera field-of-view Y - cubePosition := raylib.NewVector3(0.0, 0.0, 0.0) - cubeScreenPosition := raylib.Vector2{} + cubePosition := rl.NewVector3(0.0, 0.0, 0.0) + cubeScreenPosition := rl.Vector2{} - raylib.SetCameraMode(camera, raylib.CameraFree) // Set a free camera mode + rl.SetCameraMode(camera, rl.CameraFree) // Set a free camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.UpdateCamera(&camera) // Update camera + for !rl.WindowShouldClose() { + rl.UpdateCamera(&camera) // Update camera // Calculate cube screen space position (with a little offset to be in top) - cubeScreenPosition = raylib.GetWorldToScreen(raylib.NewVector3(cubePosition.X, cubePosition.Y+2.5, cubePosition.Z), camera) + cubeScreenPosition = rl.GetWorldToScreen(rl.NewVector3(cubePosition.X, cubePosition.Y+2.5, cubePosition.Z), camera) - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawCube(cubePosition, 2.0, 2.0, 2.0, raylib.Red) - raylib.DrawCubeWires(cubePosition, 2.0, 2.0, 2.0, raylib.Maroon) + rl.DrawCube(cubePosition, 2.0, 2.0, 2.0, rl.Red) + rl.DrawCubeWires(cubePosition, 2.0, 2.0, 2.0, rl.Maroon) - raylib.DrawGrid(10, 1.0) + rl.DrawGrid(10, 1.0) - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawText("Enemy: 100 / 100", int32(cubeScreenPosition.X)-raylib.MeasureText("Enemy: 100 / 100", 20)/2, int32(cubeScreenPosition.Y), 20, raylib.Black) - raylib.DrawText("Text is always on top of the cube", (screenWidth-raylib.MeasureText("Text is always on top of the cube", 20))/2, 25, 20, raylib.Gray) + rl.DrawText("Enemy: 100 / 100", int32(cubeScreenPosition.X)-rl.MeasureText("Enemy: 100 / 100", 20)/2, int32(cubeScreenPosition.Y), 20, rl.Black) + rl.DrawText("Text is always on top of the cube", (screenWidth-rl.MeasureText("Text is always on top of the cube", 20))/2, 25, 20, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/easings/easings/main.go b/examples/easings/easings/main.go index 92de6b3..d4ae033 100644 --- a/examples/easings/easings/main.go +++ b/examples/easings/easings/main.go @@ -10,8 +10,8 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.SetConfigFlags(raylib.FlagVsyncHint) - raylib.InitWindow(screenWidth, screenHeight, "raylib [easings] example - easings") + rl.SetConfigFlags(rl.FlagVsyncHint) + rl.InitWindow(screenWidth, screenHeight, "raylib [easings] example - easings") currentTime := 0 duration := float32(60) @@ -19,7 +19,7 @@ func main() { finalPositionX := startPositionX * 3 currentPositionX := startPositionX - ballPosition := raylib.NewVector2(startPositionX, float32(screenHeight)/2) + ballPosition := rl.NewVector2(startPositionX, float32(screenHeight)/2) comboActive := 0 comboLastActive := 0 @@ -27,10 +27,10 @@ func main() { easingTypes := []string{"SineIn", "SineOut", "SineInOut", "BounceIn", "BounceOut", "BounceInOut", "BackIn", "BackOut", "BackInOut"} ease := easingTypes[comboActive] - //raylib.SetTargetFPS(60) + //rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsKeyDown(raylib.KeyR) { + for !rl.WindowShouldClose() { + if rl.IsKeyDown(rl.KeyR) { currentTime = 0 currentPositionX = startPositionX ballPosition.X = currentPositionX @@ -71,18 +71,18 @@ func main() { currentTime++ } - raylib.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) - raygui.Label(raylib.NewRectangle(20, 20, 200, 20), "Easing Type:") - comboActive = raygui.ComboBox(raylib.NewRectangle(20, 40, 200, 20), easingTypes, comboActive) + raygui.Label(rl.NewRectangle(20, 20, 200, 20), "Easing Type:") + comboActive = raygui.ComboBox(rl.NewRectangle(20, 40, 200, 20), easingTypes, comboActive) - raygui.Label(raylib.NewRectangle(20, 80, 200, 20), "Press R to reset") + raygui.Label(rl.NewRectangle(20, 80, 200, 20), "Press R to reset") - raylib.DrawCircleV(ballPosition, 50, raylib.Maroon) + rl.DrawCircleV(ballPosition, 50, rl.Maroon) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/games/floppy/main.go b/examples/games/floppy/main.go index 3992e48..8cb14cd 100644 --- a/examples/games/floppy/main.go +++ b/examples/games/floppy/main.go @@ -35,20 +35,20 @@ const ( // Floppy type type Floppy struct { - Position raylib.Vector2 + Position rl.Vector2 } // Pipe type type Pipe struct { - Rec raylib.Rectangle - Color raylib.Color + Rec rl.Rectangle + Color rl.Color Active bool } // Particle type type Particle struct { - Position raylib.Vector2 - Color raylib.Color + Position rl.Vector2 + Color rl.Color Alpha float32 Size float32 Rotation float32 @@ -57,17 +57,17 @@ type Particle struct { // Game type type Game struct { - FxFlap raylib.Sound - FxSlap raylib.Sound - FxPoint raylib.Sound - FxClick raylib.Sound + FxFlap rl.Sound + FxSlap rl.Sound + FxPoint rl.Sound + FxClick rl.Sound - TxSprites raylib.Texture2D - TxSmoke raylib.Texture2D - TxClouds raylib.Texture2D + TxSprites rl.Texture2D + TxSmoke rl.Texture2D + TxClouds rl.Texture2D - CloudRec raylib.Rectangle - FrameRec raylib.Rectangle + CloudRec rl.Rectangle + FrameRec rl.Rectangle GameOver bool Dead bool @@ -84,7 +84,7 @@ type Game struct { Particles []Particle Pipes []Pipe - PipesPos []raylib.Vector2 + PipesPos []rl.Vector2 } // NewGame - Start new game @@ -95,7 +95,7 @@ func NewGame() (g Game) { // On Android this sets callback function to be used for android_main func init() { - raylib.SetCallbackFunc(main) + rl.SetCallbackFunc(main) } func main() { @@ -104,16 +104,16 @@ func main() { game.GameOver = true // Initialize window - raylib.InitWindow(screenWidth, screenHeight, "Floppy Gopher") + rl.InitWindow(screenWidth, screenHeight, "Floppy Gopher") // Initialize audio - raylib.InitAudioDevice() + rl.InitAudioDevice() // NOTE: Textures and Sounds MUST be loaded after Window/Audio initialization game.Load() // Limit FPS - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) // Main loop for !game.WindowShouldClose { @@ -128,10 +128,10 @@ func main() { game.Unload() // Close audio - raylib.CloseAudioDevice() + rl.CloseAudioDevice() // Close window - raylib.CloseWindow() + rl.CloseWindow() // Exit os.Exit(0) @@ -141,37 +141,37 @@ func main() { func (g *Game) Init() { // Gopher - g.Floppy = Floppy{raylib.NewVector2(80, float32(screenHeight)/2-spriteSize/2)} + g.Floppy = Floppy{rl.NewVector2(80, float32(screenHeight)/2-spriteSize/2)} // Sprite rectangle - g.FrameRec = raylib.NewRectangle(0, 0, spriteSize, spriteSize) + g.FrameRec = rl.NewRectangle(0, 0, spriteSize, spriteSize) // Cloud rectangle - g.CloudRec = raylib.NewRectangle(0, 0, float32(screenWidth), float32(g.TxClouds.Height)) + g.CloudRec = rl.NewRectangle(0, 0, float32(screenWidth), float32(g.TxClouds.Height)) // Initialize particles g.Particles = make([]Particle, maxParticles) for i := 0; i < maxParticles; i++ { - g.Particles[i].Position = raylib.NewVector2(0, 0) - g.Particles[i].Color = raylib.RayWhite + g.Particles[i].Position = rl.NewVector2(0, 0) + g.Particles[i].Color = rl.RayWhite g.Particles[i].Alpha = 1.0 - g.Particles[i].Size = float32(raylib.GetRandomValue(1, 30)) / 20.0 - g.Particles[i].Rotation = float32(raylib.GetRandomValue(0, 360)) + g.Particles[i].Size = float32(rl.GetRandomValue(1, 30)) / 20.0 + g.Particles[i].Rotation = float32(rl.GetRandomValue(0, 360)) g.Particles[i].Active = false } // Pipes positions - g.PipesPos = make([]raylib.Vector2, maxPipes) + g.PipesPos = make([]rl.Vector2, maxPipes) for i := 0; i < maxPipes; i++ { g.PipesPos[i].X = float32(480 + 360*i) - g.PipesPos[i].Y = -float32(raylib.GetRandomValue(0, 240)) + g.PipesPos[i].Y = -float32(rl.GetRandomValue(0, 240)) } // Pipes colors - colors := []raylib.Color{ - raylib.Orange, raylib.Red, raylib.Gold, raylib.Lime, - raylib.Violet, raylib.Brown, raylib.LightGray, raylib.Blue, - raylib.Yellow, raylib.Green, raylib.Purple, raylib.Beige, + colors := []rl.Color{ + rl.Orange, rl.Red, rl.Gold, rl.Lime, + rl.Violet, rl.Brown, rl.LightGray, rl.Blue, + rl.Yellow, rl.Green, rl.Purple, rl.Beige, } // Pipes @@ -181,7 +181,7 @@ func (g *Game) Init() { g.Pipes[i].Rec.Y = g.PipesPos[i/2].Y g.Pipes[i].Rec.Width = pipesWidth g.Pipes[i].Rec.Height = 550 - g.Pipes[i].Color = colors[raylib.GetRandomValue(0, int32(len(colors)-1))] + g.Pipes[i].Color = colors[rl.GetRandomValue(0, int32(len(colors)-1))] g.Pipes[i+1].Rec.X = g.PipesPos[i/2].X g.Pipes[i+1].Rec.Y = 1200 + g.PipesPos[i/2].Y - 550 @@ -203,35 +203,35 @@ func (g *Game) Init() { // Load - Load resources func (g *Game) Load() { - g.FxFlap = raylib.LoadSound("sounds/flap.wav") - g.FxSlap = raylib.LoadSound("sounds/slap.wav") - g.FxPoint = raylib.LoadSound("sounds/point.wav") - g.FxClick = raylib.LoadSound("sounds/click.wav") - g.TxSprites = raylib.LoadTexture("images/sprite.png") - g.TxSmoke = raylib.LoadTexture("images/smoke.png") - g.TxClouds = raylib.LoadTexture("images/clouds.png") + g.FxFlap = rl.LoadSound("sounds/flap.wav") + g.FxSlap = rl.LoadSound("sounds/slap.wav") + g.FxPoint = rl.LoadSound("sounds/point.wav") + g.FxClick = rl.LoadSound("sounds/click.wav") + g.TxSprites = rl.LoadTexture("images/sprite.png") + g.TxSmoke = rl.LoadTexture("images/smoke.png") + g.TxClouds = rl.LoadTexture("images/clouds.png") } // Unload - Unload resources func (g *Game) Unload() { - raylib.UnloadSound(g.FxFlap) - raylib.UnloadSound(g.FxSlap) - raylib.UnloadSound(g.FxPoint) - raylib.UnloadSound(g.FxClick) - raylib.UnloadTexture(g.TxSprites) - raylib.UnloadTexture(g.TxSmoke) - raylib.UnloadTexture(g.TxClouds) + rl.UnloadSound(g.FxFlap) + rl.UnloadSound(g.FxSlap) + rl.UnloadSound(g.FxPoint) + rl.UnloadSound(g.FxClick) + rl.UnloadTexture(g.TxSprites) + rl.UnloadTexture(g.TxSmoke) + rl.UnloadTexture(g.TxClouds) } // Update - Update game func (g *Game) Update() { - if raylib.WindowShouldClose() { + if rl.WindowShouldClose() { g.WindowShouldClose = true } if !g.GameOver { - if raylib.IsKeyPressed(raylib.KeyP) || raylib.IsKeyPressed(raylib.KeyBack) { - raylib.PlaySound(g.FxClick) + if rl.IsKeyPressed(rl.KeyP) || rl.IsKeyPressed(rl.KeyBack) { + rl.PlaySound(g.FxClick) if runtime.GOOS == "android" && g.Pause { g.WindowShouldClose = true @@ -259,8 +259,8 @@ func (g *Game) Update() { } // Movement/Controls - if raylib.IsKeyDown(raylib.KeySpace) || raylib.IsMouseButtonDown(raylib.MouseLeftButton) { - raylib.PlaySound(g.FxFlap) + if rl.IsKeyDown(rl.KeySpace) || rl.IsMouseButtonDown(rl.MouseLeftButton) { + rl.PlaySound(g.FxFlap) // Activate one particle every frame for i := 0; i < maxParticles; i++ { @@ -315,11 +315,11 @@ func (g *Game) Update() { // Check Collisions for i := 0; i < maxPipes*2; i++ { - if raylib.CheckCollisionRecs(raylib.NewRectangle(g.Floppy.Position.X, g.Floppy.Position.Y, spriteSize, spriteSize), g.Pipes[i].Rec) { + if rl.CheckCollisionRecs(rl.NewRectangle(g.Floppy.Position.X, g.Floppy.Position.Y, spriteSize, spriteSize), g.Pipes[i].Rec) { // OMG You killed Gopher you bastard! g.Dead = true - raylib.PlaySound(g.FxSlap) + rl.PlaySound(g.FxSlap) } else if (g.PipesPos[i/2].X < g.Floppy.Position.X-spriteSize) && g.Pipes[i/2].Active && !g.GameOver { // Score point g.Score += 1 @@ -333,7 +333,7 @@ func (g *Game) Update() { g.HiScore = g.Score } - raylib.PlaySound(g.FxPoint) + rl.PlaySound(g.FxPoint) } } } else { @@ -351,17 +351,17 @@ func (g *Game) Update() { } } } else { - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { g.Pause = !g.Pause } } } else { - if raylib.IsKeyPressed(raylib.KeyEnter) || raylib.IsMouseButtonDown(raylib.MouseLeftButton) { - raylib.PlaySound(g.FxClick) + if rl.IsKeyPressed(rl.KeyEnter) || rl.IsMouseButtonDown(rl.MouseLeftButton) { + rl.PlaySound(g.FxClick) // Return of the Gopher! g.Init() - } else if runtime.GOOS == "android" && raylib.IsKeyDown(raylib.KeyBack) { + } else if runtime.GOOS == "android" && rl.IsKeyDown(rl.KeyBack) { g.WindowShouldClose = true } @@ -379,32 +379,32 @@ func (g *Game) Update() { // Draw - Draw game func (g *Game) Draw() { - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.SkyBlue) + rl.ClearBackground(rl.SkyBlue) if !g.GameOver { // Draw clouds - raylib.DrawTextureRec(g.TxClouds, g.CloudRec, raylib.NewVector2(0, float32(screenHeight-g.TxClouds.Height)), raylib.RayWhite) + rl.DrawTextureRec(g.TxClouds, g.CloudRec, rl.NewVector2(0, float32(screenHeight-g.TxClouds.Height)), rl.RayWhite) // Draw rotated clouds - raylib.DrawTexturePro(g.TxClouds, raylib.NewRectangle(-g.CloudRec.X, 0, float32(g.TxClouds.Width), float32(g.TxClouds.Height)), - raylib.NewRectangle(0, 0, float32(g.TxClouds.Width), float32(g.TxClouds.Height)), raylib.NewVector2(float32(g.TxClouds.Width), float32(g.TxClouds.Height)), 180, raylib.White) + rl.DrawTexturePro(g.TxClouds, rl.NewRectangle(-g.CloudRec.X, 0, float32(g.TxClouds.Width), float32(g.TxClouds.Height)), + rl.NewRectangle(0, 0, float32(g.TxClouds.Width), float32(g.TxClouds.Height)), rl.NewVector2(float32(g.TxClouds.Width), float32(g.TxClouds.Height)), 180, rl.White) // Draw Gopher - raylib.DrawTextureRec(g.TxSprites, g.FrameRec, g.Floppy.Position, raylib.RayWhite) + rl.DrawTextureRec(g.TxSprites, g.FrameRec, g.Floppy.Position, rl.RayWhite) // Draw active particles if !g.Dead { for i := 0; i < maxParticles; i++ { if g.Particles[i].Active { - raylib.DrawTexturePro( + rl.DrawTexturePro( g.TxSmoke, - raylib.NewRectangle(0, 0, float32(g.TxSmoke.Width), float32(g.TxSmoke.Height)), - raylib.NewRectangle(g.Particles[i].Position.X, g.Particles[i].Position.Y, float32(g.TxSmoke.Width)*g.Particles[i].Size, float32(g.TxSmoke.Height)*g.Particles[i].Size), - raylib.NewVector2(float32(g.TxSmoke.Width)*g.Particles[i].Size/2, float32(g.TxSmoke.Height)*g.Particles[i].Size/2), + rl.NewRectangle(0, 0, float32(g.TxSmoke.Width), float32(g.TxSmoke.Height)), + rl.NewRectangle(g.Particles[i].Position.X, g.Particles[i].Position.Y, float32(g.TxSmoke.Width)*g.Particles[i].Size, float32(g.TxSmoke.Height)*g.Particles[i].Size), + rl.NewVector2(float32(g.TxSmoke.Width)*g.Particles[i].Size/2, float32(g.TxSmoke.Height)*g.Particles[i].Size/2), g.Particles[i].Rotation, - raylib.Fade(g.Particles[i].Color, g.Particles[i].Alpha), + rl.Fade(g.Particles[i].Color, g.Particles[i].Alpha), ) } } @@ -412,41 +412,41 @@ func (g *Game) Draw() { // Draw pipes for i := 0; i < maxPipes; i++ { - raylib.DrawRectangle(int32(g.Pipes[i*2].Rec.X), int32(g.Pipes[i*2].Rec.Y), int32(g.Pipes[i*2].Rec.Width), int32(g.Pipes[i*2].Rec.Height), g.Pipes[i*2].Color) - raylib.DrawRectangle(int32(g.Pipes[i*2+1].Rec.X), int32(g.Pipes[i*2+1].Rec.Y), int32(g.Pipes[i*2+1].Rec.Width), int32(g.Pipes[i*2+1].Rec.Height), g.Pipes[i*2].Color) + rl.DrawRectangle(int32(g.Pipes[i*2].Rec.X), int32(g.Pipes[i*2].Rec.Y), int32(g.Pipes[i*2].Rec.Width), int32(g.Pipes[i*2].Rec.Height), g.Pipes[i*2].Color) + rl.DrawRectangle(int32(g.Pipes[i*2+1].Rec.X), int32(g.Pipes[i*2+1].Rec.Y), int32(g.Pipes[i*2+1].Rec.Width), int32(g.Pipes[i*2+1].Rec.Height), g.Pipes[i*2].Color) // Draw borders - raylib.DrawRectangleLines(int32(g.Pipes[i*2].Rec.X), int32(g.Pipes[i*2].Rec.Y), int32(g.Pipes[i*2].Rec.Width), int32(g.Pipes[i*2].Rec.Height), raylib.Black) - raylib.DrawRectangleLines(int32(g.Pipes[i*2+1].Rec.X), int32(g.Pipes[i*2+1].Rec.Y), int32(g.Pipes[i*2+1].Rec.Width), int32(g.Pipes[i*2+1].Rec.Height), raylib.Black) + rl.DrawRectangleLines(int32(g.Pipes[i*2].Rec.X), int32(g.Pipes[i*2].Rec.Y), int32(g.Pipes[i*2].Rec.Width), int32(g.Pipes[i*2].Rec.Height), rl.Black) + rl.DrawRectangleLines(int32(g.Pipes[i*2+1].Rec.X), int32(g.Pipes[i*2+1].Rec.Y), int32(g.Pipes[i*2+1].Rec.Width), int32(g.Pipes[i*2+1].Rec.Height), rl.Black) } // Draw Super Flashing FX (one frame only) if g.SuperFX { - raylib.DrawRectangle(0, 0, screenWidth, screenHeight, raylib.White) + rl.DrawRectangle(0, 0, screenWidth, screenHeight, rl.White) g.SuperFX = false } // Draw HI-SCORE - raylib.DrawText(fmt.Sprintf("%02d", g.Score), 20, 20, 32, raylib.Black) - raylib.DrawText(fmt.Sprintf("HI-SCORE: %02d", g.HiScore), 20, 64, 20, raylib.Black) + rl.DrawText(fmt.Sprintf("%02d", g.Score), 20, 20, 32, rl.Black) + rl.DrawText(fmt.Sprintf("HI-SCORE: %02d", g.HiScore), 20, 64, 20, rl.Black) if g.Pause { // Draw PAUSED text - raylib.DrawText("PAUSED", screenWidth/2-raylib.MeasureText("PAUSED", 24)/2, screenHeight/2-50, 20, raylib.Black) + rl.DrawText("PAUSED", screenWidth/2-rl.MeasureText("PAUSED", 24)/2, screenHeight/2-50, 20, rl.Black) } } else { // Draw text - raylib.DrawText("Floppy Gopher", raylib.GetScreenWidth()/2-raylib.MeasureText("Floppy Gopher", 40)/2, raylib.GetScreenHeight()/2-150, 40, raylib.RayWhite) + rl.DrawText("Floppy Gopher", rl.GetScreenWidth()/2-rl.MeasureText("Floppy Gopher", 40)/2, rl.GetScreenHeight()/2-150, 40, rl.RayWhite) if runtime.GOOS == "android" { - raylib.DrawText("[TAP] TO PLAY", raylib.GetScreenWidth()/2-raylib.MeasureText("[TAP] TO PLAY", 20)/2, raylib.GetScreenHeight()/2-50, 20, raylib.Black) + rl.DrawText("[TAP] TO PLAY", rl.GetScreenWidth()/2-rl.MeasureText("[TAP] TO PLAY", 20)/2, rl.GetScreenHeight()/2-50, 20, rl.Black) } else { - raylib.DrawText("[ENTER] TO PLAY", raylib.GetScreenWidth()/2-raylib.MeasureText("[ENTER] TO PLAY", 20)/2, raylib.GetScreenHeight()/2-50, 20, raylib.Black) + rl.DrawText("[ENTER] TO PLAY", rl.GetScreenWidth()/2-rl.MeasureText("[ENTER] TO PLAY", 20)/2, rl.GetScreenHeight()/2-50, 20, rl.Black) } // Draw Gopher - raylib.DrawTextureRec(g.TxSprites, g.FrameRec, raylib.NewVector2(float32(raylib.GetScreenWidth()/2-spriteSize/2), float32(raylib.GetScreenHeight()/2)), raylib.RayWhite) + rl.DrawTextureRec(g.TxSprites, g.FrameRec, rl.NewVector2(float32(rl.GetScreenWidth()/2-spriteSize/2), float32(rl.GetScreenHeight()/2)), rl.RayWhite) } - raylib.EndDrawing() + rl.EndDrawing() } diff --git a/examples/games/life/main.go b/examples/games/life/main.go index 1b385fe..74cb410 100644 --- a/examples/games/life/main.go +++ b/examples/games/life/main.go @@ -13,8 +13,8 @@ const ( // Cell type type Cell struct { - Position raylib.Vector2 - Size raylib.Vector2 + Position rl.Vector2 + Size rl.Vector2 Alive bool Next bool Visited bool @@ -37,10 +37,10 @@ func main() { game := Game{} game.Init(false) - raylib.InitWindow(game.ScreenWidth, game.ScreenHeight, "Conway's Game of Life") - raylib.SetTargetFPS(20) + rl.InitWindow(game.ScreenWidth, game.ScreenHeight, "Conway's Game of Life") + rl.SetTargetFPS(20) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { if game.Playing { game.Update() } @@ -50,7 +50,7 @@ func main() { game.Draw() } - raylib.CloseWindow() + rl.CloseWindow() } // Init - Initialize game @@ -70,8 +70,8 @@ func (g *Game) Init(clear bool) { for x := int32(0); x <= g.Cols; x++ { for y := int32(0); y <= g.Rows; y++ { g.Cells[x][y] = &Cell{} - g.Cells[x][y].Position = raylib.NewVector2((float32(x) * squareSize), (float32(y)*squareSize)+1) - g.Cells[x][y].Size = raylib.NewVector2(squareSize-1, squareSize-1) + g.Cells[x][y].Position = rl.NewVector2((float32(x) * squareSize), (float32(y)*squareSize)+1) + g.Cells[x][y].Size = rl.NewVector2(squareSize-1, squareSize-1) if rand.Float64() < 0.1 && clear == false { g.Cells[x][y].Alive = true } @@ -82,19 +82,19 @@ func (g *Game) Init(clear bool) { // Input - Game input func (g *Game) Input() { // control - if raylib.IsKeyPressed(raylib.KeyR) { + if rl.IsKeyPressed(rl.KeyR) { g.Init(false) } - if raylib.IsKeyPressed(raylib.KeyC) { + if rl.IsKeyPressed(rl.KeyC) { g.Init(true) } - if raylib.IsKeyDown(raylib.KeyRight) && !g.Playing { + if rl.IsKeyDown(rl.KeyRight) && !g.Playing { g.Update() } - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { - g.Click(raylib.GetMouseX(), raylib.GetMouseY()) + if rl.IsMouseButtonPressed(rl.MouseLeftButton) { + g.Click(rl.GetMouseX(), rl.GetMouseY()) } - if raylib.IsKeyPressed(raylib.KeySpace) { + if rl.IsKeyPressed(rl.KeySpace) { g.Playing = !g.Playing } @@ -165,36 +165,36 @@ func (g *Game) CountNeighbors(x, y int32) int { // Draw - Draw game func (g *Game) Draw() { - raylib.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) // Draw cells for x := int32(0); x <= g.Cols; x++ { for y := int32(0); y <= g.Rows; y++ { if g.Cells[x][y].Alive { - raylib.DrawRectangleV(g.Cells[x][y].Position, g.Cells[x][y].Size, raylib.Blue) + rl.DrawRectangleV(g.Cells[x][y].Position, g.Cells[x][y].Size, rl.Blue) } else if g.Cells[x][y].Visited { - raylib.DrawRectangleV(g.Cells[x][y].Position, g.Cells[x][y].Size, raylib.Color{R: 128, G: 177, B: 136, A: 255}) + rl.DrawRectangleV(g.Cells[x][y].Position, g.Cells[x][y].Size, rl.Color{R: 128, G: 177, B: 136, A: 255}) } } } // Draw grid lines for i := int32(0); i < g.Cols+1; i++ { - raylib.DrawLineV( - raylib.NewVector2(float32(squareSize*i), 0), - raylib.NewVector2(float32(squareSize*i), float32(g.ScreenHeight)), - raylib.LightGray, + rl.DrawLineV( + rl.NewVector2(float32(squareSize*i), 0), + rl.NewVector2(float32(squareSize*i), float32(g.ScreenHeight)), + rl.LightGray, ) } for i := int32(0); i < g.Rows+1; i++ { - raylib.DrawLineV( - raylib.NewVector2(0, float32(squareSize*i)), - raylib.NewVector2(float32(g.ScreenWidth), float32(squareSize*i)), - raylib.LightGray, + rl.DrawLineV( + rl.NewVector2(0, float32(squareSize*i)), + rl.NewVector2(float32(g.ScreenWidth), float32(squareSize*i)), + rl.LightGray, ) } - raylib.EndDrawing() + rl.EndDrawing() } diff --git a/examples/games/snake/main.go b/examples/games/snake/main.go index 6396e29..aad9b52 100644 --- a/examples/games/snake/main.go +++ b/examples/games/snake/main.go @@ -11,18 +11,18 @@ const ( // Snake type type Snake struct { - Position raylib.Vector2 - Size raylib.Vector2 - Speed raylib.Vector2 - Color raylib.Color + Position rl.Vector2 + Size rl.Vector2 + Speed rl.Vector2 + Color rl.Color } // Food type type Food struct { - Position raylib.Vector2 - Size raylib.Vector2 + Position rl.Vector2 + Size rl.Vector2 Active bool - Color raylib.Color + Color rl.Color } // Game type @@ -36,9 +36,9 @@ type Game struct { Fruit Food Snake []Snake - SnakePosition []raylib.Vector2 + SnakePosition []rl.Vector2 AllowMove bool - Offset raylib.Vector2 + Offset rl.Vector2 CounterTail int } @@ -46,17 +46,17 @@ func main() { game := Game{} game.Init() - raylib.InitWindow(game.ScreenWidth, game.ScreenHeight, "sample game: snake") + rl.InitWindow(game.ScreenWidth, game.ScreenHeight, "sample game: snake") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { game.Update() game.Draw() } - raylib.CloseWindow() + rl.CloseWindow() } // Init - Initialize game @@ -71,58 +71,58 @@ func (g *Game) Init() { g.CounterTail = 1 g.AllowMove = false - g.Offset = raylib.Vector2{} + g.Offset = rl.Vector2{} g.Offset.X = float32(g.ScreenWidth % squareSize) g.Offset.Y = float32(g.ScreenHeight % squareSize) g.Snake = make([]Snake, snakeLength) for i := 0; i < snakeLength; i++ { - g.Snake[i].Position = raylib.NewVector2(g.Offset.X/2, g.Offset.Y/2) - g.Snake[i].Size = raylib.NewVector2(squareSize, squareSize) - g.Snake[i].Speed = raylib.NewVector2(squareSize, 0) + g.Snake[i].Position = rl.NewVector2(g.Offset.X/2, g.Offset.Y/2) + g.Snake[i].Size = rl.NewVector2(squareSize, squareSize) + g.Snake[i].Speed = rl.NewVector2(squareSize, 0) if i == 0 { - g.Snake[i].Color = raylib.DarkBlue + g.Snake[i].Color = rl.DarkBlue } else { - g.Snake[i].Color = raylib.Blue + g.Snake[i].Color = rl.Blue } } - g.SnakePosition = make([]raylib.Vector2, snakeLength) + g.SnakePosition = make([]rl.Vector2, snakeLength) for i := 0; i < snakeLength; i++ { - g.SnakePosition[i] = raylib.NewVector2(0.0, 0.0) + g.SnakePosition[i] = rl.NewVector2(0.0, 0.0) } - g.Fruit.Size = raylib.NewVector2(squareSize, squareSize) - g.Fruit.Color = raylib.SkyBlue + g.Fruit.Size = rl.NewVector2(squareSize, squareSize) + g.Fruit.Color = rl.SkyBlue g.Fruit.Active = false } // Update - Update game func (g *Game) Update() { if !g.GameOver { - if raylib.IsKeyPressed(raylib.KeyP) { + if rl.IsKeyPressed('P') { g.Pause = !g.Pause } if !g.Pause { // control - if raylib.IsKeyPressed(raylib.KeyRight) && g.Snake[0].Speed.X == 0 && g.AllowMove { - g.Snake[0].Speed = raylib.NewVector2(squareSize, 0) + if rl.IsKeyPressed(rl.KeyRight) && g.Snake[0].Speed.X == 0 && g.AllowMove { + g.Snake[0].Speed = rl.NewVector2(squareSize, 0) g.AllowMove = false } - if raylib.IsKeyPressed(raylib.KeyLeft) && g.Snake[0].Speed.X == 0 && g.AllowMove { - g.Snake[0].Speed = raylib.NewVector2(-squareSize, 0) + if rl.IsKeyPressed(rl.KeyLeft) && g.Snake[0].Speed.X == 0 && g.AllowMove { + g.Snake[0].Speed = rl.NewVector2(-squareSize, 0) g.AllowMove = false } - if raylib.IsKeyPressed(raylib.KeyUp) && g.Snake[0].Speed.Y == 0 && g.AllowMove { - g.Snake[0].Speed = raylib.NewVector2(0, -squareSize) + if rl.IsKeyPressed(rl.KeyUp) && g.Snake[0].Speed.Y == 0 && g.AllowMove { + g.Snake[0].Speed = rl.NewVector2(0, -squareSize) g.AllowMove = false } - if raylib.IsKeyPressed(raylib.KeyDown) && g.Snake[0].Speed.Y == 0 && g.AllowMove { - g.Snake[0].Speed = raylib.NewVector2(0, squareSize) + if rl.IsKeyPressed(rl.KeyDown) && g.Snake[0].Speed.Y == 0 && g.AllowMove { + g.Snake[0].Speed = rl.NewVector2(0, squareSize) g.AllowMove = false } @@ -159,16 +159,16 @@ func (g *Game) Update() { if !g.Fruit.Active { g.Fruit.Active = true - g.Fruit.Position = raylib.NewVector2( - float32(raylib.GetRandomValue(0, (g.ScreenWidth/squareSize)-1)*squareSize+int32(g.Offset.X)/2), - float32(raylib.GetRandomValue(0, (g.ScreenHeight/squareSize)-1)*squareSize+int32(g.Offset.Y)/2), + g.Fruit.Position = rl.NewVector2( + float32(rl.GetRandomValue(0, (g.ScreenWidth/squareSize)-1)*squareSize+int32(g.Offset.X)/2), + float32(rl.GetRandomValue(0, (g.ScreenHeight/squareSize)-1)*squareSize+int32(g.Offset.Y)/2), ) for i := 0; i < g.CounterTail; i++ { for (g.Fruit.Position.X == g.Snake[i].Position.X) && (g.Fruit.Position.Y == g.Snake[i].Position.Y) { - g.Fruit.Position = raylib.NewVector2( - float32(raylib.GetRandomValue(0, (g.ScreenWidth/squareSize)-1)*squareSize), - float32(raylib.GetRandomValue(0, (g.ScreenHeight/squareSize)-1)*squareSize), + g.Fruit.Position = rl.NewVector2( + float32(rl.GetRandomValue(0, (g.ScreenWidth/squareSize)-1)*squareSize), + float32(rl.GetRandomValue(0, (g.ScreenHeight/squareSize)-1)*squareSize), ) i = 0 } @@ -176,9 +176,9 @@ func (g *Game) Update() { } // collision - if raylib.CheckCollisionRecs( - raylib.NewRectangle(g.Snake[0].Position.X, g.Snake[0].Position.Y, g.Snake[0].Size.X, g.Snake[0].Size.Y), - raylib.NewRectangle(g.Fruit.Position.X, g.Fruit.Position.Y, g.Fruit.Size.X, g.Fruit.Size.Y), + if rl.CheckCollisionRecs( + rl.NewRectangle(g.Snake[0].Position.X, g.Snake[0].Position.Y, g.Snake[0].Size.X, g.Snake[0].Size.Y), + rl.NewRectangle(g.Fruit.Position.X, g.Fruit.Position.Y, g.Fruit.Size.X, g.Fruit.Size.Y), ) { g.Snake[g.CounterTail].Position = g.SnakePosition[g.CounterTail-1] g.CounterTail += 1 @@ -188,7 +188,7 @@ func (g *Game) Update() { g.FramesCounter++ } } else { - if raylib.IsKeyPressed(raylib.KeyEnter) { + if rl.IsKeyPressed(rl.KeyEnter) { g.Init() g.GameOver = false } @@ -197,42 +197,42 @@ func (g *Game) Update() { // Draw - Draw game func (g *Game) Draw() { - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) if !g.GameOver { // Draw grid lines for i := int32(0); i < g.ScreenWidth/squareSize+1; i++ { - raylib.DrawLineV( - raylib.NewVector2(float32(squareSize*i)+g.Offset.X/2, g.Offset.Y/2), - raylib.NewVector2(float32(squareSize*i)+g.Offset.X/2, float32(g.ScreenHeight)-g.Offset.Y/2), - raylib.LightGray, + rl.DrawLineV( + rl.NewVector2(float32(squareSize*i)+g.Offset.X/2, g.Offset.Y/2), + rl.NewVector2(float32(squareSize*i)+g.Offset.X/2, float32(g.ScreenHeight)-g.Offset.Y/2), + rl.LightGray, ) } for i := int32(0); i < g.ScreenHeight/squareSize+1; i++ { - raylib.DrawLineV( - raylib.NewVector2(g.Offset.X/2, float32(squareSize*i)+g.Offset.Y/2), - raylib.NewVector2(float32(g.ScreenWidth)-g.Offset.X/2, float32(squareSize*i)+g.Offset.Y/2), - raylib.LightGray, + rl.DrawLineV( + rl.NewVector2(g.Offset.X/2, float32(squareSize*i)+g.Offset.Y/2), + rl.NewVector2(float32(g.ScreenWidth)-g.Offset.X/2, float32(squareSize*i)+g.Offset.Y/2), + rl.LightGray, ) } // Draw snake for i := 0; i < g.CounterTail; i++ { - raylib.DrawRectangleV(g.Snake[i].Position, g.Snake[i].Size, g.Snake[i].Color) + rl.DrawRectangleV(g.Snake[i].Position, g.Snake[i].Size, g.Snake[i].Color) } // Draw fruit to pick - raylib.DrawRectangleV(g.Fruit.Position, g.Fruit.Size, g.Fruit.Color) + rl.DrawRectangleV(g.Fruit.Position, g.Fruit.Size, g.Fruit.Color) if g.Pause { - raylib.DrawText("GAME PAUSED", g.ScreenWidth/2-raylib.MeasureText("GAME PAUSED", 40)/2, g.ScreenHeight/2-40, 40, raylib.Gray) + rl.DrawText("GAME PAUSED", g.ScreenWidth/2-rl.MeasureText("GAME PAUSED", 40)/2, g.ScreenHeight/2-40, 40, rl.Gray) } } else { - raylib.DrawText("PRESS [ENTER] TO PLAY AGAIN", raylib.GetScreenWidth()/2-raylib.MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib.GetScreenHeight()/2-50, 20, raylib.Gray) + rl.DrawText("PRESS [ENTER] TO PLAY AGAIN", rl.GetScreenWidth()/2-rl.MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, rl.GetScreenHeight()/2-50, 20, rl.Gray) } - raylib.EndDrawing() + rl.EndDrawing() } diff --git a/examples/gui/basic_controls/main.go b/examples/gui/basic_controls/main.go index 7d392a5..107e2e7 100644 --- a/examples/gui/basic_controls/main.go +++ b/examples/gui/basic_controls/main.go @@ -11,9 +11,9 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.SetConfigFlags(raylib.FlagVsyncHint) + rl.SetConfigFlags(rl.FlagVsyncHint) - raylib.InitWindow(screenWidth, screenHeight, "raylib [gui] example - basic controls") + rl.InitWindow(screenWidth, screenHeight, "raylib [gui] example - basic controls") buttonToggle := true buttonClicked := false @@ -33,9 +33,9 @@ func main() { var inputText string - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { if buttonClicked { progressValue += 0.1 if progressValue >= 1.1 { @@ -43,50 +43,50 @@ func main() { } } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.Beige) + rl.ClearBackground(rl.Beige) - raygui.Label(raylib.NewRectangle(50, 50, 80, 20), "Label") + raygui.Label(rl.NewRectangle(50, 50, 80, 20), "Label") - buttonClicked = raygui.Button(raylib.NewRectangle(50, 70, 80, 40), "Button") + buttonClicked = raygui.Button(rl.NewRectangle(50, 70, 80, 40), "Button") - raygui.Label(raylib.NewRectangle(70, 140, 20, 20), "Checkbox") - checkboxChecked = raygui.CheckBox(raylib.NewRectangle(50, 140, 20, 20), checkboxChecked) + raygui.Label(rl.NewRectangle(70, 140, 20, 20), "Checkbox") + checkboxChecked = raygui.CheckBox(rl.NewRectangle(50, 140, 20, 20), checkboxChecked) - raygui.Label(raylib.NewRectangle(50, 190, 200, 20), "ProgressBar") - raygui.ProgressBar(raylib.NewRectangle(50, 210, 200, 20), progressValue) - raygui.Label(raylib.NewRectangle(200+50+5, 210, 20, 20), fmt.Sprintf("%.1f", progressValue)) + raygui.Label(rl.NewRectangle(50, 190, 200, 20), "ProgressBar") + raygui.ProgressBar(rl.NewRectangle(50, 210, 200, 20), progressValue) + raygui.Label(rl.NewRectangle(200+50+5, 210, 20, 20), fmt.Sprintf("%.1f", progressValue)) - raygui.Label(raylib.NewRectangle(50, 260, 200, 20), "Slider") - sliderValue = raygui.Slider(raylib.NewRectangle(50, 280, 200, 20), sliderValue, 0, 100) - raygui.Label(raylib.NewRectangle(200+50+5, 280, 20, 20), fmt.Sprintf("%.0f", sliderValue)) + raygui.Label(rl.NewRectangle(50, 260, 200, 20), "Slider") + sliderValue = raygui.Slider(rl.NewRectangle(50, 280, 200, 20), sliderValue, 0, 100) + raygui.Label(rl.NewRectangle(200+50+5, 280, 20, 20), fmt.Sprintf("%.0f", sliderValue)) - buttonToggle = raygui.ToggleButton(raylib.NewRectangle(50, 350, 100, 40), "ToggleButton", buttonToggle) + buttonToggle = raygui.ToggleButton(rl.NewRectangle(50, 350, 100, 40), "ToggleButton", buttonToggle) - raygui.Label(raylib.NewRectangle(500, 50, 200, 20), "ToggleGroup") - toggleActive = raygui.ToggleGroup(raylib.NewRectangle(500, 70, 60, 30), toggleText, toggleActive) + raygui.Label(rl.NewRectangle(500, 50, 200, 20), "ToggleGroup") + toggleActive = raygui.ToggleGroup(rl.NewRectangle(500, 70, 60, 30), toggleText, toggleActive) - raygui.Label(raylib.NewRectangle(500, 120, 200, 20), "SliderBar") - sliderBarValue = raygui.SliderBar(raylib.NewRectangle(500, 140, 200, 20), sliderBarValue, 0, 100) - raygui.Label(raylib.NewRectangle(500+200+5, 140, 20, 20), fmt.Sprintf("%.0f", sliderBarValue)) + raygui.Label(rl.NewRectangle(500, 120, 200, 20), "SliderBar") + sliderBarValue = raygui.SliderBar(rl.NewRectangle(500, 140, 200, 20), sliderBarValue, 0, 100) + raygui.Label(rl.NewRectangle(500+200+5, 140, 20, 20), fmt.Sprintf("%.0f", sliderBarValue)) - raygui.Label(raylib.NewRectangle(500, 190, 200, 20), "Spinner") - spinnerValue = raygui.Spinner(raylib.NewRectangle(500, 210, 200, 20), spinnerValue, 0, 100) + raygui.Label(rl.NewRectangle(500, 190, 200, 20), "Spinner") + spinnerValue = raygui.Spinner(rl.NewRectangle(500, 210, 200, 20), spinnerValue, 0, 100) - raygui.Label(raylib.NewRectangle(500, 260, 200, 20), "ComboBox") - comboActive = raygui.ComboBox(raylib.NewRectangle(500, 280, 200, 20), comboText, comboActive) + raygui.Label(rl.NewRectangle(500, 260, 200, 20), "ComboBox") + comboActive = raygui.ComboBox(rl.NewRectangle(500, 280, 200, 20), comboText, comboActive) if comboLastActive != comboActive { raygui.LoadGuiStyle(fmt.Sprintf("styles/%s.style", comboText[comboActive])) comboLastActive = comboActive } - raygui.Label(raylib.NewRectangle(500, 330, 200, 20), "TextBox") - inputText = raygui.TextBox(raylib.NewRectangle(500, 350, 200, 20), inputText) + raygui.Label(rl.NewRectangle(500, 330, 200, 20), "TextBox") + inputText = raygui.TextBox(rl.NewRectangle(500, 350, 200, 20), inputText) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/models/billboard/main.go b/examples/models/billboard/main.go index 6975b2a..8351b80 100644 --- a/examples/models/billboard/main.go +++ b/examples/models/billboard/main.go @@ -8,41 +8,41 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [models] example - drawing billboards") + rl.InitWindow(screenWidth, screenHeight, "raylib [models] example - drawing billboards") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(5.0, 4.0, 5.0) - camera.Target = raylib.NewVector3(0.0, 2.0, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera{} + camera.Position = rl.NewVector3(5.0, 4.0, 5.0) + camera.Target = rl.NewVector3(0.0, 2.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - camera.Type = raylib.CameraPerspective + camera.Type = rl.CameraPerspective - bill := raylib.LoadTexture("billboard.png") // Our texture billboard - billPosition := raylib.NewVector3(0.0, 2.0, 0.0) // Position where draw billboard + bill := rl.LoadTexture("billboard.png") // Our texture billboard + billPosition := rl.NewVector3(0.0, 2.0, 0.0) // Position where draw billboard - raylib.SetCameraMode(camera, raylib.CameraOrbital) // Set an orbital camera mode + rl.SetCameraMode(camera, rl.CameraOrbital) // Set an orbital camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.UpdateCamera(&camera) // Update camera + for !rl.WindowShouldClose() { + rl.UpdateCamera(&camera) // Update camera - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawBillboard(camera, bill, billPosition, 2.0, raylib.White) + rl.DrawBillboard(camera, bill, billPosition, 2.0, rl.White) - raylib.DrawGrid(10, 1.0) // Draw a grid + rl.DrawGrid(10, 1.0) // Draw a grid - raylib.EndMode3D() + rl.EndMode3D() - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(bill) // Unload texture + rl.UnloadTexture(bill) // Unload texture - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/models/box_collisions/main.go b/examples/models/box_collisions/main.go index d2867e4..e87a899 100644 --- a/examples/models/box_collisions/main.go +++ b/examples/models/box_collisions/main.go @@ -8,62 +8,62 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [models] example - box collisions") + rl.InitWindow(screenWidth, screenHeight, "raylib [models] example - box collisions") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(0.0, 10.0, 10.0) - camera.Target = raylib.NewVector3(0.0, 0.0, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera{} + camera.Position = rl.NewVector3(0.0, 10.0, 10.0) + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - camera.Type = raylib.CameraPerspective + camera.Type = rl.CameraPerspective - playerPosition := raylib.NewVector3(0.0, 1.0, 2.0) - playerSize := raylib.NewVector3(1.0, 2.0, 1.0) - playerColor := raylib.Green + playerPosition := rl.NewVector3(0.0, 1.0, 2.0) + playerSize := rl.NewVector3(1.0, 2.0, 1.0) + playerColor := rl.Green - enemyBoxPos := raylib.NewVector3(-4.0, 1.0, 0.0) - enemyBoxSize := raylib.NewVector3(2.0, 2.0, 2.0) + enemyBoxPos := rl.NewVector3(-4.0, 1.0, 0.0) + enemyBoxSize := rl.NewVector3(2.0, 2.0, 2.0) - enemySpherePos := raylib.NewVector3(4.0, 0.0, 0.0) + enemySpherePos := rl.NewVector3(4.0, 0.0, 0.0) enemySphereSize := float32(1.5) collision := false - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update // Move player - if raylib.IsKeyDown(raylib.KeyRight) { + if rl.IsKeyDown(rl.KeyRight) { playerPosition.X += 0.2 - } else if raylib.IsKeyDown(raylib.KeyLeft) { + } else if rl.IsKeyDown(rl.KeyLeft) { playerPosition.X -= 0.2 - } else if raylib.IsKeyDown(raylib.KeyDown) { + } else if rl.IsKeyDown(rl.KeyDown) { playerPosition.Z += 0.2 - } else if raylib.IsKeyDown(raylib.KeyUp) { + } else if rl.IsKeyDown(rl.KeyUp) { playerPosition.Z -= 0.2 } collision = false // Check collisions player vs enemy-box - if raylib.CheckCollisionBoxes( - raylib.NewBoundingBox( - raylib.NewVector3(playerPosition.X-playerSize.X/2, playerPosition.Y-playerSize.Y/2, playerPosition.Z-playerSize.Z/2), - raylib.NewVector3(playerPosition.X+playerSize.X/2, playerPosition.Y+playerSize.Y/2, playerPosition.Z+playerSize.Z/2)), - raylib.NewBoundingBox( - raylib.NewVector3(enemyBoxPos.X-enemyBoxSize.X/2, enemyBoxPos.Y-enemyBoxSize.Y/2, enemyBoxPos.Z-enemyBoxSize.Z/2), - raylib.NewVector3(enemyBoxPos.X+enemyBoxSize.X/2, enemyBoxPos.Y+enemyBoxSize.Y/2, enemyBoxPos.Z+enemyBoxSize.Z/2)), + if rl.CheckCollisionBoxes( + rl.NewBoundingBox( + rl.NewVector3(playerPosition.X-playerSize.X/2, playerPosition.Y-playerSize.Y/2, playerPosition.Z-playerSize.Z/2), + rl.NewVector3(playerPosition.X+playerSize.X/2, playerPosition.Y+playerSize.Y/2, playerPosition.Z+playerSize.Z/2)), + rl.NewBoundingBox( + rl.NewVector3(enemyBoxPos.X-enemyBoxSize.X/2, enemyBoxPos.Y-enemyBoxSize.Y/2, enemyBoxPos.Z-enemyBoxSize.Z/2), + rl.NewVector3(enemyBoxPos.X+enemyBoxSize.X/2, enemyBoxPos.Y+enemyBoxSize.Y/2, enemyBoxPos.Z+enemyBoxSize.Z/2)), ) { collision = true } // Check collisions player vs enemy-sphere - if raylib.CheckCollisionBoxSphere( - raylib.NewBoundingBox( - raylib.NewVector3(playerPosition.X-playerSize.X/2, playerPosition.Y-playerSize.Y/2, playerPosition.Z-playerSize.Z/2), - raylib.NewVector3(playerPosition.X+playerSize.X/2, playerPosition.Y+playerSize.Y/2, playerPosition.Z+playerSize.Z/2)), + if rl.CheckCollisionBoxSphere( + rl.NewBoundingBox( + rl.NewVector3(playerPosition.X-playerSize.X/2, playerPosition.Y-playerSize.Y/2, playerPosition.Z-playerSize.Z/2), + rl.NewVector3(playerPosition.X+playerSize.X/2, playerPosition.Y+playerSize.Y/2, playerPosition.Z+playerSize.Z/2)), enemySpherePos, enemySphereSize, ) { @@ -71,40 +71,40 @@ func main() { } if collision { - playerColor = raylib.Red + playerColor = rl.Red } else { - playerColor = raylib.Green + playerColor = rl.Green } // Draw - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) // Draw enemy-box - raylib.DrawCube(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, raylib.Gray) - raylib.DrawCubeWires(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, raylib.DarkGray) + rl.DrawCube(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, rl.Gray) + rl.DrawCubeWires(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, rl.DarkGray) // Draw enemy-sphere - raylib.DrawSphere(enemySpherePos, enemySphereSize, raylib.Gray) - raylib.DrawSphereWires(enemySpherePos, enemySphereSize, 16, 16, raylib.DarkGray) + rl.DrawSphere(enemySpherePos, enemySphereSize, rl.Gray) + rl.DrawSphereWires(enemySpherePos, enemySphereSize, 16, 16, rl.DarkGray) // Draw player - raylib.DrawCubeV(playerPosition, playerSize, playerColor) + rl.DrawCubeV(playerPosition, playerSize, playerColor) - raylib.DrawGrid(10, 1.0) // Draw a grid + rl.DrawGrid(10, 1.0) // Draw a grid - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawText("Move player with cursors to collide", 220, 40, 20, raylib.Gray) + rl.DrawText("Move player with cursors to collide", 220, 40, 20, rl.Gray) - raylib.DrawFPS(10, 10) + rl.DrawFPS(10, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/models/cubicmap/main.go b/examples/models/cubicmap/main.go index f105825..32e395c 100644 --- a/examples/models/cubicmap/main.go +++ b/examples/models/cubicmap/main.go @@ -8,64 +8,64 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [models] example - cubesmap loading and drawing") + rl.InitWindow(screenWidth, screenHeight, "raylib [models] example - cubesmap loading and drawing") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(16.0, 14.0, 16.0) - camera.Target = raylib.NewVector3(0.0, 0.0, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera{} + camera.Position = rl.NewVector3(16.0, 14.0, 16.0) + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - camera.Type = raylib.CameraPerspective + camera.Type = rl.CameraPerspective - image := raylib.LoadImage("cubicmap.png") // Load cubicmap image (RAM) - cubicmap := raylib.LoadTextureFromImage(image) // Convert image to texture to display (VRAM) + image := rl.LoadImage("cubicmap.png") // Load cubicmap image (RAM) + cubicmap := rl.LoadTextureFromImage(image) // Convert image to texture to display (VRAM) - mesh := raylib.GenMeshCubicmap(*image, raylib.NewVector3(1.0, 1.0, 1.0)) - model := raylib.LoadModelFromMesh(mesh) + mesh := rl.GenMeshCubicmap(*image, rl.NewVector3(1.0, 1.0, 1.0)) + model := rl.LoadModelFromMesh(mesh) // NOTE: By default each cube is mapped to one part of texture atlas - texture := raylib.LoadTexture("cubicmap_atlas.png") // Load map texture - model.Material.Maps[raylib.MapDiffuse].Texture = texture // Set map diffuse texture + texture := rl.LoadTexture("cubicmap_atlas.png") // Load map texture + model.Material.Maps[rl.MapDiffuse].Texture = texture // Set map diffuse texture - mapPosition := raylib.NewVector3(-16.0, 0.0, -8.0) // Set model position + mapPosition := rl.NewVector3(-16.0, 0.0, -8.0) // Set model position - raylib.UnloadImage(image) // Unload cubicmap image from RAM, already uploaded to VRAM + rl.UnloadImage(image) // Unload cubicmap image from RAM, already uploaded to VRAM - raylib.SetCameraMode(camera, raylib.CameraOrbital) // Set an orbital camera mode + rl.SetCameraMode(camera, rl.CameraOrbital) // Set an orbital camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update - raylib.UpdateCamera(&camera) // Update camera + rl.UpdateCamera(&camera) // Update camera // Draw - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawModel(model, mapPosition, 1.0, raylib.White) + rl.DrawModel(model, mapPosition, 1.0, rl.White) - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawTextureEx(cubicmap, raylib.NewVector2(float32(screenWidth-cubicmap.Width*4-20), 20), 0.0, 4.0, raylib.White) - raylib.DrawRectangleLines(screenWidth-cubicmap.Width*4-20, 20, cubicmap.Width*4, cubicmap.Height*4, raylib.Green) + rl.DrawTextureEx(cubicmap, rl.NewVector2(float32(screenWidth-cubicmap.Width*4-20), 20), 0.0, 4.0, rl.White) + rl.DrawRectangleLines(screenWidth-cubicmap.Width*4-20, 20, cubicmap.Width*4, cubicmap.Height*4, rl.Green) - raylib.DrawText("cubicmap image used to", 658, 90, 10, raylib.Gray) - raylib.DrawText("generate map 3d model", 658, 104, 10, raylib.Gray) + rl.DrawText("cubicmap image used to", 658, 90, 10, rl.Gray) + rl.DrawText("generate map 3d model", 658, 104, 10, rl.Gray) - raylib.DrawFPS(10, 10) + rl.DrawFPS(10, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(cubicmap) // Unload cubicmap texture - raylib.UnloadTexture(texture) // Unload map texture - raylib.UnloadModel(model) // Unload map model + rl.UnloadTexture(cubicmap) // Unload cubicmap texture + rl.UnloadTexture(texture) // Unload map texture + rl.UnloadModel(model) // Unload map model - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/models/geometric_shapes/main.go b/examples/models/geometric_shapes/main.go index 04e734d..31a6397 100644 --- a/examples/models/geometric_shapes/main.go +++ b/examples/models/geometric_shapes/main.go @@ -8,45 +8,45 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes") + rl.InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(0.0, 10.0, 10.0) - camera.Target = raylib.NewVector3(0.0, 0.0, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera{} + camera.Position = rl.NewVector3(0.0, 10.0, 10.0) + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawCube(raylib.NewVector3(-4.0, 0.0, 2.0), 2.0, 5.0, 2.0, raylib.Red) - raylib.DrawCubeWires(raylib.NewVector3(-4.0, 0.0, 2.0), 2.0, 5.0, 2.0, raylib.Gold) - raylib.DrawCubeWires(raylib.NewVector3(-4.0, 0.0, -2.0), 3.0, 6.0, 2.0, raylib.Maroon) + rl.DrawCube(rl.NewVector3(-4.0, 0.0, 2.0), 2.0, 5.0, 2.0, rl.Red) + rl.DrawCubeWires(rl.NewVector3(-4.0, 0.0, 2.0), 2.0, 5.0, 2.0, rl.Gold) + rl.DrawCubeWires(rl.NewVector3(-4.0, 0.0, -2.0), 3.0, 6.0, 2.0, rl.Maroon) - raylib.DrawSphere(raylib.NewVector3(-1.0, 0.0, -2.0), 1.0, raylib.Green) - raylib.DrawSphereWires(raylib.NewVector3(1.0, 0.0, 2.0), 2.0, 16, 16, raylib.Lime) + rl.DrawSphere(rl.NewVector3(-1.0, 0.0, -2.0), 1.0, rl.Green) + rl.DrawSphereWires(rl.NewVector3(1.0, 0.0, 2.0), 2.0, 16, 16, rl.Lime) - raylib.DrawCylinder(raylib.NewVector3(4.0, 0.0, -2.0), 1.0, 2.0, 3.0, 4, raylib.SkyBlue) - raylib.DrawCylinderWires(raylib.NewVector3(4.0, 0.0, -2.0), 1.0, 2.0, 3.0, 4, raylib.DarkBlue) - raylib.DrawCylinderWires(raylib.NewVector3(4.5, -1.0, 2.0), 1.0, 1.0, 2.0, 6, raylib.Brown) + rl.DrawCylinder(rl.NewVector3(4.0, 0.0, -2.0), 1.0, 2.0, 3.0, 4, rl.SkyBlue) + rl.DrawCylinderWires(rl.NewVector3(4.0, 0.0, -2.0), 1.0, 2.0, 3.0, 4, rl.DarkBlue) + rl.DrawCylinderWires(rl.NewVector3(4.5, -1.0, 2.0), 1.0, 1.0, 2.0, 6, rl.Brown) - raylib.DrawCylinder(raylib.NewVector3(1.0, 0.0, -4.0), 0.0, 1.5, 3.0, 8, raylib.Gold) - raylib.DrawCylinderWires(raylib.NewVector3(1.0, 0.0, -4.0), 0.0, 1.5, 3.0, 8, raylib.Pink) + rl.DrawCylinder(rl.NewVector3(1.0, 0.0, -4.0), 0.0, 1.5, 3.0, 8, rl.Gold) + rl.DrawCylinderWires(rl.NewVector3(1.0, 0.0, -4.0), 0.0, 1.5, 3.0, 8, rl.Pink) - raylib.DrawGrid(10, 1.0) // Draw a grid + rl.DrawGrid(10, 1.0) // Draw a grid - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawFPS(10, 10) + rl.DrawFPS(10, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/models/heightmap/main.go b/examples/models/heightmap/main.go index 9e59043..b74bf42 100644 --- a/examples/models/heightmap/main.go +++ b/examples/models/heightmap/main.go @@ -8,58 +8,58 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap loading and drawing") + rl.InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap loading and drawing") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(18.0, 16.0, 18.0) - camera.Target = raylib.NewVector3(0.0, 0.0, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera{} + camera.Position = rl.NewVector3(18.0, 16.0, 18.0) + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - image := raylib.LoadImage("heightmap.png") // Load heightmap image (RAM) - texture := raylib.LoadTextureFromImage(image) // Convert image to texture (VRAM) + image := rl.LoadImage("heightmap.png") // Load heightmap image (RAM) + texture := rl.LoadTextureFromImage(image) // Convert image to texture (VRAM) - mesh := raylib.GenMeshHeightmap(*image, raylib.NewVector3(16, 8, 16)) // Generate heightmap mesh (RAM and VRAM) - model := raylib.LoadModelFromMesh(mesh) // Load model from generated mesh + mesh := rl.GenMeshHeightmap(*image, rl.NewVector3(16, 8, 16)) // Generate heightmap mesh (RAM and VRAM) + model := rl.LoadModelFromMesh(mesh) // Load model from generated mesh - model.Material.Maps[raylib.MapDiffuse].Texture = texture // Set map diffuse texture - mapPosition := raylib.NewVector3(-8.0, 0.0, -8.0) // Set model position + model.Material.Maps[rl.MapDiffuse].Texture = texture // Set map diffuse texture + mapPosition := rl.NewVector3(-8.0, 0.0, -8.0) // Set model position - raylib.UnloadImage(image) // Unload heightmap image from RAM, already uploaded to VRAM + rl.UnloadImage(image) // Unload heightmap image from RAM, already uploaded to VRAM - raylib.SetCameraMode(camera, raylib.CameraOrbital) // Set an orbital camera mode + rl.SetCameraMode(camera, rl.CameraOrbital) // Set an orbital camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update - raylib.UpdateCamera(&camera) // Update camera + rl.UpdateCamera(&camera) // Update camera // Draw - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawModel(model, mapPosition, 1.0, raylib.Red) + rl.DrawModel(model, mapPosition, 1.0, rl.Red) - raylib.DrawGrid(20, 1.0) + rl.DrawGrid(20, 1.0) - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawTexture(texture, screenWidth-texture.Width-20, 20, raylib.White) - raylib.DrawRectangleLines(screenWidth-texture.Width-20, 20, texture.Width, texture.Height, raylib.Green) + rl.DrawTexture(texture, screenWidth-texture.Width-20, 20, rl.White) + rl.DrawRectangleLines(screenWidth-texture.Width-20, 20, texture.Width, texture.Height, rl.Green) - raylib.DrawFPS(10, 10) + rl.DrawFPS(10, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) // Unload map texture - raylib.UnloadModel(model) // Unload map model + rl.UnloadTexture(texture) // Unload map texture + rl.UnloadModel(model) // Unload map model - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/models/obj_loading/main.go b/examples/models/obj_loading/main.go index 2517b22..b15071a 100644 --- a/examples/models/obj_loading/main.go +++ b/examples/models/obj_loading/main.go @@ -8,45 +8,45 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [models] example - obj model loading") + rl.InitWindow(screenWidth, screenHeight, "raylib [models] example - obj model loading") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(3.0, 3.0, 3.0) - camera.Target = raylib.NewVector3(0.0, 1.5, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera{} + camera.Position = rl.NewVector3(3.0, 3.0, 3.0) + camera.Target = rl.NewVector3(0.0, 1.5, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - dwarf := raylib.LoadModel("dwarf.obj") // Load OBJ model - texture := raylib.LoadTexture("dwarf_diffuse.png") // Load model texture + dwarf := rl.LoadModel("dwarf.obj") // Load OBJ model + texture := rl.LoadTexture("dwarf_diffuse.png") // Load model texture - dwarf.Material.Maps[raylib.MapDiffuse].Texture = texture // Set dwarf model diffuse texture + dwarf.Material.Maps[rl.MapDiffuse].Texture = texture // Set dwarf model diffuse texture - position := raylib.NewVector3(0.0, 0.0, 0.0) // Set model position + position := rl.NewVector3(0.0, 0.0, 0.0) // Set model position - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawModel(dwarf, position, 2.0, raylib.White) // Draw 3d model with texture + rl.DrawModel(dwarf, position, 2.0, rl.White) // Draw 3d model with texture - raylib.DrawGrid(10, 1.0) // Draw a grid + rl.DrawGrid(10, 1.0) // Draw a grid - raylib.DrawGizmo(position) // Draw gizmo + rl.DrawGizmo(position) // Draw gizmo - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawText("(c) Dwarf 3D model by David Moreno", screenWidth-200, screenHeight-20, 10, raylib.Gray) + rl.DrawText("(c) Dwarf 3D model by David Moreno", screenWidth-200, screenHeight-20, 10, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) // Unload texture - raylib.UnloadModel(dwarf) // Unload model + rl.UnloadTexture(texture) // Unload texture + rl.UnloadModel(dwarf) // Unload model - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/others/android/example/main.go b/examples/others/android/example/main.go index 9d9bea0..dc2d7a6 100644 --- a/examples/others/android/example/main.go +++ b/examples/others/android/example/main.go @@ -16,36 +16,36 @@ const ( ) func init() { - raylib.SetCallbackFunc(main) + rl.SetCallbackFunc(main) } func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.SetConfigFlags(raylib.FlagVsyncHint) + rl.SetConfigFlags(rl.FlagVsyncHint) - raylib.InitWindow(screenWidth, screenHeight, "Android example") + rl.InitWindow(screenWidth, screenHeight, "Android example") - raylib.InitAudioDevice() + rl.InitAudioDevice() currentScreen := Logo windowShouldClose := false - texture := raylib.LoadTexture("raylib_logo.png") // Load texture (placed on assets folder) - fx := raylib.LoadSound("coin.wav") // Load WAV audio file (placed on assets folder) - ambient := raylib.LoadMusicStream("ambient.ogg") // Load music + texture := rl.LoadTexture("raylib_logo.png") // Load texture (placed on assets folder) + fx := rl.LoadSound("coin.wav") // Load WAV audio file (placed on assets folder) + ambient := rl.LoadMusicStream("ambient.ogg") // Load music - raylib.PlayMusicStream(ambient) + rl.PlayMusicStream(ambient) framesCounter := 0 // Used to count frames - //raylib.SetTargetFPS(60) + //rl.SetTargetFPS(60) for !windowShouldClose { - raylib.UpdateMusicStream(ambient) + rl.UpdateMusicStream(ambient) - if runtime.GOOS == "android" && raylib.IsKeyDown(raylib.KeyBack) || raylib.WindowShouldClose() { + if runtime.GOOS == "android" && rl.IsKeyDown(rl.KeyBack) || rl.WindowShouldClose() { windowShouldClose = true } @@ -60,64 +60,64 @@ func main() { break case Title: // Press enter to change to GamePlay screen - if raylib.IsGestureDetected(raylib.GestureTap) { - raylib.PlaySound(fx) + if rl.IsGestureDetected(rl.GestureTap) { + rl.PlaySound(fx) currentScreen = GamePlay } break case GamePlay: // Press enter to change to Ending screen - if raylib.IsGestureDetected(raylib.GestureTap) { - raylib.PlaySound(fx) + if rl.IsGestureDetected(rl.GestureTap) { + rl.PlaySound(fx) currentScreen = Ending } break case Ending: // Press enter to return to Title screen - if raylib.IsGestureDetected(raylib.GestureTap) { - raylib.PlaySound(fx) + if rl.IsGestureDetected(rl.GestureTap) { + rl.PlaySound(fx) currentScreen = Title } break } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) switch currentScreen { case Logo: - raylib.DrawText("LOGO SCREEN", 20, 20, 40, raylib.LightGray) - raylib.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, raylib.White) - raylib.DrawText("WAIT for 4 SECONDS...", 290, 400, 20, raylib.Gray) + rl.DrawText("LOGO SCREEN", 20, 20, 40, rl.LightGray) + rl.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, rl.White) + rl.DrawText("WAIT for 4 SECONDS...", 290, 400, 20, rl.Gray) break case Title: - raylib.DrawRectangle(0, 0, screenWidth, screenHeight, raylib.Green) - raylib.DrawText("TITLE SCREEN", 20, 20, 40, raylib.DarkGreen) - raylib.DrawText("TAP SCREEN to JUMP to GAMEPLAY SCREEN", 160, 220, 20, raylib.DarkGreen) + rl.DrawRectangle(0, 0, screenWidth, screenHeight, rl.Green) + rl.DrawText("TITLE SCREEN", 20, 20, 40, rl.DarkGreen) + rl.DrawText("TAP SCREEN to JUMP to GAMEPLAY SCREEN", 160, 220, 20, rl.DarkGreen) break case GamePlay: - raylib.DrawRectangle(0, 0, screenWidth, screenHeight, raylib.Purple) - raylib.DrawText("GAMEPLAY SCREEN", 20, 20, 40, raylib.Maroon) - raylib.DrawText("TAP SCREEN to JUMP to ENDING SCREEN", 170, 220, 20, raylib.Maroon) + rl.DrawRectangle(0, 0, screenWidth, screenHeight, rl.Purple) + rl.DrawText("GAMEPLAY SCREEN", 20, 20, 40, rl.Maroon) + rl.DrawText("TAP SCREEN to JUMP to ENDING SCREEN", 170, 220, 20, rl.Maroon) break case Ending: - raylib.DrawRectangle(0, 0, screenWidth, screenHeight, raylib.Blue) - raylib.DrawText("ENDING SCREEN", 20, 20, 40, raylib.DarkBlue) - raylib.DrawText("TAP SCREEN to RETURN to TITLE SCREEN", 160, 220, 20, raylib.DarkBlue) + rl.DrawRectangle(0, 0, screenWidth, screenHeight, rl.Blue) + rl.DrawText("ENDING SCREEN", 20, 20, 40, rl.DarkBlue) + rl.DrawText("TAP SCREEN to RETURN to TITLE SCREEN", 160, 220, 20, rl.DarkBlue) break default: break } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadSound(fx) // Unload sound data - raylib.UnloadMusicStream(ambient) // Unload music stream data - raylib.CloseAudioDevice() // Close audio device (music streaming is automatically stopped) - raylib.UnloadTexture(texture) // Unload texture data - raylib.CloseWindow() // Close window + rl.UnloadSound(fx) // Unload sound data + rl.UnloadMusicStream(ambient) // Unload music stream data + rl.CloseAudioDevice() // Close audio device (music streaming is automatically stopped) + rl.UnloadTexture(texture) // Unload texture data + rl.CloseWindow() // Close window os.Exit(0) } diff --git a/examples/others/bunnymark/main.go b/examples/others/bunnymark/main.go index 70a12f8..d7f1b43 100644 --- a/examples/others/bunnymark/main.go +++ b/examples/others/bunnymark/main.go @@ -8,33 +8,33 @@ import ( // Bunny type type Bunny struct { - Position raylib.Vector2 - Speed raylib.Vector2 - Color raylib.Color + Position rl.Vector2 + Speed rl.Vector2 + Color rl.Color } func main() { screenWidth := int32(1280) screenHeight := int32(960) - raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - Bunnymark") + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - Bunnymark") - texture := raylib.LoadTexture("wabbit_alpha.png") + texture := rl.LoadTexture("wabbit_alpha.png") bunnies := make([]*Bunny, 0) bunniesCount := 0 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { // Create more bunnies for i := 0; i < 100; i++ { b := &Bunny{} - b.Position = raylib.GetMousePosition() - b.Speed.X = float32(raylib.GetRandomValue(250, 500)) / 60.0 - b.Speed.Y = float32(raylib.GetRandomValue(250, 500)-500) / 60.0 + b.Position = rl.GetMousePosition() + b.Speed.X = float32(rl.GetRandomValue(250, 500)) / 60.0 + b.Speed.Y = float32(rl.GetRandomValue(250, 500)-500) / 60.0 bunnies = append(bunnies, b) bunniesCount++ @@ -55,27 +55,27 @@ func main() { } } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) for _, b := range bunnies { // NOTE: When internal QUADS batch limit is reached, a draw call is launched and // batching buffer starts being filled again; before launching the draw call, // updated vertex data from internal buffer is send to GPU... it seems it generates // a stall and consequently a frame drop, limiting number of bunnies drawn at 60 fps - raylib.DrawTexture(texture, int32(b.Position.X), int32(b.Position.Y), raylib.RayWhite) + rl.DrawTexture(texture, int32(b.Position.X), int32(b.Position.Y), rl.RayWhite) } - raylib.DrawRectangle(0, 0, screenWidth, 40, raylib.LightGray) - raylib.DrawText("raylib bunnymark", 10, 10, 20, raylib.DarkGray) - raylib.DrawText(fmt.Sprintf("bunnies: %d", bunniesCount), 400, 10, 20, raylib.Red) + rl.DrawRectangle(0, 0, screenWidth, 40, rl.LightGray) + rl.DrawText("raylib bunnymark", 10, 10, 20, rl.DarkGray) + rl.DrawText(fmt.Sprintf("bunnies: %d", bunniesCount), 400, 10, 20, rl.Red) - raylib.DrawFPS(260, 10) + rl.DrawFPS(260, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) - raylib.CloseWindow() + rl.UnloadTexture(texture) + rl.CloseWindow() } diff --git a/examples/others/resources/main.go b/examples/others/resources/main.go index 1a93480..1b9bd84 100644 --- a/examples/others/resources/main.go +++ b/examples/others/resources/main.go @@ -13,14 +13,14 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - resources loading") + rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - resources loading") - raylib.InitAudioDevice() + rl.InitAudioDevice() // OpenAsset() will also work on Android (reads files from assets/) - reader, err := raylib.OpenAsset("data.rres") + reader, err := rl.OpenAsset("data.rres") if err != nil { - raylib.TraceLog(raylib.LogWarning, "[%s] rRES raylib resource file could not be opened: %v", "data.rres", err) + rl.TraceLog(rl.LogWarning, "[%s] rRES raylib resource file could not be opened: %v", "data.rres", err) } defer reader.Close() @@ -30,67 +30,67 @@ func main() { //reader := bytes.NewReader(b) res := rres.LoadResource(reader, 0, []byte("passwordpassword")) - wav := raylib.LoadWaveEx(res.Data, int32(res.Param1), int32(res.Param2), int32(res.Param3), int32(res.Param4)) - snd := raylib.LoadSoundFromWave(wav) - raylib.UnloadWave(wav) + wav := rl.LoadWaveEx(res.Data, int32(res.Param1), int32(res.Param2), int32(res.Param3), int32(res.Param4)) + snd := rl.LoadSoundFromWave(wav) + rl.UnloadWave(wav) - textures := make([]raylib.Texture2D, numTextures) + textures := make([]rl.Texture2D, numTextures) for i := 0; i < numTextures; i++ { r := rres.LoadResource(reader, i+1, []byte("passwordpassword")) - image := raylib.LoadImagePro(r.Data, int32(r.Param1), int32(r.Param2), raylib.PixelFormat(r.Param3)) - textures[i] = raylib.LoadTextureFromImage(image) - raylib.UnloadImage(image) + image := rl.LoadImagePro(r.Data, int32(r.Param1), int32(r.Param2), rl.PixelFormat(r.Param3)) + textures[i] = rl.LoadTextureFromImage(image) + rl.UnloadImage(image) } currentTexture := 0 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsKeyPressed(raylib.KeySpace) { - raylib.PlaySound(snd) + for !rl.WindowShouldClose() { + if rl.IsKeyPressed(rl.KeySpace) { + rl.PlaySound(snd) } - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + if rl.IsMouseButtonPressed(rl.MouseLeftButton) { currentTexture = (currentTexture + 1) % numTextures // Cycle between the textures } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawTexture(textures[currentTexture], screenWidth/2-textures[currentTexture].Width/2, screenHeight/2-textures[currentTexture].Height/2, raylib.RayWhite) + rl.DrawTexture(textures[currentTexture], screenWidth/2-textures[currentTexture].Width/2, screenHeight/2-textures[currentTexture].Height/2, rl.RayWhite) - raylib.DrawText("MOUSE LEFT BUTTON to CYCLE TEXTURES", 40, 410, 10, raylib.Gray) - raylib.DrawText("SPACE to PLAY SOUND", 40, 430, 10, raylib.Gray) + rl.DrawText("MOUSE LEFT BUTTON to CYCLE TEXTURES", 40, 410, 10, rl.Gray) + rl.DrawText("SPACE to PLAY SOUND", 40, 430, 10, rl.Gray) switch currentTexture { case 0: - raylib.DrawText("GIF", 272, 70, 20, raylib.Gray) + rl.DrawText("GIF", 272, 70, 20, rl.Gray) break case 1: - raylib.DrawText("JPEG", 272, 70, 20, raylib.Gray) + rl.DrawText("JPEG", 272, 70, 20, rl.Gray) break case 2: - raylib.DrawText("PNG", 272, 70, 20, raylib.Gray) + rl.DrawText("PNG", 272, 70, 20, rl.Gray) break case 3: - raylib.DrawText("TGA", 272, 70, 20, raylib.Gray) + rl.DrawText("TGA", 272, 70, 20, rl.Gray) break default: break } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadSound(snd) + rl.UnloadSound(snd) for _, t := range textures { - raylib.UnloadTexture(t) + rl.UnloadTexture(t) } - raylib.CloseAudioDevice() + rl.CloseAudioDevice() - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/others/rpi/basic_window/main.go b/examples/others/rpi/basic_window/main.go index 7785d1a..38749a8 100644 --- a/examples/others/rpi/basic_window/main.go +++ b/examples/others/rpi/basic_window/main.go @@ -3,19 +3,19 @@ package main import "github.com/gen2brain/raylib-go/raylib" func main() { - raylib.InitWindow(800, 450, "raylib [rpi] example - basic window") + rl.InitWindow(800, 450, "raylib [rpi] example - basic window") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("Congrats! You created your first window!", 190, 200, 20, raylib.LightGray) + rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LightGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/physics/box2d/main.go b/examples/physics/box2d/main.go index a053a4b..d0e61b6 100644 --- a/examples/physics/box2d/main.go +++ b/examples/physics/box2d/main.go @@ -30,28 +30,28 @@ func (g *Game) Init() { // Update - Update game func (g *Game) Update() { // Keys 1-9 switch demos - switch raylib.GetKeyPressed() { - case raylib.KeyOne: + switch rl.GetKeyPressed() { + case rl.KeyOne: g.Demo1() - case raylib.KeyTwo: + case rl.KeyTwo: g.Demo2() - case raylib.KeyThree: + case rl.KeyThree: g.Demo3() - case raylib.KeyFour: + case rl.KeyFour: g.Demo4() - case raylib.KeyFive: + case rl.KeyFive: g.Demo5() - case raylib.KeySix: + case rl.KeySix: g.Demo6() - case raylib.KeySeven: + case rl.KeySeven: g.Demo7() - case raylib.KeyEight: + case rl.KeyEight: g.Demo8() - case raylib.KeyNine: + case rl.KeyNine: g.Demo9() } - g.TimeStep = float64(raylib.GetFrameTime()) + g.TimeStep = float64(rl.GetFrameTime()) // Physics steps calculations g.World.Step(g.TimeStep) @@ -66,7 +66,7 @@ func (g *Game) Draw() { g.DrawJoint(j) } - raylib.DrawText("Use keys 1-9 to switch current demo", 20, 20, 10, raylib.RayWhite) + rl.DrawText("Use keys 1-9 to switch current demo", 20, 20, 10, rl.RayWhite) } // DrawBody - Draw body @@ -83,10 +83,10 @@ func (g *Game) DrawBody(b *box2d.Body) { v3 := o.Add(S.MulV(x.Add(R.MulV(box2d.Vec2{h.X, h.Y})))) v4 := o.Add(S.MulV(x.Add(R.MulV(box2d.Vec2{-h.X, h.Y})))) - raylib.DrawLine(int32(v1.X), int32(v1.Y), int32(v2.X), int32(v2.Y), raylib.RayWhite) - raylib.DrawLine(int32(v2.X), int32(v2.Y), int32(v3.X), int32(v3.Y), raylib.RayWhite) - raylib.DrawLine(int32(v3.X), int32(v3.Y), int32(v4.X), int32(v4.Y), raylib.RayWhite) - raylib.DrawLine(int32(v4.X), int32(v4.Y), int32(v1.X), int32(v1.Y), raylib.RayWhite) + rl.DrawLine(int32(v1.X), int32(v1.Y), int32(v2.X), int32(v2.Y), rl.RayWhite) + rl.DrawLine(int32(v2.X), int32(v2.Y), int32(v3.X), int32(v3.Y), rl.RayWhite) + rl.DrawLine(int32(v3.X), int32(v3.Y), int32(v4.X), int32(v4.Y), rl.RayWhite) + rl.DrawLine(int32(v4.X), int32(v4.Y), int32(v1.X), int32(v1.Y), rl.RayWhite) } // DrawJoint - Draw joint @@ -111,8 +111,8 @@ func (g *Game) DrawJoint(j *box2d.Joint) { x2 = o.Add(S.MulV(x2)) p2 = o.Add(S.MulV(p2)) - raylib.DrawLine(int32(x1.X), int32(x1.Y), int32(p1.X), int32(p1.Y), raylib.RayWhite) - raylib.DrawLine(int32(x2.X), int32(x2.Y), int32(p2.X), int32(p2.Y), raylib.RayWhite) + rl.DrawLine(int32(x1.X), int32(x1.Y), int32(p1.X), int32(p1.Y), rl.RayWhite) + rl.DrawLine(int32(x2.X), int32(x2.Y), int32(p2.X), int32(p2.Y), rl.RayWhite) } // Demo1 - Single box @@ -489,25 +489,25 @@ func (g *Game) Demo9() { } func main() { - raylib.InitWindow(800, 450, "raylib [physics] example - box2d") + rl.InitWindow(800, 450, "raylib [physics] example - box2d") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) game := NewGame() game.Demo1() - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.Black) + rl.ClearBackground(rl.Black) game.Update() game.Draw() - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/physics/chipmunk/main.go b/examples/physics/chipmunk/main.go index 85a629e..b5d46d4 100644 --- a/examples/physics/chipmunk/main.go +++ b/examples/physics/chipmunk/main.go @@ -1,163 +1,139 @@ package main import ( - "fmt" "math" "math/rand" "github.com/gen2brain/raylib-go/raylib" - "github.com/jakecoffman/cp" + + "github.com/vova616/chipmunk" + "github.com/vova616/chipmunk/vect" ) -var grabbableMaskBit uint = 1 << 31 -var grabFilter = cp.ShapeFilter{ - cp.NO_GROUP, grabbableMaskBit, grabbableMaskBit, +const ( + ballRadius = 25 + ballMass = 1 +) + +// Game type +type Game struct { + Space *chipmunk.Space + Balls []*chipmunk.Shape + StaticLines []*chipmunk.Shape + + ticksToNextBall int } -func randUnitCircle() cp.Vector { - v := cp.Vector{X: rand.Float64()*2.0 - 1.0, Y: rand.Float64()*2.0 - 1.0} - if v.LengthSq() < 1.0 { - return v - } - return randUnitCircle() +// NewGame - Start new game +func NewGame() (g Game) { + g.Init() + return } -var simpleTerrainVerts = []cp.Vector{ - {350.00, 425.07}, {336.00, 436.55}, {272.00, 435.39}, {258.00, 427.63}, {225.28, 420.00}, {202.82, 396.00}, - {191.81, 388.00}, {189.00, 381.89}, {173.00, 380.39}, {162.59, 368.00}, {150.47, 319.00}, {128.00, 311.55}, - {119.14, 286.00}, {126.84, 263.00}, {120.56, 227.00}, {141.14, 178.00}, {137.52, 162.00}, {146.51, 142.00}, - {156.23, 136.00}, {158.00, 118.27}, {170.00, 100.77}, {208.43, 84.00}, {224.00, 69.65}, {249.30, 68.00}, - {257.00, 54.77}, {363.00, 45.94}, {374.15, 54.00}, {386.00, 69.60}, {413.00, 70.73}, {456.00, 84.89}, - {468.09, 99.00}, {467.09, 123.00}, {464.92, 135.00}, {469.00, 141.03}, {497.00, 148.67}, {513.85, 180.00}, - {509.56, 223.00}, {523.51, 247.00}, {523.00, 277.00}, {497.79, 311.00}, {478.67, 348.00}, {467.90, 360.00}, - {456.76, 382.00}, {432.95, 389.00}, {417.00, 411.32}, {373.00, 433.19}, {361.00, 430.02}, {350.00, 425.07}, +// Init - Initialize game +func (g *Game) Init() { + g.createBodies() + + g.ticksToNextBall = 10 } -// creates a circle with random placement -func addCircle(space *cp.Space, radius float64) { - mass := radius * radius / 25.0 - body := space.AddBody(cp.NewBody(mass, cp.MomentForCircle(mass, 0, radius, cp.Vector{}))) - body.SetPosition(randUnitCircle().Mult(180)) - - shape := space.AddShape(cp.NewCircle(body, radius, cp.Vector{})) - shape.SetElasticity(0) - shape.SetFriction(0.9) -} - -// creates a simple terrain to contain bodies -func simpleTerrain() *cp.Space { - space := cp.NewSpace() - space.Iterations = 10 - space.SetGravity(cp.Vector{0, -100}) - space.SetCollisionSlop(0.5) - - offset := cp.Vector{X: -320, Y: -240} - for i := 0; i < len(simpleTerrainVerts)-1; i++ { - a := simpleTerrainVerts[i] - b := simpleTerrainVerts[i+1] - space.AddShape(cp.NewSegment(space.StaticBody, a.Add(offset), b.Add(offset), 0)) +// Update - Update game +func (g *Game) Update() { + g.ticksToNextBall-- + if g.ticksToNextBall == 0 { + g.ticksToNextBall = rand.Intn(100) + 1 + g.addBall() } - return space + // Physics steps calculations + g.step(rl.GetFrameTime()) +} + +// Draw - Draw game +func (g *Game) Draw() { + for i := range g.StaticLines { + x := g.StaticLines[i].GetAsSegment().A.X + y := g.StaticLines[i].GetAsSegment().A.Y + + x2 := g.StaticLines[i].GetAsSegment().B.X + y2 := g.StaticLines[i].GetAsSegment().B.Y + + rl.DrawLine(int32(x), int32(y), int32(x2), int32(y2), rl.DarkBlue) + } + + for _, b := range g.Balls { + pos := b.Body.Position() + rl.DrawCircleLines(int32(pos.X), int32(pos.Y), float32(ballRadius), rl.DarkBlue) + } +} + +// createBodies sets up the chipmunk space and static bodies +func (g *Game) createBodies() { + g.Space = chipmunk.NewSpace() + g.Space.Gravity = vect.Vect{0, 900} + + staticBody := chipmunk.NewBodyStatic() + g.StaticLines = []*chipmunk.Shape{ + chipmunk.NewSegment(vect.Vect{250.0, 240.0}, vect.Vect{550.0, 280.0}, 0), + chipmunk.NewSegment(vect.Vect{550.0, 280.0}, vect.Vect{550.0, 180.0}, 0), + } + + for _, segment := range g.StaticLines { + segment.SetElasticity(0.6) + staticBody.AddShape(segment) + } + + g.Space.AddBody(staticBody) +} + +// addBall adds ball to chipmunk space and body +func (g *Game) addBall() { + x := rand.Intn(600-200) + 200 + ball := chipmunk.NewCircle(vect.Vector_Zero, float32(ballRadius)) + ball.SetElasticity(0.95) + + body := chipmunk.NewBody(vect.Float(ballMass), ball.Moment(float32(ballMass))) + body.SetPosition(vect.Vect{vect.Float(x), 0.0}) + body.SetAngle(vect.Float(rand.Float32() * 2 * math.Pi)) + body.AddShape(ball) + + g.Space.AddBody(body) + g.Balls = append(g.Balls, ball) +} + +// step advances the physics engine and cleans up any balls that are off-screen +func (g *Game) step(dt float32) { + g.Space.Step(vect.Float(dt)) + + for i := 0; i < len(g.Balls); i++ { + p := g.Balls[i].Body.Position() + if p.Y < -100 { + g.Space.RemoveBody(g.Balls[i].Body) + g.Balls[i] = nil + g.Balls = append(g.Balls[:i], g.Balls[i+1:]...) + i-- // consider same index again + } + } } func main() { - const width, height = 800, 450 - const physicsTickrate = 1.0 / 60.0 + rl.InitWindow(800, 450, "raylib [physics] example - chipmunk") - raylib.SetConfigFlags(raylib.FlagVsyncHint) - raylib.InitWindow(width, height, "raylib [physics] example - chipmunk") + rl.SetTargetFPS(60) - offset := raylib.Vector2{X: width / 2, Y: height / 2} - // since the example ported from elsewhere, flip the camera 180 and offset to center it - camera := raylib.NewCamera2D(offset, raylib.Vector2{}, 180, 1) + game := NewGame() - space := simpleTerrain() - for i := 0; i < 1000; i++ { - addCircle(space, 5) - } - mouseBody := cp.NewKinematicBody() - var mouse cp.Vector - var mouseJoint *cp.Constraint + for !rl.WindowShouldClose() { + rl.BeginDrawing() - var accumulator, dt float32 - lastTime := raylib.GetTime() - for !raylib.WindowShouldClose() { - // calculate dt - now := raylib.GetTime() - dt = now - lastTime - lastTime = now + rl.ClearBackground(rl.RayWhite) - // update the mouse position - mousePos := raylib.GetMousePosition() - // alter the mouse coordinates based on the camera position, rotation - mouse.X = float64(mousePos.X-camera.Offset.X) * -1 - mouse.Y = float64(mousePos.Y-camera.Offset.Y) * -1 - // smooth mouse movements to new position - newPoint := mouseBody.Position().Lerp(mouse, 0.25) - mouseBody.SetVelocityVector(newPoint.Sub(mouseBody.Position()).Mult(60.0)) - mouseBody.SetPosition(newPoint) + game.Update() - // handle grabbing - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { - result := space.PointQueryNearest(mouse, 5, grabFilter) - if result.Shape != nil && result.Shape.Body().Mass() < cp.INFINITY { - var nearest cp.Vector - if result.Distance > 0 { - nearest = result.Point - } else { - nearest = mouse - } + game.Draw() - // create a new constraint where the mouse is to draw the body towards the mouse - body := result.Shape.Body() - mouseJoint = cp.NewPivotJoint2(mouseBody, body, cp.Vector{}, body.WorldToLocal(nearest)) - mouseJoint.SetMaxForce(50000) - mouseJoint.SetErrorBias(math.Pow(1.0-0.15, 60.0)) - space.AddConstraint(mouseJoint) - } - } else if raylib.IsMouseButtonReleased(raylib.MouseLeftButton) && mouseJoint != nil { - space.RemoveConstraint(mouseJoint) - mouseJoint = nil - } - - // perform a fixed rate physics tick - accumulator += dt - for accumulator >= physicsTickrate { - space.Step(physicsTickrate) - accumulator -= physicsTickrate - } - - raylib.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) - raylib.BeginMode2D(camera) - - // this is a generic way to iterate over the shapes in a space, - // to avoid the type switch just keep a pointer to the shapes when they've been created - space.EachShape(func(s *cp.Shape) { - switch s.Class.(type) { - case *cp.Segment: - segment := s.Class.(*cp.Segment) - a := segment.A() - b := segment.B() - raylib.DrawLineV(v(a), v(b), raylib.Black) - case *cp.Circle: - circle := s.Class.(*cp.Circle) - pos := circle.Body().Position() - raylib.DrawCircleV(v(pos), float32(circle.Radius()), raylib.Red) - default: - fmt.Println("unexpected shape", s.Class) - } - }) - - raylib.EndMode2D() - raylib.DrawFPS(0, 0) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() -} - -func v(v cp.Vector) raylib.Vector2 { - return raylib.Vector2{X: float32(v.X), Y: float32(v.Y)} + rl.CloseWindow() } diff --git a/examples/physics/physac/demo/main.go b/examples/physics/physac/demo/main.go index 6ca9bb1..8fb3777 100644 --- a/examples/physics/physac/demo/main.go +++ b/examples/physics/physac/demo/main.go @@ -9,46 +9,46 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.SetConfigFlags(raylib.FlagMsaa4xHint) - raylib.InitWindow(screenWidth, screenHeight, "Physac [raylib] - physics demo") + rl.SetConfigFlags(rl.FlagMsaa4xHint) + rl.InitWindow(screenWidth, screenHeight, "Physac [raylib] - physics demo") // Physac logo drawing position - logoX := screenWidth - raylib.MeasureText("Physac", 30) - 10 + logoX := screenWidth - rl.MeasureText("Physac", 30) - 10 logoY := int32(15) // Initialize physics and default physics bodies physics.Init() // Create floor rectangle physics body - floor := physics.NewBodyRectangle(raylib.NewVector2(float32(screenWidth)/2, float32(screenHeight)), 500, 100, 10) + floor := physics.NewBodyRectangle(rl.NewVector2(float32(screenWidth)/2, float32(screenHeight)), 500, 100, 10) floor.Enabled = false // Disable body state to convert it to static (no dynamics, but collisions) // Create obstacle circle physics body - circle := physics.NewBodyCircle(raylib.NewVector2(float32(screenWidth)/2, float32(screenHeight)/2), 45, 10) + circle := physics.NewBodyCircle(rl.NewVector2(float32(screenWidth)/2, float32(screenHeight)/2), 45, 10) circle.Enabled = false // Disable body state to convert it to static (no dynamics, but collisions) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update created physics objects physics.Update() - if raylib.IsKeyPressed(raylib.KeyR) { // Reset physics input + if rl.IsKeyPressed(rl.KeyR) { // Reset physics input physics.Reset() - floor = physics.NewBodyRectangle(raylib.NewVector2(float32(screenWidth)/2, float32(screenHeight)), 500, 100, 10) + floor = physics.NewBodyRectangle(rl.NewVector2(float32(screenWidth)/2, float32(screenHeight)), 500, 100, 10) floor.Enabled = false - circle = physics.NewBodyCircle(raylib.NewVector2(float32(screenWidth)/2, float32(screenHeight)/2), 45, 10) + circle = physics.NewBodyCircle(rl.NewVector2(float32(screenWidth)/2, float32(screenHeight)/2), 45, 10) circle.Enabled = false } // Physics body creation inputs - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { - physics.NewBodyPolygon(raylib.GetMousePosition(), float32(raylib.GetRandomValue(20, 80)), int(raylib.GetRandomValue(3, 8)), 10) - } else if raylib.IsMouseButtonPressed(raylib.MouseRightButton) { - physics.NewBodyCircle(raylib.GetMousePosition(), float32(raylib.GetRandomValue(10, 45)), 10) + if rl.IsMouseButtonPressed(rl.MouseLeftButton) { + physics.NewBodyPolygon(rl.GetMousePosition(), float32(rl.GetRandomValue(20, 80)), int(rl.GetRandomValue(3, 8)), 10) + } else if rl.IsMouseButtonPressed(rl.MouseRightButton) { + physics.NewBodyCircle(rl.GetMousePosition(), float32(rl.GetRandomValue(10, 45)), 10) } // Destroy falling physics bodies @@ -58,11 +58,11 @@ func main() { } } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.Black) + rl.ClearBackground(rl.Black) - raylib.DrawFPS(screenWidth-90, screenHeight-30) + rl.DrawFPS(screenWidth-90, screenHeight-30) // Draw created physics bodies for i, body := range physics.GetBodies() { @@ -79,21 +79,21 @@ func main() { vertexB := body.GetShapeVertex(jj) - raylib.DrawLineV(vertexA, vertexB, raylib.Green) // Draw a line between two vertex positions + rl.DrawLineV(vertexA, vertexB, rl.Green) // Draw a line between two vertex positions } } - raylib.DrawText("Left mouse button to create a polygon", 10, 10, 10, raylib.White) - raylib.DrawText("Right mouse button to create a circle", 10, 25, 10, raylib.White) - raylib.DrawText("Press 'R' to reset example", 10, 40, 10, raylib.White) + rl.DrawText("Left mouse button to create a polygon", 10, 10, 10, rl.White) + rl.DrawText("Right mouse button to create a circle", 10, 25, 10, rl.White) + rl.DrawText("Press 'R' to reset example", 10, 40, 10, rl.White) - raylib.DrawText("Physac", logoX, logoY, 30, raylib.White) - raylib.DrawText("Powered by", logoX+50, logoY-7, 10, raylib.White) + rl.DrawText("Physac", logoX, logoY, 30, rl.White) + rl.DrawText("Powered by", logoX+50, logoY-7, 10, rl.White) - raylib.EndDrawing() + rl.EndDrawing() } physics.Close() // Unitialize physics - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/physics/physac/friction/main.go b/examples/physics/physac/friction/main.go index 5ebe201..6eed272 100644 --- a/examples/physics/physac/friction/main.go +++ b/examples/physics/physac/friction/main.go @@ -9,67 +9,67 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.SetConfigFlags(raylib.FlagMsaa4xHint) - raylib.InitWindow(screenWidth, screenHeight, "Physac [raylib] - physics friction") + rl.SetConfigFlags(rl.FlagMsaa4xHint) + rl.InitWindow(screenWidth, screenHeight, "Physac [raylib] - physics friction") // Physac logo drawing position - logoX := screenWidth - raylib.MeasureText("Physac", 30) - 10 + logoX := screenWidth - rl.MeasureText("Physac", 30) - 10 logoY := int32(15) // Initialize physics and default physics bodies physics.Init() // Create floor rectangle physics body - floor := physics.NewBodyRectangle(raylib.NewVector2(float32(screenWidth)/2, float32(screenHeight)), float32(screenHeight), 100, 10) + floor := physics.NewBodyRectangle(rl.NewVector2(float32(screenWidth)/2, float32(screenHeight)), float32(screenHeight), 100, 10) floor.Enabled = false // Disable body state to convert it to static (no dynamics, but collisions) - wall := physics.NewBodyRectangle(raylib.NewVector2(float32(screenWidth)/2, float32(screenHeight)*0.8), 10, 80, 10) + wall := physics.NewBodyRectangle(rl.NewVector2(float32(screenWidth)/2, float32(screenHeight)*0.8), 10, 80, 10) wall.Enabled = false // Disable body state to convert it to static (no dynamics, but collisions) // Create left ramp physics body - rectLeft := physics.NewBodyRectangle(raylib.NewVector2(25, float32(screenHeight)-5), 250, 250, 10) + rectLeft := physics.NewBodyRectangle(rl.NewVector2(25, float32(screenHeight)-5), 250, 250, 10) rectLeft.Enabled = false // Disable body state to convert it to static (no dynamics, but collisions) - rectLeft.SetRotation(30 * raylib.Deg2rad) + rectLeft.SetRotation(30 * rl.Deg2rad) // Create right ramp physics body - rectRight := physics.NewBodyRectangle(raylib.NewVector2(float32(screenWidth)-25, float32(screenHeight)-5), 250, 250, 10) + rectRight := physics.NewBodyRectangle(rl.NewVector2(float32(screenWidth)-25, float32(screenHeight)-5), 250, 250, 10) rectRight.Enabled = false // Disable body state to convert it to static (no dynamics, but collisions) - rectRight.SetRotation(330 * raylib.Deg2rad) + rectRight.SetRotation(330 * rl.Deg2rad) // Create dynamic physics bodies - bodyA := physics.NewBodyRectangle(raylib.NewVector2(35, float32(screenHeight)*0.6), 40, 40, 10) + bodyA := physics.NewBodyRectangle(rl.NewVector2(35, float32(screenHeight)*0.6), 40, 40, 10) bodyA.StaticFriction = 0.1 bodyA.DynamicFriction = 0.1 - bodyA.SetRotation(30 * raylib.Deg2rad) + bodyA.SetRotation(30 * rl.Deg2rad) - bodyB := physics.NewBodyRectangle(raylib.NewVector2(float32(screenWidth)-35, float32(screenHeight)*0.6), 40, 40, 10) + bodyB := physics.NewBodyRectangle(rl.NewVector2(float32(screenWidth)-35, float32(screenHeight)*0.6), 40, 40, 10) bodyB.StaticFriction = 1 bodyB.DynamicFriction = 1 - bodyB.SetRotation(330 * raylib.Deg2rad) + bodyB.SetRotation(330 * rl.Deg2rad) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Physics steps calculations physics.Update() - if raylib.IsKeyPressed(raylib.KeyR) { // Reset physics input + if rl.IsKeyPressed(rl.KeyR) { // Reset physics input // Reset dynamic physics bodies position, velocity and rotation - bodyA.Position = raylib.NewVector2(35, float32(screenHeight)*0.6) - bodyA.Velocity = raylib.NewVector2(0, 0) + bodyA.Position = rl.NewVector2(35, float32(screenHeight)*0.6) + bodyA.Velocity = rl.NewVector2(0, 0) bodyA.AngularVelocity = 0 - bodyA.SetRotation(30 * raylib.Deg2rad) + bodyA.SetRotation(30 * rl.Deg2rad) - bodyB.Position = raylib.NewVector2(float32(screenWidth)-35, float32(screenHeight)*0.6) - bodyB.Velocity = raylib.NewVector2(0, 0) + bodyB.Position = rl.NewVector2(float32(screenWidth)-35, float32(screenHeight)*0.6) + bodyB.Velocity = rl.NewVector2(0, 0) bodyB.AngularVelocity = 0 - bodyB.SetRotation(330 * raylib.Deg2rad) + bodyB.SetRotation(330 * rl.Deg2rad) } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.Black) + rl.ClearBackground(rl.Black) - raylib.DrawFPS(screenWidth-90, screenHeight-30) + rl.DrawFPS(screenWidth-90, screenHeight-30) // Draw created physics bodies bodiesCount := physics.GetBodiesCount() @@ -89,25 +89,25 @@ func main() { vertexB := body.GetShapeVertex(jj) - raylib.DrawLineV(vertexA, vertexB, raylib.Green) // Draw a line between two vertex positions + rl.DrawLineV(vertexA, vertexB, rl.Green) // Draw a line between two vertex positions } } - raylib.DrawRectangle(0, screenHeight-49, screenWidth, 49, raylib.Black) + rl.DrawRectangle(0, screenHeight-49, screenWidth, 49, rl.Black) - raylib.DrawText("Friction amount", (screenWidth-raylib.MeasureText("Friction amount", 30))/2, 75, 30, raylib.White) - raylib.DrawText("0.1", int32(bodyA.Position.X)-raylib.MeasureText("0.1", 20)/2, int32(bodyA.Position.Y)-7, 20, raylib.White) - raylib.DrawText("1", int32(bodyB.Position.X)-raylib.MeasureText("1", 20)/2, int32(bodyB.Position.Y)-7, 20, raylib.White) + rl.DrawText("Friction amount", (screenWidth-rl.MeasureText("Friction amount", 30))/2, 75, 30, rl.White) + rl.DrawText("0.1", int32(bodyA.Position.X)-rl.MeasureText("0.1", 20)/2, int32(bodyA.Position.Y)-7, 20, rl.White) + rl.DrawText("1", int32(bodyB.Position.X)-rl.MeasureText("1", 20)/2, int32(bodyB.Position.Y)-7, 20, rl.White) - raylib.DrawText("Press 'R' to reset example", 10, 10, 10, raylib.White) + rl.DrawText("Press 'R' to reset example", 10, 10, 10, rl.White) - raylib.DrawText("Physac", logoX, logoY, 30, raylib.White) - raylib.DrawText("Powered by", logoX+50, logoY-7, 10, raylib.White) + rl.DrawText("Physac", logoX, logoY, 30, rl.White) + rl.DrawText("Powered by", logoX+50, logoY-7, 10, rl.White) - raylib.EndDrawing() + rl.EndDrawing() } physics.Close() // Unitialize physics - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/physics/physac/movement/main.go b/examples/physics/physac/movement/main.go index 096ec4c..164b62f 100644 --- a/examples/physics/physac/movement/main.go +++ b/examples/physics/physac/movement/main.go @@ -13,22 +13,22 @@ func main() { screenWidth := float32(800) screenHeight := float32(450) - raylib.SetConfigFlags(raylib.FlagMsaa4xHint) - raylib.InitWindow(int32(screenWidth), int32(screenHeight), "Physac [raylib] - physics movement") + rl.SetConfigFlags(rl.FlagMsaa4xHint) + rl.InitWindow(int32(screenWidth), int32(screenHeight), "Physac [raylib] - physics movement") // Physac logo drawing position - logoX := int32(screenWidth) - raylib.MeasureText("Physac", 30) - 10 + logoX := int32(screenWidth) - rl.MeasureText("Physac", 30) - 10 logoY := int32(15) // Initialize physics and default physics bodies physics.Init() // Create floor and walls rectangle physics body - floor := physics.NewBodyRectangle(raylib.NewVector2(screenWidth/2, screenHeight), screenWidth, 100, 10) - platformLeft := physics.NewBodyRectangle(raylib.NewVector2(screenWidth*0.25, screenHeight*0.6), screenWidth*0.25, 10, 10) - platformRight := physics.NewBodyRectangle(raylib.NewVector2(screenWidth*0.75, screenHeight*0.6), screenWidth*0.25, 10, 10) - wallLeft := physics.NewBodyRectangle(raylib.NewVector2(-5, screenHeight/2), 10, screenHeight, 10) - wallRight := physics.NewBodyRectangle(raylib.NewVector2(screenWidth+5, screenHeight/2), 10, screenHeight, 10) + floor := physics.NewBodyRectangle(rl.NewVector2(screenWidth/2, screenHeight), screenWidth, 100, 10) + platformLeft := physics.NewBodyRectangle(rl.NewVector2(screenWidth*0.25, screenHeight*0.6), screenWidth*0.25, 10, 10) + platformRight := physics.NewBodyRectangle(rl.NewVector2(screenWidth*0.75, screenHeight*0.6), screenWidth*0.25, 10, 10) + wallLeft := physics.NewBodyRectangle(rl.NewVector2(-5, screenHeight/2), 10, screenHeight, 10) + wallRight := physics.NewBodyRectangle(rl.NewVector2(screenWidth+5, screenHeight/2), 10, screenHeight, 10) // Disable dynamics to floor and walls physics bodies floor.Enabled = false @@ -38,38 +38,38 @@ func main() { wallRight.Enabled = false // Create movement physics body - body := physics.NewBodyRectangle(raylib.NewVector2(screenWidth/2, screenHeight/2), 50, 50, 1) + body := physics.NewBodyRectangle(rl.NewVector2(screenWidth/2, screenHeight/2), 50, 50, 1) body.FreezeOrient = true // Constrain body rotation to avoid little collision torque amounts - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update created physics objects physics.Update() - if raylib.IsKeyPressed(raylib.KeyR) { // Reset physics input + if rl.IsKeyPressed(rl.KeyR) { // Reset physics input // Reset movement physics body position, velocity and rotation - body.Position = raylib.NewVector2(screenWidth/2, screenHeight/2) - body.Velocity = raylib.NewVector2(0, 0) + body.Position = rl.NewVector2(screenWidth/2, screenHeight/2) + body.Velocity = rl.NewVector2(0, 0) body.SetRotation(0) } // Physics body creation inputs - if raylib.IsKeyDown(raylib.KeyRight) { + if rl.IsKeyDown(rl.KeyRight) { body.Velocity.X = velocity - } else if raylib.IsKeyDown(raylib.KeyLeft) { + } else if rl.IsKeyDown(rl.KeyLeft) { body.Velocity.X = -velocity } - if raylib.IsKeyDown(raylib.KeyUp) && body.IsGrounded { + if rl.IsKeyDown(rl.KeyUp) && body.IsGrounded { body.Velocity.Y = -velocity * 4 } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.Black) + rl.ClearBackground(rl.Black) - raylib.DrawFPS(int32(screenWidth)-90, int32(screenHeight)-30) + rl.DrawFPS(int32(screenWidth)-90, int32(screenHeight)-30) // Draw created physics bodies for i, body := range physics.GetBodies() { @@ -86,20 +86,20 @@ func main() { vertexB := body.GetShapeVertex(jj) - raylib.DrawLineV(vertexA, vertexB, raylib.Green) // Draw a line between two vertex positions + rl.DrawLineV(vertexA, vertexB, rl.Green) // Draw a line between two vertex positions } } - raylib.DrawText("Use 'ARROWS' to move player", 10, 10, 10, raylib.White) - raylib.DrawText("Press 'R' to reset example", 10, 30, 10, raylib.White) + rl.DrawText("Use 'ARROWS' to move player", 10, 10, 10, rl.White) + rl.DrawText("Press 'R' to reset example", 10, 30, 10, rl.White) - raylib.DrawText("Physac", logoX, logoY, 30, raylib.White) - raylib.DrawText("Powered by", logoX+50, logoY-7, 10, raylib.White) + rl.DrawText("Physac", logoX, logoY, 30, rl.White) + rl.DrawText("Powered by", logoX+50, logoY-7, 10, rl.White) - raylib.EndDrawing() + rl.EndDrawing() } physics.Close() // Unitialize physics - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/physics/physac/restitution/main.go b/examples/physics/physac/restitution/main.go index 90710da..0c069b1 100644 --- a/examples/physics/physac/restitution/main.go +++ b/examples/physics/physac/restitution/main.go @@ -13,50 +13,50 @@ func main() { screenWidth := float32(800) screenHeight := float32(450) - raylib.SetConfigFlags(raylib.FlagMsaa4xHint) - raylib.InitWindow(int32(screenWidth), int32(screenHeight), "Physac [raylib] - physics restitution") + rl.SetConfigFlags(rl.FlagMsaa4xHint) + rl.InitWindow(int32(screenWidth), int32(screenHeight), "Physac [raylib] - physics restitution") // Physac logo drawing position - logoX := int32(screenWidth) - raylib.MeasureText("Physac", 30) - 10 + logoX := int32(screenWidth) - rl.MeasureText("Physac", 30) - 10 logoY := int32(15) // Initialize physics and default physics bodies physics.Init() // Create floor rectangle physics body - floor := physics.NewBodyRectangle(raylib.NewVector2(screenWidth/2, screenHeight), screenWidth, 100, 10) + floor := physics.NewBodyRectangle(rl.NewVector2(screenWidth/2, screenHeight), screenWidth, 100, 10) floor.Enabled = false // Disable body state to convert it to static (no dynamics, but collisions) floor.Restitution = 1 // Create circles physics body - circleA := physics.NewBodyCircle(raylib.NewVector2(screenWidth*0.25, screenHeight/2), 30, 10) + circleA := physics.NewBodyCircle(rl.NewVector2(screenWidth*0.25, screenHeight/2), 30, 10) circleA.Restitution = 0 - circleB := physics.NewBodyCircle(raylib.NewVector2(screenWidth*0.5, screenHeight/2), 30, 10) + circleB := physics.NewBodyCircle(rl.NewVector2(screenWidth*0.5, screenHeight/2), 30, 10) circleB.Restitution = 0.5 - circleC := physics.NewBodyCircle(raylib.NewVector2(screenWidth*0.75, screenHeight/2), 30, 10) + circleC := physics.NewBodyCircle(rl.NewVector2(screenWidth*0.75, screenHeight/2), 30, 10) circleC.Restitution = 1 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update created physics objects physics.Update() - if raylib.IsKeyPressed(raylib.KeyR) { // Reset physics input + if rl.IsKeyPressed(rl.KeyR) { // Reset physics input // Reset circles physics bodies position and velocity - circleA.Position = raylib.NewVector2(screenWidth*0.25, screenHeight/2) - circleA.Velocity = raylib.NewVector2(0, 0) - circleB.Position = raylib.NewVector2(screenWidth*0.5, screenHeight/2) - circleB.Velocity = raylib.NewVector2(0, 0) - circleC.Position = raylib.NewVector2(screenWidth*0.75, screenHeight/2) - circleC.Velocity = raylib.NewVector2(0, 0) + circleA.Position = rl.NewVector2(screenWidth*0.25, screenHeight/2) + circleA.Velocity = rl.NewVector2(0, 0) + circleB.Position = rl.NewVector2(screenWidth*0.5, screenHeight/2) + circleB.Velocity = rl.NewVector2(0, 0) + circleC.Position = rl.NewVector2(screenWidth*0.75, screenHeight/2) + circleC.Velocity = rl.NewVector2(0, 0) } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.Black) + rl.ClearBackground(rl.Black) - raylib.DrawFPS(int32(screenWidth)-90, int32(screenHeight)-30) + rl.DrawFPS(int32(screenWidth)-90, int32(screenHeight)-30) // Draw created physics bodies for i, body := range physics.GetBodies() { @@ -73,24 +73,24 @@ func main() { vertexB := body.GetShapeVertex(jj) - raylib.DrawLineV(vertexA, vertexB, raylib.Green) // Draw a line between two vertex positions + rl.DrawLineV(vertexA, vertexB, rl.Green) // Draw a line between two vertex positions } } - raylib.DrawText("Restitution amount", (int32(screenWidth)-raylib.MeasureText("Restitution amount", 30))/2, 75, 30, raylib.White) - raylib.DrawText("0", int32(circleA.Position.X)-raylib.MeasureText("0", 20)/2, int32(circleA.Position.Y)-7, 20, raylib.White) - raylib.DrawText("0.5", int32(circleB.Position.X)-raylib.MeasureText("0.5", 20)/2, int32(circleB.Position.Y)-7, 20, raylib.White) - raylib.DrawText("1", int32(circleC.Position.X)-raylib.MeasureText("1", 20)/2, int32(circleC.Position.Y)-7, 20, raylib.White) + rl.DrawText("Restitution amount", (int32(screenWidth)-rl.MeasureText("Restitution amount", 30))/2, 75, 30, rl.White) + rl.DrawText("0", int32(circleA.Position.X)-rl.MeasureText("0", 20)/2, int32(circleA.Position.Y)-7, 20, rl.White) + rl.DrawText("0.5", int32(circleB.Position.X)-rl.MeasureText("0.5", 20)/2, int32(circleB.Position.Y)-7, 20, rl.White) + rl.DrawText("1", int32(circleC.Position.X)-rl.MeasureText("1", 20)/2, int32(circleC.Position.Y)-7, 20, rl.White) - raylib.DrawText("Press 'R' to reset example", 10, 10, 10, raylib.White) + rl.DrawText("Press 'R' to reset example", 10, 10, 10, rl.White) - raylib.DrawText("Physac", logoX, logoY, 30, raylib.White) - raylib.DrawText("Powered by", logoX+50, logoY-7, 10, raylib.White) + rl.DrawText("Physac", logoX, logoY, 30, rl.White) + rl.DrawText("Powered by", logoX+50, logoY-7, 10, rl.White) - raylib.EndDrawing() + rl.EndDrawing() } physics.Close() // Unitialize physics - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/physics/physac/shatter/main.go b/examples/physics/physac/shatter/main.go index eddf2cb..56ad42f 100644 --- a/examples/physics/physac/shatter/main.go +++ b/examples/physics/physac/shatter/main.go @@ -13,11 +13,11 @@ func main() { screenWidth := float32(800) screenHeight := float32(450) - raylib.SetConfigFlags(raylib.FlagMsaa4xHint) - raylib.InitWindow(int32(screenWidth), int32(screenHeight), "Physac [raylib] - body shatter") + rl.SetConfigFlags(rl.FlagMsaa4xHint) + rl.InitWindow(int32(screenWidth), int32(screenHeight), "Physac [raylib] - body shatter") // Physac logo drawing position - logoX := int32(screenWidth) - raylib.MeasureText("Physac", 30) - 10 + logoX := int32(screenWidth) - rl.MeasureText("Physac", 30) - 10 logoY := int32(15) // Initialize physics and default physics bodies @@ -25,30 +25,30 @@ func main() { physics.SetGravity(0, 0) // Create random polygon physics body to shatter - physics.NewBodyPolygon(raylib.NewVector2(screenWidth/2, screenHeight/2), float32(raylib.GetRandomValue(80, 200)), int(raylib.GetRandomValue(3, 8)), 10) + physics.NewBodyPolygon(rl.NewVector2(screenWidth/2, screenHeight/2), float32(rl.GetRandomValue(80, 200)), int(rl.GetRandomValue(3, 8)), 10) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update created physics objects physics.Update() - if raylib.IsKeyPressed(raylib.KeyR) { // Reset physics input + if rl.IsKeyPressed(rl.KeyR) { // Reset physics input physics.Reset() // Create random polygon physics body to shatter - physics.NewBodyPolygon(raylib.NewVector2(screenWidth/2, screenHeight/2), float32(raylib.GetRandomValue(80, 200)), int(raylib.GetRandomValue(3, 8)), 10) + physics.NewBodyPolygon(rl.NewVector2(screenWidth/2, screenHeight/2), float32(rl.GetRandomValue(80, 200)), int(rl.GetRandomValue(3, 8)), 10) } - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + if rl.IsMouseButtonPressed(rl.MouseLeftButton) { for _, b := range physics.GetBodies() { - b.Shatter(raylib.GetMousePosition(), 10/b.InverseMass) + b.Shatter(rl.GetMousePosition(), 10/b.InverseMass) } } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.Black) + rl.ClearBackground(rl.Black) // Draw created physics bodies for i, body := range physics.GetBodies() { @@ -65,19 +65,19 @@ func main() { vertexB := body.GetShapeVertex(jj) - raylib.DrawLineV(vertexA, vertexB, raylib.Green) // Draw a line between two vertex positions + rl.DrawLineV(vertexA, vertexB, rl.Green) // Draw a line between two vertex positions } } - raylib.DrawText("Left mouse button in polygon area to shatter body\nPress 'R' to reset example", 10, 10, 10, raylib.White) + rl.DrawText("Left mouse button in polygon area to shatter body\nPress 'R' to reset example", 10, 10, 10, rl.White) - raylib.DrawText("Physac", logoX, logoY, 30, raylib.White) - raylib.DrawText("Powered by", logoX+50, logoY-7, 10, raylib.White) + rl.DrawText("Physac", logoX, logoY, 30, rl.White) + rl.DrawText("Powered by", logoX+50, logoY-7, 10, rl.White) - raylib.EndDrawing() + rl.EndDrawing() } physics.Close() // Unitialize physics - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/shaders/custom_uniform/main.go b/examples/shaders/custom_uniform/main.go index 8742189..3a130e8 100644 --- a/examples/shaders/custom_uniform/main.go +++ b/examples/shaders/custom_uniform/main.go @@ -8,90 +8,90 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.SetConfigFlags(raylib.FlagMsaa4xHint) // Enable Multi Sampling Anti Aliasing 4x (if available) + rl.SetConfigFlags(rl.FlagMsaa4xHint) // Enable Multi Sampling Anti Aliasing 4x (if available) - raylib.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - custom uniform variable") + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - custom uniform variable") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(3.0, 3.0, 3.0) - camera.Target = raylib.NewVector3(0.0, 1.5, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera{} + camera.Position = rl.NewVector3(3.0, 3.0, 3.0) + camera.Target = rl.NewVector3(0.0, 1.5, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - dwarf := raylib.LoadModel("dwarf.obj") // Load OBJ model - texture := raylib.LoadTexture("dwarf_diffuse.png") // Load model texture + dwarf := rl.LoadModel("dwarf.obj") // Load OBJ model + texture := rl.LoadTexture("dwarf_diffuse.png") // Load model texture - dwarf.Material.Maps[raylib.MapDiffuse].Texture = texture // Set dwarf model diffuse texture + dwarf.Material.Maps[rl.MapDiffuse].Texture = texture // Set dwarf model diffuse texture - position := raylib.NewVector3(0.0, 0.0, 0.0) // Set model position + position := rl.NewVector3(0.0, 0.0, 0.0) // Set model position - shader := raylib.LoadShader("glsl330/base.vs", "glsl330/swirl.fs") // Load postpro shader + shader := rl.LoadShader("glsl330/base.vs", "glsl330/swirl.fs") // Load postpro shader // Get variable (uniform) location on the shader to connect with the program // NOTE: If uniform variable could not be found in the shader, function returns -1 - swirlCenterLoc := raylib.GetShaderLocation(shader, "center") + swirlCenterLoc := rl.GetShaderLocation(shader, "center") swirlCenter := make([]float32, 2) swirlCenter[0] = float32(screenWidth) / 2 swirlCenter[1] = float32(screenHeight) / 2 // Create a RenderTexture2D to be used for render to texture - target := raylib.LoadRenderTexture(screenWidth, screenHeight) + target := rl.LoadRenderTexture(screenWidth, screenHeight) // Setup orbital camera - raylib.SetCameraMode(camera, raylib.CameraOrbital) // Set an orbital camera mode + rl.SetCameraMode(camera, rl.CameraOrbital) // Set an orbital camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update //---------------------------------------------------------------------------------- - mousePosition := raylib.GetMousePosition() + mousePosition := rl.GetMousePosition() swirlCenter[0] = mousePosition.X swirlCenter[1] = float32(screenHeight) - mousePosition.Y // Send new value to the shader to be used on drawing - raylib.SetShaderValue(shader, swirlCenterLoc, swirlCenter, 2) + rl.SetShaderValue(shader, swirlCenterLoc, swirlCenter, 2) - raylib.UpdateCamera(&camera) // Update camera + rl.UpdateCamera(&camera) // Update camera - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginTextureMode(target) // Enable drawing to texture + rl.BeginTextureMode(target) // Enable drawing to texture - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawModel(dwarf, position, 2.0, raylib.White) // Draw 3d model with texture + rl.DrawModel(dwarf, position, 2.0, rl.White) // Draw 3d model with texture - raylib.DrawGrid(10, 1.0) // Draw a grid + rl.DrawGrid(10, 1.0) // Draw a grid - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawText("TEXT DRAWN IN RENDER TEXTURE", 200, 10, 30, raylib.Red) + rl.DrawText("TEXT DRAWN IN RENDER TEXTURE", 200, 10, 30, rl.Red) - raylib.EndTextureMode() // End drawing to texture (now we have a texture available for next passes) + rl.EndTextureMode() // End drawing to texture (now we have a texture available for next passes) - raylib.BeginShaderMode(shader) + rl.BeginShaderMode(shader) // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) - raylib.DrawTextureRec(target.Texture, raylib.NewRectangle(0, 0, float32(target.Texture.Width), float32(-target.Texture.Height)), raylib.NewVector2(0, 0), raylib.White) + rl.DrawTextureRec(target.Texture, rl.NewRectangle(0, 0, float32(target.Texture.Width), float32(-target.Texture.Height)), rl.NewVector2(0, 0), rl.White) - raylib.EndShaderMode() + rl.EndShaderMode() - raylib.DrawText("(c) Dwarf 3D model by David Moreno", screenWidth-200, screenHeight-20, 10, raylib.Gray) + rl.DrawText("(c) Dwarf 3D model by David Moreno", screenWidth-200, screenHeight-20, 10, rl.Gray) - raylib.DrawFPS(10, 10) + rl.DrawFPS(10, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadShader(shader) // Unload shader - raylib.UnloadTexture(texture) // Unload texture - raylib.UnloadModel(dwarf) // Unload model - raylib.UnloadRenderTexture(target) // Unload render texture + rl.UnloadShader(shader) // Unload shader + rl.UnloadTexture(texture) // Unload texture + rl.UnloadModel(dwarf) // Unload model + rl.UnloadRenderTexture(target) // Unload render texture - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/shaders/model_shader/main.go b/examples/shaders/model_shader/main.go index 4c8e19e..6f813c8 100644 --- a/examples/shaders/model_shader/main.go +++ b/examples/shaders/model_shader/main.go @@ -10,57 +10,57 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.SetConfigFlags(raylib.FlagMsaa4xHint) // Enable Multi Sampling Anti Aliasing 4x (if available) + rl.SetConfigFlags(rl.FlagMsaa4xHint) // Enable Multi Sampling Anti Aliasing 4x (if available) - raylib.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - model shader") + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - model shader") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(3.0, 3.0, 3.0) - camera.Target = raylib.NewVector3(0.0, 1.5, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera{} + camera.Position = rl.NewVector3(3.0, 3.0, 3.0) + camera.Target = rl.NewVector3(0.0, 1.5, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - dwarf := raylib.LoadModel("dwarf.obj") // Load OBJ model - texture := raylib.LoadTexture("dwarf_diffuse.png") // Load model texture - shader := raylib.LoadShader("glsl330/base.vs", "glsl330/grayscale.fs") // Load model shader + dwarf := rl.LoadModel("dwarf.obj") // Load OBJ model + texture := rl.LoadTexture("dwarf_diffuse.png") // Load model texture + shader := rl.LoadShader("glsl330/base.vs", "glsl330/grayscale.fs") // Load model shader dwarf.Material.Shader = shader // Set shader effect to 3d model - dwarf.Material.Maps[raylib.MapDiffuse].Texture = texture // Set dwarf model diffuse texture + dwarf.Material.Maps[rl.MapDiffuse].Texture = texture // Set dwarf model diffuse texture - position := raylib.NewVector3(0.0, 0.0, 0.0) // Set model position + position := rl.NewVector3(0.0, 0.0, 0.0) // Set model position - raylib.SetCameraMode(camera, raylib.CameraFree) // Set free camera mode + rl.SetCameraMode(camera, rl.CameraFree) // Set free camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.UpdateCamera(&camera) // Update camera + for !rl.WindowShouldClose() { + rl.UpdateCamera(&camera) // Update camera - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawModel(dwarf, position, 2.0, raylib.White) // Draw 3d model with texture + rl.DrawModel(dwarf, position, 2.0, rl.White) // Draw 3d model with texture - raylib.DrawGrid(10, 1.0) // Draw a grid + rl.DrawGrid(10, 1.0) // Draw a grid - raylib.EndMode3D() + rl.EndMode3D() - raylib.DrawText("(c) Dwarf 3D model by David Moreno", screenWidth-200, screenHeight-20, 10, raylib.Gray) + rl.DrawText("(c) Dwarf 3D model by David Moreno", screenWidth-200, screenHeight-20, 10, rl.Gray) - raylib.DrawText(fmt.Sprintf("Camera position: (%.2f, %.2f, %.2f)", camera.Position.X, camera.Position.Y, camera.Position.Z), 600, 20, 10, raylib.Black) - raylib.DrawText(fmt.Sprintf("Camera target: (%.2f, %.2f, %.2f)", camera.Target.X, camera.Target.Y, camera.Target.Z), 600, 40, 10, raylib.Gray) + rl.DrawText(fmt.Sprintf("Camera position: (%.2f, %.2f, %.2f)", camera.Position.X, camera.Position.Y, camera.Position.Z), 600, 20, 10, rl.Black) + rl.DrawText(fmt.Sprintf("Camera target: (%.2f, %.2f, %.2f)", camera.Target.X, camera.Target.Y, camera.Target.Z), 600, 40, 10, rl.Gray) - raylib.DrawFPS(10, 10) + rl.DrawFPS(10, 10) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadShader(shader) // Unload shader - raylib.UnloadTexture(texture) // Unload texture - raylib.UnloadModel(dwarf) // Unload model + rl.UnloadShader(shader) // Unload shader + rl.UnloadTexture(texture) // Unload texture + rl.UnloadModel(dwarf) // Unload model - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/shaders/postprocessing/main.go b/examples/shaders/postprocessing/main.go index e1ce2ad..76630f2 100644 --- a/examples/shaders/postprocessing/main.go +++ b/examples/shaders/postprocessing/main.go @@ -40,53 +40,53 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.SetConfigFlags(raylib.FlagMsaa4xHint | raylib.FlagVsyncHint) // Enable Multi Sampling Anti Aliasing 4x (if available) + rl.SetConfigFlags(rl.FlagMsaa4xHint | rl.FlagVsyncHint) // Enable Multi Sampling Anti Aliasing 4x (if available) - raylib.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - postprocessing shader") + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - postprocessing shader") - camera := raylib.Camera{} - camera.Position = raylib.NewVector3(3.0, 3.0, 3.0) - camera.Target = raylib.NewVector3(0.0, 1.5, 0.0) - camera.Up = raylib.NewVector3(0.0, 1.0, 0.0) + camera := rl.Camera{} + camera.Position = rl.NewVector3(3.0, 3.0, 3.0) + camera.Target = rl.NewVector3(0.0, 1.5, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) camera.Fovy = 45.0 - dwarf := raylib.LoadModel("dwarf.obj") // Load OBJ model - texture := raylib.LoadTexture("dwarf_diffuse.png") // Load model texture - dwarf.Material.Maps[raylib.MapDiffuse].Texture = texture // Set dwarf model diffuse texture + dwarf := rl.LoadModel("dwarf.obj") // Load OBJ model + texture := rl.LoadTexture("dwarf_diffuse.png") // Load model texture + dwarf.Material.Maps[rl.MapDiffuse].Texture = texture // Set dwarf model diffuse texture - position := raylib.NewVector3(0.0, 0.0, 0.0) // Set model position + position := rl.NewVector3(0.0, 0.0, 0.0) // Set model position // Load all postpro shaders // NOTE 1: All postpro shader use the base vertex shader (DEFAULT_VERTEX_SHADER) - shaders := make([]raylib.Shader, MaxPostproShaders) - shaders[FxGrayscale] = raylib.LoadShader("", "glsl330/grayscale.fs") - shaders[FxPosterization] = raylib.LoadShader("", "glsl330/posterization.fs") - shaders[FxDreamVision] = raylib.LoadShader("", "glsl330/dream_vision.fs") - shaders[FxPixelizer] = raylib.LoadShader("", "glsl330/pixelizer.fs") - shaders[FxCrossHatching] = raylib.LoadShader("", "glsl330/cross_hatching.fs") - shaders[FxCrossStitching] = raylib.LoadShader("", "glsl330/cross_stitching.fs") - shaders[FxPredatorView] = raylib.LoadShader("", "glsl330/predator.fs") - shaders[FxScanlines] = raylib.LoadShader("", "glsl330/scanlines.fs") - shaders[FxFisheye] = raylib.LoadShader("", "glsl330/fisheye.fs") - shaders[FxSobel] = raylib.LoadShader("", "glsl330/sobel.fs") - shaders[FxBlur] = raylib.LoadShader("", "glsl330/blur.fs") - shaders[FxBloom] = raylib.LoadShader("", "glsl330/bloom.fs") + shaders := make([]rl.Shader, MaxPostproShaders) + shaders[FxGrayscale] = rl.LoadShader("", "glsl330/grayscale.fs") + shaders[FxPosterization] = rl.LoadShader("", "glsl330/posterization.fs") + shaders[FxDreamVision] = rl.LoadShader("", "glsl330/dream_vision.fs") + shaders[FxPixelizer] = rl.LoadShader("", "glsl330/pixelizer.fs") + shaders[FxCrossHatching] = rl.LoadShader("", "glsl330/cross_hatching.fs") + shaders[FxCrossStitching] = rl.LoadShader("", "glsl330/cross_stitching.fs") + shaders[FxPredatorView] = rl.LoadShader("", "glsl330/predator.fs") + shaders[FxScanlines] = rl.LoadShader("", "glsl330/scanlines.fs") + shaders[FxFisheye] = rl.LoadShader("", "glsl330/fisheye.fs") + shaders[FxSobel] = rl.LoadShader("", "glsl330/sobel.fs") + shaders[FxBlur] = rl.LoadShader("", "glsl330/blur.fs") + shaders[FxBloom] = rl.LoadShader("", "glsl330/bloom.fs") currentShader := FxGrayscale // Create a RenderTexture2D to be used for render to texture - target := raylib.LoadRenderTexture(screenWidth, screenHeight) + target := rl.LoadRenderTexture(screenWidth, screenHeight) - raylib.SetCameraMode(camera, raylib.CameraOrbital) // Set free camera mode + rl.SetCameraMode(camera, rl.CameraOrbital) // Set free camera mode - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.UpdateCamera(&camera) // Update camera + for !rl.WindowShouldClose() { + rl.UpdateCamera(&camera) // Update camera - if raylib.IsKeyPressed(raylib.KeyRight) { + if rl.IsKeyPressed(rl.KeyRight) { currentShader++ - } else if raylib.IsKeyPressed(raylib.KeyLeft) { + } else if rl.IsKeyPressed(rl.KeyLeft) { currentShader-- } @@ -96,51 +96,51 @@ func main() { currentShader = MaxPostproShaders - 1 } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.BeginTextureMode(target) // Enable drawing to texture + rl.BeginTextureMode(target) // Enable drawing to texture - raylib.BeginMode3D(camera) + rl.BeginMode3D(camera) - raylib.DrawModel(dwarf, position, 2.0, raylib.White) // Draw 3d model with texture + rl.DrawModel(dwarf, position, 2.0, rl.White) // Draw 3d model with texture - raylib.DrawGrid(10, 1.0) // Draw a grid + rl.DrawGrid(10, 1.0) // Draw a grid - raylib.EndMode3D() + rl.EndMode3D() - raylib.EndTextureMode() // End drawing to texture (now we have a texture available for next passes) + rl.EndTextureMode() // End drawing to texture (now we have a texture available for next passes) // Render previously generated texture using selected postpro shader - raylib.BeginShaderMode(shaders[currentShader]) + rl.BeginShaderMode(shaders[currentShader]) // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) - raylib.DrawTextureRec(target.Texture, raylib.NewRectangle(0, 0, float32(target.Texture.Width), float32(-target.Texture.Height)), raylib.NewVector2(0, 0), raylib.White) + rl.DrawTextureRec(target.Texture, rl.NewRectangle(0, 0, float32(target.Texture.Width), float32(-target.Texture.Height)), rl.NewVector2(0, 0), rl.White) - raylib.EndShaderMode() + rl.EndShaderMode() - raylib.DrawRectangle(0, 9, 580, 30, raylib.Fade(raylib.LightGray, 0.7)) + rl.DrawRectangle(0, 9, 580, 30, rl.Fade(rl.LightGray, 0.7)) - raylib.DrawText("(c) Dwarf 3D model by David Moreno", screenWidth-200, screenHeight-20, 10, raylib.DarkGray) + rl.DrawText("(c) Dwarf 3D model by David Moreno", screenWidth-200, screenHeight-20, 10, rl.DarkGray) - raylib.DrawText("CURRENT POSTPRO SHADER:", 10, 15, 20, raylib.Black) - raylib.DrawText(postproShaderText[currentShader], 330, 15, 20, raylib.Red) - raylib.DrawText("< >", 540, 10, 30, raylib.DarkBlue) + rl.DrawText("CURRENT POSTPRO SHADER:", 10, 15, 20, rl.Black) + rl.DrawText(postproShaderText[currentShader], 330, 15, 20, rl.Red) + rl.DrawText("< >", 540, 10, 30, rl.DarkBlue) - raylib.DrawFPS(700, 15) + rl.DrawFPS(700, 15) - raylib.EndDrawing() + rl.EndDrawing() } // Unload all postpro shaders for i := 0; i < MaxPostproShaders; i++ { - raylib.UnloadShader(shaders[i]) + rl.UnloadShader(shaders[i]) } - raylib.UnloadTexture(texture) // Unload texture - raylib.UnloadModel(dwarf) // Unload model - raylib.UnloadRenderTexture(target) // Unload render texture + rl.UnloadTexture(texture) // Unload texture + rl.UnloadModel(dwarf) // Unload model + rl.UnloadRenderTexture(target) // Unload render texture - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/shaders/shapes_textures/main.go b/examples/shaders/shapes_textures/main.go index 7cc319f..9bd6443 100644 --- a/examples/shaders/shapes_textures/main.go +++ b/examples/shaders/shapes_textures/main.go @@ -8,67 +8,67 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shapes and texture shaders") + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shapes and texture shaders") - fudesumi := raylib.LoadTexture("fudesumi.png") + fudesumi := rl.LoadTexture("fudesumi.png") // NOTE: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version - shader := raylib.LoadShader("glsl330/base.vs", "glsl330/grayscale.fs") + shader := rl.LoadShader("glsl330/base.vs", "glsl330/grayscale.fs") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) // Start drawing with default shader - raylib.DrawText("USING DEFAULT SHADER", 20, 40, 10, raylib.Red) + rl.DrawText("USING DEFAULT SHADER", 20, 40, 10, rl.Red) - raylib.DrawCircle(80, 120, 35, raylib.DarkBlue) - raylib.DrawCircleGradient(80, 220, 60, raylib.Green, raylib.SkyBlue) - raylib.DrawCircleLines(80, 340, 80, raylib.DarkBlue) + rl.DrawCircle(80, 120, 35, rl.DarkBlue) + rl.DrawCircleGradient(80, 220, 60, rl.Green, rl.SkyBlue) + rl.DrawCircleLines(80, 340, 80, rl.DarkBlue) // Activate our custom shader to be applied on next shapes/textures drawings - raylib.BeginShaderMode(shader) + rl.BeginShaderMode(shader) - raylib.DrawText("USING CUSTOM SHADER", 190, 40, 10, raylib.Red) + rl.DrawText("USING CUSTOM SHADER", 190, 40, 10, rl.Red) - raylib.DrawRectangle(250-60, 90, 120, 60, raylib.Red) - raylib.DrawRectangleGradientH(250-90, 170, 180, 130, raylib.Maroon, raylib.Gold) - raylib.DrawRectangleLines(250-40, 320, 80, 60, raylib.Orange) + rl.DrawRectangle(250-60, 90, 120, 60, rl.Red) + rl.DrawRectangleGradientH(250-90, 170, 180, 130, rl.Maroon, rl.Gold) + rl.DrawRectangleLines(250-40, 320, 80, 60, rl.Orange) // Activate our default shader for next drawings - raylib.EndShaderMode() + rl.EndShaderMode() - raylib.DrawText("USING DEFAULT SHADER", 370, 40, 10, raylib.Red) + rl.DrawText("USING DEFAULT SHADER", 370, 40, 10, rl.Red) - raylib.DrawTriangle(raylib.NewVector2(430, 80), - raylib.NewVector2(430-60, 150), - raylib.NewVector2(430+60, 150), raylib.Violet) + rl.DrawTriangle(rl.NewVector2(430, 80), + rl.NewVector2(430-60, 150), + rl.NewVector2(430+60, 150), rl.Violet) - raylib.DrawTriangleLines(raylib.NewVector2(430, 160), - raylib.NewVector2(430-20, 230), - raylib.NewVector2(430+20, 230), raylib.DarkBlue) + rl.DrawTriangleLines(rl.NewVector2(430, 160), + rl.NewVector2(430-20, 230), + rl.NewVector2(430+20, 230), rl.DarkBlue) - raylib.DrawPoly(raylib.NewVector2(430, 320), 6, 80, 0, raylib.Brown) + rl.DrawPoly(rl.NewVector2(430, 320), 6, 80, 0, rl.Brown) // Activate our custom shader to be applied on next shapes/textures drawings - raylib.BeginShaderMode(shader) + rl.BeginShaderMode(shader) - raylib.DrawTexture(fudesumi, 500, -30, raylib.White) // Using custom shader + rl.DrawTexture(fudesumi, 500, -30, rl.White) // Using custom shader // Activate our default shader for next drawings - raylib.EndShaderMode() + rl.EndShaderMode() - raylib.DrawText("(c) Fudesumi sprite by Eiden Marsal", 380, screenHeight-20, 10, raylib.Gray) + rl.DrawText("(c) Fudesumi sprite by Eiden Marsal", 380, screenHeight-20, 10, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadShader(shader) // Unload shader - raylib.UnloadTexture(fudesumi) // Unload texture + rl.UnloadShader(shader) // Unload shader + rl.UnloadTexture(fudesumi) // Unload texture - raylib.CloseWindow() // Close window and OpenGL context + rl.CloseWindow() // Close window and OpenGL context } diff --git a/examples/shapes/basic_shapes/main.go b/examples/shapes/basic_shapes/main.go index 18517a4..62fbdb9 100644 --- a/examples/shapes/basic_shapes/main.go +++ b/examples/shapes/basic_shapes/main.go @@ -8,38 +8,38 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [shapes] example - basic shapes drawing") + rl.InitWindow(screenWidth, screenHeight, "raylib [shapes] example - basic shapes drawing") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + for !rl.WindowShouldClose() { + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("some basic shapes available on raylib", 20, 20, 20, raylib.DarkGray) + rl.DrawText("some basic shapes available on raylib", 20, 20, 20, rl.DarkGray) - raylib.DrawLine(18, 42, screenWidth-18, 42, raylib.Black) + rl.DrawLine(18, 42, screenWidth-18, 42, rl.Black) - raylib.DrawCircle(screenWidth/4, 120, 35, raylib.DarkBlue) - raylib.DrawCircleGradient(screenWidth/4, 220, 60, raylib.Green, raylib.SkyBlue) - raylib.DrawCircleLines(screenWidth/4, 340, 80, raylib.DarkBlue) + rl.DrawCircle(screenWidth/4, 120, 35, rl.DarkBlue) + rl.DrawCircleGradient(screenWidth/4, 220, 60, rl.Green, rl.SkyBlue) + rl.DrawCircleLines(screenWidth/4, 340, 80, rl.DarkBlue) - raylib.DrawRectangle(screenWidth/4*2-60, 100, 120, 60, raylib.Red) - raylib.DrawRectangleGradientH(screenWidth/4*2-90, 170, 180, 130, raylib.Maroon, raylib.Gold) - raylib.DrawRectangleLines(screenWidth/4*2-40, 320, 80, 60, raylib.Orange) + rl.DrawRectangle(screenWidth/4*2-60, 100, 120, 60, rl.Red) + rl.DrawRectangleGradientH(screenWidth/4*2-90, 170, 180, 130, rl.Maroon, rl.Gold) + rl.DrawRectangleLines(screenWidth/4*2-40, 320, 80, 60, rl.Orange) - raylib.DrawTriangle(raylib.NewVector2(float32(screenWidth)/4*3, 80), - raylib.NewVector2(float32(screenWidth)/4*3-60, 150), - raylib.NewVector2(float32(screenWidth)/4*3+60, 150), raylib.Violet) + rl.DrawTriangle(rl.NewVector2(float32(screenWidth)/4*3, 80), + rl.NewVector2(float32(screenWidth)/4*3-60, 150), + rl.NewVector2(float32(screenWidth)/4*3+60, 150), rl.Violet) - raylib.DrawTriangleLines(raylib.NewVector2(float32(screenWidth)/4*3, 160), - raylib.NewVector2(float32(screenWidth)/4*3-20, 230), - raylib.NewVector2(float32(screenWidth)/4*3+20, 230), raylib.DarkBlue) + rl.DrawTriangleLines(rl.NewVector2(float32(screenWidth)/4*3, 160), + rl.NewVector2(float32(screenWidth)/4*3-20, 230), + rl.NewVector2(float32(screenWidth)/4*3+20, 230), rl.DarkBlue) - raylib.DrawPoly(raylib.NewVector2(float32(screenWidth)/4*3, 320), 6, 80, 0, raylib.Brown) + rl.DrawPoly(rl.NewVector2(float32(screenWidth)/4*3, 320), 6, 80, 0, rl.Brown) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/shapes/colors_pallete/main.go b/examples/shapes/colors_pallete/main.go index ccba781..c732bbb 100644 --- a/examples/shapes/colors_pallete/main.go +++ b/examples/shapes/colors_pallete/main.go @@ -5,62 +5,62 @@ import ( ) func main() { - raylib.InitWindow(800, 450, "raylib [shapes] example - raylib color palette") - raylib.SetTargetFPS(60) + rl.InitWindow(800, 450, "raylib [shapes] example - raylib color palette") + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("raylib color palette", 28, 42, 20, raylib.Black) + rl.DrawText("raylib color palette", 28, 42, 20, rl.Black) - raylib.DrawRectangle(26, 80, 100, 100, raylib.DarkGray) - raylib.DrawRectangle(26, 188, 100, 100, raylib.Gray) - raylib.DrawRectangle(26, 296, 100, 100, raylib.LightGray) - raylib.DrawRectangle(134, 80, 100, 100, raylib.Maroon) - raylib.DrawRectangle(134, 188, 100, 100, raylib.Red) - raylib.DrawRectangle(134, 296, 100, 100, raylib.Pink) - raylib.DrawRectangle(242, 80, 100, 100, raylib.Orange) - raylib.DrawRectangle(242, 188, 100, 100, raylib.Gold) - raylib.DrawRectangle(242, 296, 100, 100, raylib.Yellow) - raylib.DrawRectangle(350, 80, 100, 100, raylib.DarkGreen) - raylib.DrawRectangle(350, 188, 100, 100, raylib.Lime) - raylib.DrawRectangle(350, 296, 100, 100, raylib.Green) - raylib.DrawRectangle(458, 80, 100, 100, raylib.DarkBlue) - raylib.DrawRectangle(458, 188, 100, 100, raylib.Blue) - raylib.DrawRectangle(458, 296, 100, 100, raylib.SkyBlue) - raylib.DrawRectangle(566, 80, 100, 100, raylib.DarkPurple) - raylib.DrawRectangle(566, 188, 100, 100, raylib.Violet) - raylib.DrawRectangle(566, 296, 100, 100, raylib.Purple) - raylib.DrawRectangle(674, 80, 100, 100, raylib.DarkBrown) - raylib.DrawRectangle(674, 188, 100, 100, raylib.Brown) - raylib.DrawRectangle(674, 296, 100, 100, raylib.Beige) + rl.DrawRectangle(26, 80, 100, 100, rl.DarkGray) + rl.DrawRectangle(26, 188, 100, 100, rl.Gray) + rl.DrawRectangle(26, 296, 100, 100, rl.LightGray) + rl.DrawRectangle(134, 80, 100, 100, rl.Maroon) + rl.DrawRectangle(134, 188, 100, 100, rl.Red) + rl.DrawRectangle(134, 296, 100, 100, rl.Pink) + rl.DrawRectangle(242, 80, 100, 100, rl.Orange) + rl.DrawRectangle(242, 188, 100, 100, rl.Gold) + rl.DrawRectangle(242, 296, 100, 100, rl.Yellow) + rl.DrawRectangle(350, 80, 100, 100, rl.DarkGreen) + rl.DrawRectangle(350, 188, 100, 100, rl.Lime) + rl.DrawRectangle(350, 296, 100, 100, rl.Green) + rl.DrawRectangle(458, 80, 100, 100, rl.DarkBlue) + rl.DrawRectangle(458, 188, 100, 100, rl.Blue) + rl.DrawRectangle(458, 296, 100, 100, rl.SkyBlue) + rl.DrawRectangle(566, 80, 100, 100, rl.DarkPurple) + rl.DrawRectangle(566, 188, 100, 100, rl.Violet) + rl.DrawRectangle(566, 296, 100, 100, rl.Purple) + rl.DrawRectangle(674, 80, 100, 100, rl.DarkBrown) + rl.DrawRectangle(674, 188, 100, 100, rl.Brown) + rl.DrawRectangle(674, 296, 100, 100, rl.Beige) - raylib.DrawText("DARKGRAY", 65, 166, 10, raylib.Black) - raylib.DrawText("GRAY", 93, 274, 10, raylib.Black) - raylib.DrawText("LIGHTGRAY", 61, 382, 10, raylib.Black) - raylib.DrawText("MAROON", 186, 166, 10, raylib.Black) - raylib.DrawText("RED", 208, 274, 10, raylib.Black) - raylib.DrawText("PINK", 204, 382, 10, raylib.Black) - raylib.DrawText("ORANGE", 295, 166, 10, raylib.Black) - raylib.DrawText("GOLD", 310, 274, 10, raylib.Black) - raylib.DrawText("YELLOW", 300, 382, 10, raylib.Black) - raylib.DrawText("DARKGREEN", 382, 166, 10, raylib.Black) - raylib.DrawText("LIME", 420, 274, 10, raylib.Black) - raylib.DrawText("GREEN", 410, 382, 10, raylib.Black) - raylib.DrawText("DARKBLUE", 498, 166, 10, raylib.Black) - raylib.DrawText("BLUE", 526, 274, 10, raylib.Black) - raylib.DrawText("SKYBLUE", 505, 382, 10, raylib.Black) - raylib.DrawText("DARKPURPLE", 592, 166, 10, raylib.Black) - raylib.DrawText("VIOLET", 621, 274, 10, raylib.Black) - raylib.DrawText("PURPLE", 620, 382, 10, raylib.Black) - raylib.DrawText("DARKBROWN", 705, 166, 10, raylib.Black) - raylib.DrawText("BROWN", 733, 274, 10, raylib.Black) - raylib.DrawText("BEIGE", 737, 382, 10, raylib.Black) + rl.DrawText("DARKGRAY", 65, 166, 10, rl.Black) + rl.DrawText("GRAY", 93, 274, 10, rl.Black) + rl.DrawText("LIGHTGRAY", 61, 382, 10, rl.Black) + rl.DrawText("MAROON", 186, 166, 10, rl.Black) + rl.DrawText("RED", 208, 274, 10, rl.Black) + rl.DrawText("PINK", 204, 382, 10, rl.Black) + rl.DrawText("ORANGE", 295, 166, 10, rl.Black) + rl.DrawText("GOLD", 310, 274, 10, rl.Black) + rl.DrawText("YELLOW", 300, 382, 10, rl.Black) + rl.DrawText("DARKGREEN", 382, 166, 10, rl.Black) + rl.DrawText("LIME", 420, 274, 10, rl.Black) + rl.DrawText("GREEN", 410, 382, 10, rl.Black) + rl.DrawText("DARKBLUE", 498, 166, 10, rl.Black) + rl.DrawText("BLUE", 526, 274, 10, rl.Black) + rl.DrawText("SKYBLUE", 505, 382, 10, rl.Black) + rl.DrawText("DARKPURPLE", 592, 166, 10, rl.Black) + rl.DrawText("VIOLET", 621, 274, 10, rl.Black) + rl.DrawText("PURPLE", 620, 382, 10, rl.Black) + rl.DrawText("DARKBROWN", 705, 166, 10, rl.Black) + rl.DrawText("BROWN", 733, 274, 10, rl.Black) + rl.DrawText("BEIGE", 737, 382, 10, rl.Black) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/shapes/lines_bezier/main.go b/examples/shapes/lines_bezier/main.go index 7ca9377..dd2e0bb 100644 --- a/examples/shapes/lines_bezier/main.go +++ b/examples/shapes/lines_bezier/main.go @@ -8,29 +8,29 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [shapes] example - cubic-bezier lines") + rl.InitWindow(screenWidth, screenHeight, "raylib [shapes] example - cubic-bezier lines") - start := raylib.NewVector2(0, 0) - end := raylib.NewVector2(float32(screenWidth), float32(screenHeight)) + start := rl.NewVector2(0, 0) + end := rl.NewVector2(float32(screenWidth), float32(screenHeight)) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { - start = raylib.GetMousePosition() - } else if raylib.IsMouseButtonDown(raylib.MouseRightButton) { - end = raylib.GetMousePosition() + for !rl.WindowShouldClose() { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { + start = rl.GetMousePosition() + } else if rl.IsMouseButtonDown(rl.MouseRightButton) { + end = rl.GetMousePosition() } - raylib.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("USE MOUSE LEFT-RIGHT CLICK to DEFINE LINE START and END POINTS", 15, 20, 20, raylib.Gray) + rl.DrawText("USE MOUSE LEFT-RIGHT CLICK to DEFINE LINE START and END POINTS", 15, 20, 20, rl.Gray) - raylib.DrawLineBezier(start, end, 2.0, raylib.Red) + rl.DrawLineBezier(start, end, 2.0, rl.Red) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/shapes/logo_raylib/main.go b/examples/shapes/logo_raylib/main.go index cbe98f9..79b2062 100644 --- a/examples/shapes/logo_raylib/main.go +++ b/examples/shapes/logo_raylib/main.go @@ -8,22 +8,22 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [shapes] example - raylib logo using shapes") + rl.InitWindow(screenWidth, screenHeight, "raylib [shapes] example - raylib logo using shapes") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + for !rl.WindowShouldClose() { + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) - raylib.DrawRectangle(screenWidth/2-128, screenHeight/2-128, 256, 256, raylib.Black) - raylib.DrawRectangle(screenWidth/2-112, screenHeight/2-112, 224, 224, raylib.RayWhite) - raylib.DrawText("raylib", screenWidth/2-44, screenHeight/2+48, 50, raylib.Black) + rl.DrawRectangle(screenWidth/2-128, screenHeight/2-128, 256, 256, rl.Black) + rl.DrawRectangle(screenWidth/2-112, screenHeight/2-112, 224, 224, rl.RayWhite) + rl.DrawText("raylib", screenWidth/2-44, screenHeight/2+48, 50, rl.Black) - raylib.DrawText("this is NOT a texture!", 350, 370, 10, raylib.Gray) + rl.DrawText("this is NOT a texture!", 350, 370, 10, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/shapes/logo_raylib_anim/main.go b/examples/shapes/logo_raylib_anim/main.go index fbc2f4a..931084e 100644 --- a/examples/shapes/logo_raylib_anim/main.go +++ b/examples/shapes/logo_raylib_anim/main.go @@ -8,7 +8,7 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [shapes] example - raylib logo animation") + rl.InitWindow(screenWidth, screenHeight, "raylib [shapes] example - raylib logo animation") logoPositionX := screenWidth/2 - 128 logoPositionY := screenHeight/2 - 128 @@ -25,9 +25,9 @@ func main() { state := 0 // Tracking animation states (State Machine) alpha := float32(1.0) // Useful for fading - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { if state == 0 { // State 0: Small box blinking framesCounter++ @@ -66,7 +66,7 @@ func main() { } } } else if state == 4 { // State 4: Reset and Replay - if raylib.IsKeyPressed(raylib.KeyR) { + if rl.IsKeyPressed(rl.KeyR) { framesCounter = 0 lettersCount = 0 @@ -81,30 +81,30 @@ func main() { } } - raylib.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) if state == 0 { if (framesCounter/15)%2 == 0 { - raylib.DrawRectangle(logoPositionX, logoPositionY, 16, 16, raylib.Black) + rl.DrawRectangle(logoPositionX, logoPositionY, 16, 16, rl.Black) } } else if state == 1 { - raylib.DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, raylib.Black) - raylib.DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, raylib.Black) + rl.DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, rl.Black) + rl.DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, rl.Black) } else if state == 2 { - raylib.DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, raylib.Black) - raylib.DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, raylib.Black) + rl.DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, rl.Black) + rl.DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, rl.Black) - raylib.DrawRectangle(logoPositionX+240, logoPositionY, 16, rightSideRecHeight, raylib.Black) - raylib.DrawRectangle(logoPositionX, logoPositionY+240, bottomSideRecWidth, 16, raylib.Black) + rl.DrawRectangle(logoPositionX+240, logoPositionY, 16, rightSideRecHeight, rl.Black) + rl.DrawRectangle(logoPositionX, logoPositionY+240, bottomSideRecWidth, 16, rl.Black) } else if state == 3 { - raylib.DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, raylib.Fade(raylib.Black, alpha)) - raylib.DrawRectangle(logoPositionX, logoPositionY+16, 16, leftSideRecHeight-32, raylib.Fade(raylib.Black, alpha)) + rl.DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, rl.Fade(rl.Black, alpha)) + rl.DrawRectangle(logoPositionX, logoPositionY+16, 16, leftSideRecHeight-32, rl.Fade(rl.Black, alpha)) - raylib.DrawRectangle(logoPositionX+240, logoPositionY+16, 16, rightSideRecHeight-32, raylib.Fade(raylib.Black, alpha)) - raylib.DrawRectangle(logoPositionX, logoPositionY+240, bottomSideRecWidth, 16, raylib.Fade(raylib.Black, alpha)) + rl.DrawRectangle(logoPositionX+240, logoPositionY+16, 16, rightSideRecHeight-32, rl.Fade(rl.Black, alpha)) + rl.DrawRectangle(logoPositionX, logoPositionY+240, bottomSideRecWidth, 16, rl.Fade(rl.Black, alpha)) - raylib.DrawRectangle(screenWidth/2-112, screenHeight/2-112, 224, 224, raylib.Fade(raylib.RayWhite, alpha)) + rl.DrawRectangle(screenWidth/2-112, screenHeight/2-112, 224, 224, rl.Fade(rl.RayWhite, alpha)) text := "raylib" length := int32(len(text)) @@ -112,13 +112,13 @@ func main() { lettersCount = length } - raylib.DrawText(text[0:lettersCount], screenWidth/2-44, screenHeight/2+48, 50, raylib.Fade(raylib.Black, alpha)) + rl.DrawText(text[0:lettersCount], screenWidth/2-44, screenHeight/2+48, 50, rl.Fade(rl.Black, alpha)) } else if state == 4 { - raylib.DrawText("[R] REPLAY", 340, 200, 20, raylib.Gray) + rl.DrawText("[R] REPLAY", 340, 200, 20, rl.Gray) } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/text/bmfont_ttf/main.go b/examples/text/bmfont_ttf/main.go index ee6fb87..1378eae 100644 --- a/examples/text/bmfont_ttf/main.go +++ b/examples/text/bmfont_ttf/main.go @@ -8,35 +8,35 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [text] example - bmfont and ttf sprite fonts loading") + rl.InitWindow(screenWidth, screenHeight, "raylib [text] example - bmfont and ttf sprite fonts loading") msgBm := "THIS IS AN AngelCode SPRITE FONT" msgTtf := "THIS SPRITE FONT has been GENERATED from a TTF" // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) - fontBm := raylib.LoadFont("fonts/bmfont.fnt") // BMFont (AngelCode) - fontTtf := raylib.LoadFont("fonts/pixantiqua.ttf") // TTF font + fontBm := rl.LoadFont("fonts/bmfont.fnt") // BMFont (AngelCode) + fontTtf := rl.LoadFont("fonts/pixantiqua.ttf") // TTF font - fontPosition := raylib.Vector2{} + fontPosition := rl.Vector2{} - fontPosition.X = float32(screenWidth)/2 - raylib.MeasureTextEx(fontBm, msgBm, float32(fontBm.BaseSize), 0).X/2 + fontPosition.X = float32(screenWidth)/2 - rl.MeasureTextEx(fontBm, msgBm, float32(fontBm.BaseSize), 0).X/2 fontPosition.Y = float32(screenHeight)/2 - float32(fontBm.BaseSize)/2 - 80 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawTextEx(fontBm, msgBm, fontPosition, float32(fontBm.BaseSize), 0, raylib.Maroon) - raylib.DrawTextEx(fontTtf, msgTtf, raylib.NewVector2(75.0, 240.0), float32(fontTtf.BaseSize)*0.8, 2, raylib.Lime) + rl.DrawTextEx(fontBm, msgBm, fontPosition, float32(fontBm.BaseSize), 0, rl.Maroon) + rl.DrawTextEx(fontTtf, msgTtf, rl.NewVector2(75.0, 240.0), float32(fontTtf.BaseSize)*0.8, 2, rl.Lime) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadFont(fontBm) // AngelCode Font unloading - raylib.UnloadFont(fontTtf) // TTF Font unloading + rl.UnloadFont(fontBm) // AngelCode Font unloading + rl.UnloadFont(fontTtf) // TTF Font unloading - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/text/bmfont_unordered/main.go b/examples/text/bmfont_unordered/main.go index a3238e3..54df292 100644 --- a/examples/text/bmfont_unordered/main.go +++ b/examples/text/bmfont_unordered/main.go @@ -10,32 +10,32 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [text] example - bmfont unordered loading and drawing") + rl.InitWindow(screenWidth, screenHeight, "raylib [text] example - bmfont unordered loading and drawing") // NOTE: Using chars outside the [32..127] limits! // NOTE: If a character is not found in the font, it just renders a space msg := "ASCII extended characters:\n¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆ\nÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæ\nçèéêëìíîïðñòóôõö÷øùúûüýþÿ" // NOTE: Loaded font has an unordered list of characters (chars in the range 32..255) - font := raylib.LoadFont("fonts/pixantiqua.fnt") // BMFont (AngelCode) + font := rl.LoadFont("fonts/pixantiqua.fnt") // BMFont (AngelCode) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("Font name: PixAntiqua", 40, 50, 20, raylib.Gray) - raylib.DrawText(fmt.Sprintf("Font base size: %d", font.BaseSize), 40, 80, 20, raylib.Gray) - raylib.DrawText(fmt.Sprintf("Font chars number: %d", font.CharsCount), 40, 110, 20, raylib.Gray) + rl.DrawText("Font name: PixAntiqua", 40, 50, 20, rl.Gray) + rl.DrawText(fmt.Sprintf("Font base size: %d", font.BaseSize), 40, 80, 20, rl.Gray) + rl.DrawText(fmt.Sprintf("Font chars number: %d", font.CharsCount), 40, 110, 20, rl.Gray) - raylib.DrawTextEx(font, msg, raylib.NewVector2(40, 180), float32(font.BaseSize), 0, raylib.Maroon) + rl.DrawTextEx(font, msg, rl.NewVector2(40, 180), float32(font.BaseSize), 0, rl.Maroon) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadFont(font) // AngelCode Font unloading + rl.UnloadFont(font) // AngelCode Font unloading - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/text/format_text/main.go b/examples/text/format_text/main.go index f741f33..d5962c0 100644 --- a/examples/text/format_text/main.go +++ b/examples/text/format_text/main.go @@ -10,29 +10,29 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [text] example - text formatting") + rl.InitWindow(screenWidth, screenHeight, "raylib [text] example - text formatting") score := 100020 hiscore := 200450 lives := 5 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText(fmt.Sprintf("Score: %08d", score), 200, 80, 20, raylib.Red) + rl.DrawText(fmt.Sprintf("Score: %08d", score), 200, 80, 20, rl.Red) - raylib.DrawText(fmt.Sprintf("HiScore: %08d", hiscore), 200, 120, 20, raylib.Green) + rl.DrawText(fmt.Sprintf("HiScore: %08d", hiscore), 200, 120, 20, rl.Green) - raylib.DrawText(fmt.Sprintf("Lives: %02d", lives), 200, 160, 40, raylib.Blue) + rl.DrawText(fmt.Sprintf("Lives: %02d", lives), 200, 160, 40, rl.Blue) - raylib.DrawText(fmt.Sprintf("Elapsed Time: %02.02f ms", raylib.GetFrameTime()*1000), 200, 220, 20, raylib.Black) + rl.DrawText(fmt.Sprintf("Elapsed Time: %02.02f ms", rl.GetFrameTime()*1000), 200, 220, 20, rl.Black) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/text/raylib_fonts/main.go b/examples/text/raylib_fonts/main.go index 8576528..fcecdac 100644 --- a/examples/text/raylib_fonts/main.go +++ b/examples/text/raylib_fonts/main.go @@ -10,17 +10,17 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [text] example - raylib fonts") + rl.InitWindow(screenWidth, screenHeight, "raylib [text] example - raylib fonts") - fonts := make([]raylib.Font, maxFonts) - fonts[0] = raylib.LoadFont("fonts/alagard.png") - fonts[1] = raylib.LoadFont("fonts/pixelplay.png") - fonts[2] = raylib.LoadFont("fonts/mecha.png") - fonts[3] = raylib.LoadFont("fonts/setback.png") - fonts[4] = raylib.LoadFont("fonts/romulus.png") - fonts[5] = raylib.LoadFont("fonts/pixantiqua.png") - fonts[6] = raylib.LoadFont("fonts/alpha_beta.png") - fonts[7] = raylib.LoadFont("fonts/jupiter_crash.png") + fonts := make([]rl.Font, maxFonts) + fonts[0] = rl.LoadFont("fonts/alagard.png") + fonts[1] = rl.LoadFont("fonts/pixelplay.png") + fonts[2] = rl.LoadFont("fonts/mecha.png") + fonts[3] = rl.LoadFont("fonts/setback.png") + fonts[4] = rl.LoadFont("fonts/romulus.png") + fonts[5] = rl.LoadFont("fonts/pixantiqua.png") + fonts[6] = rl.LoadFont("fonts/alpha_beta.png") + fonts[7] = rl.LoadFont("fonts/jupiter_crash.png") messages := []string{ "ALAGARD FONT designed by Hewett Tsoi", @@ -34,13 +34,13 @@ func main() { } spacings := []float32{2, 4, 8, 4, 3, 4, 4, 1} - positions := make([]raylib.Vector2, maxFonts) + positions := make([]rl.Vector2, maxFonts) var i int32 for i = 0; i < maxFonts; i++ { - x := screenWidth/2 - int32(raylib.MeasureTextEx(fonts[i], messages[i], float32(fonts[i].BaseSize*2), spacings[i]).X/2) + x := screenWidth/2 - int32(rl.MeasureTextEx(fonts[i], messages[i], float32(fonts[i].BaseSize*2), spacings[i]).X/2) y := 60 + fonts[i].BaseSize + 45*i - positions[i] = raylib.NewVector2(float32(x), float32(y)) + positions[i] = rl.NewVector2(float32(x), float32(y)) } // Small Y position corrections @@ -48,27 +48,27 @@ func main() { positions[4].Y += 2 positions[7].Y -= 8 - colors := []raylib.Color{raylib.Maroon, raylib.Orange, raylib.DarkGreen, raylib.DarkBlue, raylib.DarkPurple, raylib.Lime, raylib.Gold, raylib.DarkBrown} + colors := []rl.Color{rl.Maroon, rl.Orange, rl.DarkGreen, rl.DarkBlue, rl.DarkPurple, rl.Lime, rl.Gold, rl.DarkBrown} - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) - raylib.DrawText("free fonts included with raylib", 250, 20, 20, raylib.DarkGray) - raylib.DrawLine(220, 50, 590, 50, raylib.DarkGray) + rl.ClearBackground(rl.RayWhite) + rl.DrawText("free fonts included with raylib", 250, 20, 20, rl.DarkGray) + rl.DrawLine(220, 50, 590, 50, rl.DarkGray) for i = 0; i < maxFonts; i++ { - raylib.DrawTextEx(fonts[i], messages[i], positions[i], float32(fonts[i].BaseSize*2), spacings[i], colors[i]) + rl.DrawTextEx(fonts[i], messages[i], positions[i], float32(fonts[i].BaseSize*2), spacings[i], colors[i]) } - raylib.EndDrawing() + rl.EndDrawing() } for i = 0; i < maxFonts; i++ { - raylib.UnloadFont(fonts[i]) + rl.UnloadFont(fonts[i]) } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/text/sprite_fonts/main.go b/examples/text/sprite_fonts/main.go index b49bde8..0958818 100644 --- a/examples/text/sprite_fonts/main.go +++ b/examples/text/sprite_fonts/main.go @@ -8,45 +8,45 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite fonts usage") + rl.InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite fonts usage") msg1 := "THIS IS A custom SPRITE FONT..." msg2 := "...and this is ANOTHER CUSTOM font..." msg3 := "...and a THIRD one! GREAT! :D" // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) - font1 := raylib.LoadFont("fonts/custom_mecha.png") // Font loading - font2 := raylib.LoadFont("fonts/custom_alagard.png") // Font loading - font3 := raylib.LoadFont("fonts/custom_jupiter_crash.png") // Font loading + font1 := rl.LoadFont("fonts/custom_mecha.png") // Font loading + font2 := rl.LoadFont("fonts/custom_alagard.png") // Font loading + font3 := rl.LoadFont("fonts/custom_jupiter_crash.png") // Font loading - var fontPosition1, fontPosition2, fontPosition3 raylib.Vector2 + var fontPosition1, fontPosition2, fontPosition3 rl.Vector2 - fontPosition1.X = float32(screenWidth)/2 - raylib.MeasureTextEx(font1, msg1, float32(font1.BaseSize), -3).X/2 + fontPosition1.X = float32(screenWidth)/2 - rl.MeasureTextEx(font1, msg1, float32(font1.BaseSize), -3).X/2 fontPosition1.Y = float32(screenHeight)/2 - float32(font1.BaseSize)/2 - 80 - fontPosition2.X = float32(screenWidth)/2 - raylib.MeasureTextEx(font2, msg2, float32(font2.BaseSize), -2).X/2 + fontPosition2.X = float32(screenWidth)/2 - rl.MeasureTextEx(font2, msg2, float32(font2.BaseSize), -2).X/2 fontPosition2.Y = float32(screenHeight)/2 - float32(font2.BaseSize)/2 - 10 - fontPosition3.X = float32(screenWidth)/2 - raylib.MeasureTextEx(font3, msg3, float32(font3.BaseSize), 2).X/2 + fontPosition3.X = float32(screenWidth)/2 - rl.MeasureTextEx(font3, msg3, float32(font3.BaseSize), 2).X/2 fontPosition3.Y = float32(screenHeight)/2 - float32(font3.BaseSize)/2 + 50 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawTextEx(font1, msg1, fontPosition1, float32(font1.BaseSize), -3, raylib.White) - raylib.DrawTextEx(font2, msg2, fontPosition2, float32(font2.BaseSize), -2, raylib.White) - raylib.DrawTextEx(font3, msg3, fontPosition3, float32(font3.BaseSize), 2, raylib.White) + rl.DrawTextEx(font1, msg1, fontPosition1, float32(font1.BaseSize), -3, rl.White) + rl.DrawTextEx(font2, msg2, fontPosition2, float32(font2.BaseSize), -2, rl.White) + rl.DrawTextEx(font3, msg3, fontPosition3, float32(font3.BaseSize), 2, rl.White) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadFont(font1) // Font unloading - raylib.UnloadFont(font2) // Font unloading - raylib.UnloadFont(font3) // Font unloading + rl.UnloadFont(font1) // Font unloading + rl.UnloadFont(font2) // Font unloading + rl.UnloadFont(font3) // Font unloading - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/text/ttf_loading/main.go b/examples/text/ttf_loading/main.go index f03e40d..38ed446 100644 --- a/examples/text/ttf_loading/main.go +++ b/examples/text/ttf_loading/main.go @@ -10,7 +10,7 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [text] example - ttf loading") + rl.InitWindow(screenWidth, screenHeight, "raylib [text] example - ttf loading") msg := "TTF Font" @@ -19,94 +19,94 @@ func main() { fontChars := int32(0) // TTF Font loading with custom generation parameters - font := raylib.LoadFontEx("fonts/KAISG.ttf", 96, 0, &fontChars) + font := rl.LoadFontEx("fonts/KAISG.ttf", 96, 0, &fontChars) // Generate mipmap levels to use trilinear filtering // NOTE: On 2D drawing it won't be noticeable, it looks like FILTER_BILINEAR - raylib.GenTextureMipmaps(&font.Texture) + rl.GenTextureMipmaps(&font.Texture) fontSize := font.BaseSize - fontPosition := raylib.NewVector2(40, float32(screenHeight)/2+50) - textSize := raylib.Vector2{} + fontPosition := rl.NewVector2(40, float32(screenHeight)/2+50) + textSize := rl.Vector2{} - raylib.SetTextureFilter(font.Texture, raylib.FilterPoint) + rl.SetTextureFilter(font.Texture, rl.FilterPoint) currentFontFilter := 0 // FilterPoint count := int32(0) droppedFiles := make([]string, 0) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update //---------------------------------------------------------------------------------- - fontSize += raylib.GetMouseWheelMove() * 4.0 + fontSize += rl.GetMouseWheelMove() * 4.0 // Choose font texture filter method - if raylib.IsKeyPressed(raylib.KeyOne) { - raylib.SetTextureFilter(font.Texture, raylib.FilterPoint) + if rl.IsKeyPressed(rl.KeyOne) { + rl.SetTextureFilter(font.Texture, rl.FilterPoint) currentFontFilter = 0 - } else if raylib.IsKeyPressed(raylib.KeyTwo) { - raylib.SetTextureFilter(font.Texture, raylib.FilterBilinear) + } else if rl.IsKeyPressed(rl.KeyTwo) { + rl.SetTextureFilter(font.Texture, rl.FilterBilinear) currentFontFilter = 1 - } else if raylib.IsKeyPressed(raylib.KeyThree) { + } else if rl.IsKeyPressed(rl.KeyThree) { // NOTE: Trilinear filter won't be noticed on 2D drawing - raylib.SetTextureFilter(font.Texture, raylib.FilterTrilinear) + rl.SetTextureFilter(font.Texture, rl.FilterTrilinear) currentFontFilter = 2 } - textSize = raylib.MeasureTextEx(font, msg, float32(fontSize), 0) + textSize = rl.MeasureTextEx(font, msg, float32(fontSize), 0) - if raylib.IsKeyDown(raylib.KeyLeft) { + if rl.IsKeyDown(rl.KeyLeft) { fontPosition.X -= 10 - } else if raylib.IsKeyDown(raylib.KeyRight) { + } else if rl.IsKeyDown(rl.KeyRight) { fontPosition.X += 10 } // Load a dropped TTF file dynamically (at current fontSize) - if raylib.IsFileDropped() { - droppedFiles = raylib.GetDroppedFiles(&count) + if rl.IsFileDropped() { + droppedFiles = rl.GetDroppedFiles(&count) if count == 1 { // Only support one ttf file dropped - raylib.UnloadFont(font) - font = raylib.LoadFontEx(droppedFiles[0], fontSize, 0, &fontChars) - raylib.ClearDroppedFiles() + rl.UnloadFont(font) + font = rl.LoadFontEx(droppedFiles[0], fontSize, 0, &fontChars) + rl.ClearDroppedFiles() } } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("Use mouse wheel to change font size", 20, 20, 10, raylib.Gray) - raylib.DrawText("Use KEY_RIGHT and KEY_LEFT to move text", 20, 40, 10, raylib.Gray) - raylib.DrawText("Use 1, 2, 3 to change texture filter", 20, 60, 10, raylib.Gray) - raylib.DrawText("Drop a new TTF font for dynamic loading", 20, 80, 10, raylib.DarkGray) + rl.DrawText("Use mouse wheel to change font size", 20, 20, 10, rl.Gray) + rl.DrawText("Use KEY_RIGHT and KEY_LEFT to move text", 20, 40, 10, rl.Gray) + rl.DrawText("Use 1, 2, 3 to change texture filter", 20, 60, 10, rl.Gray) + rl.DrawText("Drop a new TTF font for dynamic loading", 20, 80, 10, rl.DarkGray) - raylib.DrawTextEx(font, msg, fontPosition, float32(fontSize), 0, raylib.Black) + rl.DrawTextEx(font, msg, fontPosition, float32(fontSize), 0, rl.Black) // TODO: It seems texSize measurement is not accurate due to chars offsets... - //raylib.DrawRectangleLines(int32(fontPosition.X), int32(fontPosition.Y), int32(textSize.X), int32(textSize.Y), raylib.Red) + //rl.DrawRectangleLines(int32(fontPosition.X), int32(fontPosition.Y), int32(textSize.X), int32(textSize.Y), rl.Red) - raylib.DrawRectangle(0, screenHeight-80, screenWidth, 80, raylib.LightGray) - raylib.DrawText(fmt.Sprintf("Font size: %02.02f", float32(fontSize)), 20, screenHeight-50, 10, raylib.DarkGray) - raylib.DrawText(fmt.Sprintf("Text size: [%02.02f, %02.02f]", textSize.X, textSize.Y), 20, screenHeight-30, 10, raylib.DarkGray) - raylib.DrawText("CURRENT TEXTURE FILTER:", 250, 400, 20, raylib.Gray) + rl.DrawRectangle(0, screenHeight-80, screenWidth, 80, rl.LightGray) + rl.DrawText(fmt.Sprintf("Font size: %02.02f", float32(fontSize)), 20, screenHeight-50, 10, rl.DarkGray) + rl.DrawText(fmt.Sprintf("Text size: [%02.02f, %02.02f]", textSize.X, textSize.Y), 20, screenHeight-30, 10, rl.DarkGray) + rl.DrawText("CURRENT TEXTURE FILTER:", 250, 400, 20, rl.Gray) if currentFontFilter == 0 { - raylib.DrawText("POINT", 570, 400, 20, raylib.Black) + rl.DrawText("POINT", 570, 400, 20, rl.Black) } else if currentFontFilter == 1 { - raylib.DrawText("BILINEAR", 570, 400, 20, raylib.Black) + rl.DrawText("BILINEAR", 570, 400, 20, rl.Black) } else if currentFontFilter == 2 { - raylib.DrawText("TRILINEAR", 570, 400, 20, raylib.Black) + rl.DrawText("TRILINEAR", 570, 400, 20, rl.Black) } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadFont(font) // Font unloading + rl.UnloadFont(font) // Font unloading - raylib.ClearDroppedFiles() // Clear internal buffers + rl.ClearDroppedFiles() // Clear internal buffers - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/text/writing_anim/main.go b/examples/text/writing_anim/main.go index 78fddbb..daabe5b 100644 --- a/examples/text/writing_anim/main.go +++ b/examples/text/writing_anim/main.go @@ -8,24 +8,24 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [text] example - text writing anim") + rl.InitWindow(screenWidth, screenHeight, "raylib [text] example - text writing anim") message := "This sample illustrates a text writing\nanimation effect! Check it out! ;)" length := len(message) framesCounter := 0 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update - if raylib.IsKeyDown(raylib.KeySpace) { + if rl.IsKeyDown(rl.KeySpace) { framesCounter += 8 } else { framesCounter++ } - if raylib.IsKeyPressed(raylib.KeyEnter) { + if rl.IsKeyPressed(rl.KeyEnter) { framesCounter = 0 } @@ -34,17 +34,17 @@ func main() { } // Draw - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText(message[0:framesCounter/10], 210, 160, 20, raylib.Maroon) + rl.DrawText(message[0:framesCounter/10], 210, 160, 20, rl.Maroon) - raylib.DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, raylib.LightGray) - raylib.DrawText("PRESS [SPACE] to SPEED UP!", 239, 300, 20, raylib.LightGray) + rl.DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, rl.LightGray) + rl.DrawText("PRESS [SPACE] to SPEED UP!", 239, 300, 20, rl.LightGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/image_drawing/main.go b/examples/textures/image_drawing/main.go index 230ce99..46d14f2 100644 --- a/examples/textures/image_drawing/main.go +++ b/examples/textures/image_drawing/main.go @@ -8,42 +8,42 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing") // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - cat := raylib.LoadImage("cat.png") // Load image in CPU memory (RAM) - raylib.ImageCrop(cat, raylib.NewRectangle(100, 10, 280, 380)) // Crop an image piece - raylib.ImageFlipHorizontal(cat) // Flip cropped image horizontally - raylib.ImageResize(cat, 150, 200) // Resize flipped-cropped image + cat := rl.LoadImage("cat.png") // Load image in CPU memory (RAM) + rl.ImageCrop(cat, rl.NewRectangle(100, 10, 280, 380)) // Crop an image piece + rl.ImageFlipHorizontal(cat) // Flip cropped image horizontally + rl.ImageResize(cat, 150, 200) // Resize flipped-cropped image - parrots := raylib.LoadImage("parrots.png") // Load image in CPU memory (RAM) + parrots := rl.LoadImage("parrots.png") // Load image in CPU memory (RAM) // Draw one image over the other with a scaling of 1.5f - raylib.ImageDraw(parrots, cat, raylib.NewRectangle(0, 0, float32(cat.Width), float32(cat.Height)), raylib.NewRectangle(30, 40, float32(cat.Width)*1.5, float32(cat.Height)*1.5)) - raylib.ImageCrop(parrots, raylib.NewRectangle(0, 50, float32(parrots.Width), float32(parrots.Height-100))) // Crop resulting image + rl.ImageDraw(parrots, cat, rl.NewRectangle(0, 0, float32(cat.Width), float32(cat.Height)), rl.NewRectangle(30, 40, float32(cat.Width)*1.5, float32(cat.Height)*1.5)) + rl.ImageCrop(parrots, rl.NewRectangle(0, 50, float32(parrots.Width), float32(parrots.Height-100))) // Crop resulting image - raylib.UnloadImage(cat) // Unload image from RAM + rl.UnloadImage(cat) // Unload image from RAM - texture := raylib.LoadTextureFromImage(parrots) // Image converted to texture, uploaded to GPU memory (VRAM) - raylib.UnloadImage(parrots) // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM + texture := rl.LoadTextureFromImage(parrots) // Image converted to texture, uploaded to GPU memory (VRAM) + rl.UnloadImage(parrots) // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2-40, raylib.White) - raylib.DrawRectangleLines(screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2-40, texture.Width, texture.Height, raylib.DarkGray) + rl.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2-40, rl.White) + rl.DrawRectangleLines(screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2-40, texture.Width, texture.Height, rl.DarkGray) - raylib.DrawText("We are drawing only one texture from various images composed!", 240, 350, 10, raylib.DarkGray) - raylib.DrawText("Source images have been cropped, scaled, flipped and copied one over the other.", 190, 370, 10, raylib.DarkGray) + rl.DrawText("We are drawing only one texture from various images composed!", 240, 350, 10, rl.DarkGray) + rl.DrawText("Source images have been cropped, scaled, flipped and copied one over the other.", 190, 370, 10, rl.DarkGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) + rl.UnloadTexture(texture) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/image_generation/main.go b/examples/textures/image_generation/main.go index 7e963e8..cab5e9a 100644 --- a/examples/textures/image_generation/main.go +++ b/examples/textures/image_generation/main.go @@ -10,85 +10,85 @@ func main() { screenWidth := 800 screenHeight := 450 - raylib.InitWindow(int32(screenWidth), int32(screenHeight), "raylib [textures] example - procedural images generation") + rl.InitWindow(int32(screenWidth), int32(screenHeight), "raylib [textures] example - procedural images generation") - verticalGradient := raylib.GenImageGradientV(screenWidth, screenHeight, raylib.Red, raylib.Blue) - horizontalGradient := raylib.GenImageGradientH(screenWidth, screenHeight, raylib.Red, raylib.Blue) - radialGradient := raylib.GenImageGradientRadial(screenWidth, screenHeight, 0, raylib.White, raylib.Black) - checked := raylib.GenImageChecked(screenWidth, screenHeight, 32, 32, raylib.Red, raylib.Blue) - whiteNoise := raylib.GenImageWhiteNoise(screenWidth, screenHeight, 0.5) - perlinNoise := raylib.GenImagePerlinNoise(screenWidth, screenHeight, 50, 50, 4.0) - cellular := raylib.GenImageCellular(screenWidth, screenHeight, 32) + verticalGradient := rl.GenImageGradientV(screenWidth, screenHeight, rl.Red, rl.Blue) + horizontalGradient := rl.GenImageGradientH(screenWidth, screenHeight, rl.Red, rl.Blue) + radialGradient := rl.GenImageGradientRadial(screenWidth, screenHeight, 0, rl.White, rl.Black) + checked := rl.GenImageChecked(screenWidth, screenHeight, 32, 32, rl.Red, rl.Blue) + whiteNoise := rl.GenImageWhiteNoise(screenWidth, screenHeight, 0.5) + perlinNoise := rl.GenImagePerlinNoise(screenWidth, screenHeight, 50, 50, 4.0) + cellular := rl.GenImageCellular(screenWidth, screenHeight, 32) - textures := make([]raylib.Texture2D, numTextures) - textures[0] = raylib.LoadTextureFromImage(verticalGradient) - textures[1] = raylib.LoadTextureFromImage(horizontalGradient) - textures[2] = raylib.LoadTextureFromImage(radialGradient) - textures[3] = raylib.LoadTextureFromImage(checked) - textures[4] = raylib.LoadTextureFromImage(whiteNoise) - textures[5] = raylib.LoadTextureFromImage(perlinNoise) - textures[6] = raylib.LoadTextureFromImage(cellular) + textures := make([]rl.Texture2D, numTextures) + textures[0] = rl.LoadTextureFromImage(verticalGradient) + textures[1] = rl.LoadTextureFromImage(horizontalGradient) + textures[2] = rl.LoadTextureFromImage(radialGradient) + textures[3] = rl.LoadTextureFromImage(checked) + textures[4] = rl.LoadTextureFromImage(whiteNoise) + textures[5] = rl.LoadTextureFromImage(perlinNoise) + textures[6] = rl.LoadTextureFromImage(cellular) // Unload image data (CPU RAM) - raylib.UnloadImage(verticalGradient) - raylib.UnloadImage(horizontalGradient) - raylib.UnloadImage(radialGradient) - raylib.UnloadImage(checked) - raylib.UnloadImage(whiteNoise) - raylib.UnloadImage(perlinNoise) - raylib.UnloadImage(cellular) + rl.UnloadImage(verticalGradient) + rl.UnloadImage(horizontalGradient) + rl.UnloadImage(radialGradient) + rl.UnloadImage(checked) + rl.UnloadImage(whiteNoise) + rl.UnloadImage(perlinNoise) + rl.UnloadImage(cellular) currentTexture := 0 - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + for !rl.WindowShouldClose() { + if rl.IsMouseButtonPressed(rl.MouseLeftButton) { currentTexture = (currentTexture + 1) % numTextures // Cycle between the textures } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawTexture(textures[currentTexture], 0, 0, raylib.White) + rl.DrawTexture(textures[currentTexture], 0, 0, rl.White) - raylib.DrawRectangle(30, 400, 325, 30, raylib.Fade(raylib.SkyBlue, 0.5)) - raylib.DrawRectangleLines(30, 400, 325, 30, raylib.Fade(raylib.White, 0.5)) - raylib.DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL TEXTURES", 40, 410, 10, raylib.White) + rl.DrawRectangle(30, 400, 325, 30, rl.Fade(rl.SkyBlue, 0.5)) + rl.DrawRectangleLines(30, 400, 325, 30, rl.Fade(rl.White, 0.5)) + rl.DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL TEXTURES", 40, 410, 10, rl.White) switch currentTexture { case 0: - raylib.DrawText("VERTICAL GRADIENT", 560, 10, 20, raylib.RayWhite) + rl.DrawText("VERTICAL GRADIENT", 560, 10, 20, rl.RayWhite) break case 1: - raylib.DrawText("HORIZONTAL GRADIENT", 540, 10, 20, raylib.RayWhite) + rl.DrawText("HORIZONTAL GRADIENT", 540, 10, 20, rl.RayWhite) break case 2: - raylib.DrawText("RADIAL GRADIENT", 580, 10, 20, raylib.LightGray) + rl.DrawText("RADIAL GRADIENT", 580, 10, 20, rl.LightGray) break case 3: - raylib.DrawText("CHECKED", 680, 10, 20, raylib.RayWhite) + rl.DrawText("CHECKED", 680, 10, 20, rl.RayWhite) break case 4: - raylib.DrawText("WHITE NOISE", 640, 10, 20, raylib.Red) + rl.DrawText("WHITE NOISE", 640, 10, 20, rl.Red) break case 5: - raylib.DrawText("PERLIN NOISE", 630, 10, 20, raylib.RayWhite) + rl.DrawText("PERLIN NOISE", 630, 10, 20, rl.RayWhite) break case 6: - raylib.DrawText("CELLULAR", 670, 10, 20, raylib.RayWhite) + rl.DrawText("CELLULAR", 670, 10, 20, rl.RayWhite) break default: break } - raylib.EndDrawing() + rl.EndDrawing() } for _, t := range textures { - raylib.UnloadTexture(t) + rl.UnloadTexture(t) } - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/image_image/main.go b/examples/textures/image_image/main.go index 82d3e5a..24472eb 100644 --- a/examples/textures/image_image/main.go +++ b/examples/textures/image_image/main.go @@ -11,57 +11,57 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture from image.Image") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture from image.Image") r, err := os.Open("raylib_logo.png") if err != nil { - raylib.TraceLog(raylib.LogError, err.Error()) + rl.TraceLog(rl.LogError, err.Error()) } defer r.Close() img, err := png.Decode(r) if err != nil { - raylib.TraceLog(raylib.LogError, err.Error()) + rl.TraceLog(rl.LogError, err.Error()) } - // Create raylib.Image from Go image.Image and create texture - im := raylib.NewImageFromImage(img) - texture := raylib.LoadTextureFromImage(im) + // Create rl.Image from Go image.Image and create texture + im := rl.NewImageFromImage(img) + texture := rl.LoadTextureFromImage(im) // Unload CPU (RAM) image data - raylib.UnloadImage(im) + rl.UnloadImage(im) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsKeyPressed(raylib.KeyS) { - rimg := raylib.GetTextureData(texture) + for !rl.WindowShouldClose() { + if rl.IsKeyPressed(rl.KeyS) { + rimg := rl.GetTextureData(texture) f, err := os.Create("image_saved.png") if err != nil { - raylib.TraceLog(raylib.LogError, err.Error()) + rl.TraceLog(rl.LogError, err.Error()) } err = png.Encode(f, rimg.ToImage()) if err != nil { - raylib.TraceLog(raylib.LogError, err.Error()) + rl.TraceLog(rl.LogError, err.Error()) } f.Close() } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("PRESS S TO SAVE IMAGE FROM TEXTURE", 20, 20, 12, raylib.LightGray) - raylib.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, raylib.White) - raylib.DrawText("this IS a texture loaded from an image.Image!", 285, 370, 10, raylib.Gray) + rl.DrawText("PRESS S TO SAVE IMAGE FROM TEXTURE", 20, 20, 12, rl.LightGray) + rl.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, rl.White) + rl.DrawText("this IS a texture loaded from an image.Image!", 285, 370, 10, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) + rl.UnloadTexture(texture) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/image_loading/main.go b/examples/textures/image_loading/main.go index bbaba11..9aeb41e 100644 --- a/examples/textures/image_loading/main.go +++ b/examples/textures/image_loading/main.go @@ -8,30 +8,30 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - image loading") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - image loading") // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - image := raylib.LoadImage("raylib_logo.png") // Loaded in CPU memory (RAM) - texture := raylib.LoadTextureFromImage(image) // Image converted to texture, GPU memory (VRAM) + image := rl.LoadImage("raylib_logo.png") // Loaded in CPU memory (RAM) + texture := rl.LoadTextureFromImage(image) // Image converted to texture, GPU memory (VRAM) - raylib.UnloadImage(image) // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM + rl.UnloadImage(image) // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, raylib.White) + rl.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, rl.White) - raylib.DrawText("this IS a texture loaded from an image!", 300, 370, 10, raylib.Gray) + rl.DrawText("this IS a texture loaded from an image!", 300, 370, 10, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) + rl.UnloadTexture(texture) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/image_processing/main.go b/examples/textures/image_processing/main.go index b9d532b..5c449a4 100644 --- a/examples/textures/image_processing/main.go +++ b/examples/textures/image_processing/main.go @@ -33,31 +33,31 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - image processing") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - image processing") - image := raylib.LoadImage("parrots.png") // Loaded in CPU memory (RAM) - raylib.ImageFormat(image, raylib.UncompressedR8g8b8a8) // Format image to RGBA 32bit (required for texture update) - texture := raylib.LoadTextureFromImage(image) // Image converted to texture, GPU memory (VRAM) + image := rl.LoadImage("parrots.png") // Loaded in CPU memory (RAM) + rl.ImageFormat(image, rl.UncompressedR8g8b8a8) // Format image to RGBA 32bit (required for texture update) + texture := rl.LoadTextureFromImage(image) // Image converted to texture, GPU memory (VRAM) currentProcess := None textureReload := false - selectRecs := make([]raylib.Rectangle, numProcesses) + selectRecs := make([]rl.Rectangle, numProcesses) for i := 0; i < numProcesses; i++ { - selectRecs[i] = raylib.NewRectangle(40, 50+32*float32(i), 150, 30) + selectRecs[i] = rl.NewRectangle(40, 50+32*float32(i), 150, 30) } - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsKeyPressed(raylib.KeyDown) { + for !rl.WindowShouldClose() { + if rl.IsKeyPressed(rl.KeyDown) { currentProcess++ if currentProcess > 7 { currentProcess = 0 } textureReload = true - } else if raylib.IsKeyPressed(raylib.KeyUp) { + } else if rl.IsKeyPressed(rl.KeyUp) { currentProcess-- if currentProcess < 0 { currentProcess = 7 @@ -66,70 +66,70 @@ func main() { } if textureReload { - raylib.UnloadImage(image) // Unload current image data - image = raylib.LoadImage("parrots.png") // Re-load image data + rl.UnloadImage(image) // Unload current image data + image = rl.LoadImage("parrots.png") // Re-load image data // NOTE: Image processing is a costly CPU process to be done every frame, // If image processing is required in a frame-basis, it should be done // with a texture and by shaders switch currentProcess { case ColorGrayscale: - raylib.ImageColorGrayscale(image) + rl.ImageColorGrayscale(image) break case ColorTint: - raylib.ImageColorTint(image, raylib.Green) + rl.ImageColorTint(image, rl.Green) break case ColorInvert: - raylib.ImageColorInvert(image) + rl.ImageColorInvert(image) break case ColorContrast: - raylib.ImageColorContrast(image, -40) + rl.ImageColorContrast(image, -40) break case ColorBrightness: - raylib.ImageColorBrightness(image, -80) + rl.ImageColorBrightness(image, -80) break case FlipVertical: - raylib.ImageFlipVertical(image) + rl.ImageFlipVertical(image) break case FlipHorizontal: - raylib.ImageFlipHorizontal(image) + rl.ImageFlipHorizontal(image) break default: break } - pixels := raylib.GetImageData(image) // Get pixel data from image (RGBA 32bit) - raylib.UpdateTexture(texture, pixels) // Update texture with new image data + pixels := rl.GetImageData(image) // Get pixel data from image (RGBA 32bit) + rl.UpdateTexture(texture, pixels) // Update texture with new image data textureReload = false } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawText("IMAGE PROCESSING:", 40, 30, 10, raylib.DarkGray) + rl.DrawText("IMAGE PROCESSING:", 40, 30, 10, rl.DarkGray) // Draw rectangles for i := 0; i < numProcesses; i++ { if i == currentProcess { - raylib.DrawRectangleRec(selectRecs[i], raylib.SkyBlue) - raylib.DrawRectangleLines(int32(selectRecs[i].X), int32(selectRecs[i].Y), int32(selectRecs[i].Width), int32(selectRecs[i].Height), raylib.Blue) - raylib.DrawText(processText[i], int32(selectRecs[i].X+selectRecs[i].Width/2)-raylib.MeasureText(processText[i], 10)/2, int32(selectRecs[i].Y)+11, 10, raylib.DarkBlue) + rl.DrawRectangleRec(selectRecs[i], rl.SkyBlue) + rl.DrawRectangleLines(int32(selectRecs[i].X), int32(selectRecs[i].Y), int32(selectRecs[i].Width), int32(selectRecs[i].Height), rl.Blue) + rl.DrawText(processText[i], int32(selectRecs[i].X+selectRecs[i].Width/2)-rl.MeasureText(processText[i], 10)/2, int32(selectRecs[i].Y)+11, 10, rl.DarkBlue) } else { - raylib.DrawRectangleRec(selectRecs[i], raylib.LightGray) - raylib.DrawRectangleLines(int32(selectRecs[i].X), int32(selectRecs[i].Y), int32(selectRecs[i].Width), int32(selectRecs[i].Height), raylib.Gray) - raylib.DrawText(processText[i], int32(selectRecs[i].X+selectRecs[i].Width/2)-raylib.MeasureText(processText[i], 10)/2, int32(selectRecs[i].Y)+11, 10, raylib.DarkGray) + rl.DrawRectangleRec(selectRecs[i], rl.LightGray) + rl.DrawRectangleLines(int32(selectRecs[i].X), int32(selectRecs[i].Y), int32(selectRecs[i].Width), int32(selectRecs[i].Height), rl.Gray) + rl.DrawText(processText[i], int32(selectRecs[i].X+selectRecs[i].Width/2)-rl.MeasureText(processText[i], 10)/2, int32(selectRecs[i].Y)+11, 10, rl.DarkGray) } } - raylib.DrawTexture(texture, screenWidth-texture.Width-60, screenHeight/2-texture.Height/2, raylib.White) - raylib.DrawRectangleLines(screenWidth-texture.Width-60, screenHeight/2-texture.Height/2, texture.Width, texture.Height, raylib.Black) + rl.DrawTexture(texture, screenWidth-texture.Width-60, screenHeight/2-texture.Height/2, rl.White) + rl.DrawRectangleLines(screenWidth-texture.Width-60, screenHeight/2-texture.Height/2, texture.Width, texture.Height, rl.Black) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) + rl.UnloadTexture(texture) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/image_text/main.go b/examples/textures/image_text/main.go index d5c1cab..9c092a6 100644 --- a/examples/textures/image_text/main.go +++ b/examples/textures/image_text/main.go @@ -8,55 +8,55 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - image text drawing") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - image text drawing") // TTF Font loading with custom generation parameters var fontChars int32 - font := raylib.LoadFontEx("fonts/KAISG.ttf", 64, 0, &fontChars) + font := rl.LoadFontEx("fonts/KAISG.ttf", 64, 0, &fontChars) - parrots := raylib.LoadImage("parrots.png") // Load image in CPU memory (RAM) + parrots := rl.LoadImage("parrots.png") // Load image in CPU memory (RAM) // Draw over image using custom font - raylib.ImageDrawTextEx(parrots, raylib.NewVector2(20, 20), font, "[Parrots font drawing]", float32(font.BaseSize), 0, raylib.White) + rl.ImageDrawTextEx(parrots, rl.NewVector2(20, 20), font, "[Parrots font drawing]", float32(font.BaseSize), 0, rl.White) - texture := raylib.LoadTextureFromImage(parrots) // Image converted to texture, uploaded to GPU memory (VRAM) + texture := rl.LoadTextureFromImage(parrots) // Image converted to texture, uploaded to GPU memory (VRAM) - raylib.UnloadImage(parrots) // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM + rl.UnloadImage(parrots) // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM - position := raylib.NewVector2(float32(screenWidth)/2-float32(texture.Width)/2, float32(screenHeight)/2-float32(texture.Height)/2-20) + position := rl.NewVector2(float32(screenWidth)/2-float32(texture.Width)/2, float32(screenHeight)/2-float32(texture.Height)/2-20) showFont := false - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - if raylib.IsKeyDown(raylib.KeySpace) { + for !rl.WindowShouldClose() { + if rl.IsKeyDown(rl.KeySpace) { showFont = true } else { showFont = false } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) if !showFont { // Draw texture with text already drawn inside - raylib.DrawTextureV(texture, position, raylib.White) + rl.DrawTextureV(texture, position, rl.White) // Draw text directly using sprite font - raylib.DrawTextEx(font, "[Parrots font drawing]", raylib.NewVector2(position.X+20, position.Y+20+280), float32(font.BaseSize), 0, raylib.White) + rl.DrawTextEx(font, "[Parrots font drawing]", rl.NewVector2(position.X+20, position.Y+20+280), float32(font.BaseSize), 0, rl.White) } else { - raylib.DrawTexture(font.Texture, screenWidth/2-font.Texture.Width/2, 50, raylib.Black) + rl.DrawTexture(font.Texture, screenWidth/2-font.Texture.Width/2, 50, rl.Black) } - raylib.DrawText("PRESS SPACE to SEE USED SPRITEFONT ", 290, 420, 10, raylib.DarkGray) + rl.DrawText("PRESS SPACE to SEE USED SPRITEFONT ", 290, 420, 10, rl.DarkGray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) - raylib.UnloadFont(font) + rl.UnloadTexture(texture) + rl.UnloadFont(font) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/logo_raylib/main.go b/examples/textures/logo_raylib/main.go index 473e171..8777074 100644 --- a/examples/textures/logo_raylib/main.go +++ b/examples/textures/logo_raylib/main.go @@ -8,24 +8,24 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing") // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - texture := raylib.LoadTexture("raylib_logo.png") + texture := rl.LoadTexture("raylib_logo.png") - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) - raylib.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, raylib.White) - raylib.DrawText("this IS a texture!", 360, 370, 10, raylib.Gray) + rl.ClearBackground(rl.RayWhite) + rl.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, rl.White) + rl.DrawText("this IS a texture!", 360, 370, 10, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) + rl.UnloadTexture(texture) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/particles_blending/main.go b/examples/textures/particles_blending/main.go index 3cd7cd3..414d700 100644 --- a/examples/textures/particles_blending/main.go +++ b/examples/textures/particles_blending/main.go @@ -9,8 +9,8 @@ const ( ) type particle struct { - Position raylib.Vector2 - Color raylib.Color + Position rl.Vector2 + Color rl.Color Alpha float32 Size float32 Rotation float32 @@ -21,31 +21,31 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - //raylib.SetConfigFlags(raylib.FlagVsyncHint) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - particles blending") + //rl.SetConfigFlags(rl.FlagVsyncHint) + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - particles blending") // Particles pool, reuse them! mouseTail := make([]particle, maxParticles) // Initialize particles for i := 0; i < maxParticles; i++ { - mouseTail[i].Position = raylib.NewVector2(0, 0) - mouseTail[i].Color = raylib.NewColor(byte(raylib.GetRandomValue(0, 255)), byte(raylib.GetRandomValue(0, 255)), byte(raylib.GetRandomValue(0, 255)), 255) + mouseTail[i].Position = rl.NewVector2(0, 0) + mouseTail[i].Color = rl.NewColor(byte(rl.GetRandomValue(0, 255)), byte(rl.GetRandomValue(0, 255)), byte(rl.GetRandomValue(0, 255)), 255) mouseTail[i].Alpha = 1.0 - mouseTail[i].Size = float32(raylib.GetRandomValue(1, 30)) / 20.0 - mouseTail[i].Rotation = float32(raylib.GetRandomValue(0, 360)) + mouseTail[i].Size = float32(rl.GetRandomValue(1, 30)) / 20.0 + mouseTail[i].Rotation = float32(rl.GetRandomValue(0, 360)) mouseTail[i].Active = false } gravity := float32(3.0) - smoke := raylib.LoadTexture("smoke.png") + smoke := rl.LoadTexture("smoke.png") - blending := raylib.BlendAlpha + blending := rl.BlendAlpha - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update // Activate one particle every frame and Update active particles @@ -56,7 +56,7 @@ func main() { if !mouseTail[i].Active { mouseTail[i].Active = true mouseTail[i].Alpha = 1.0 - mouseTail[i].Position = raylib.GetMousePosition() + mouseTail[i].Position = rl.GetMousePosition() i = maxParticles } } @@ -74,50 +74,50 @@ func main() { } } - if raylib.IsKeyPressed(raylib.KeySpace) { - if blending == raylib.BlendAlpha { - blending = raylib.BlendAdditive + if rl.IsKeyPressed(rl.KeySpace) { + if blending == rl.BlendAlpha { + blending = rl.BlendAdditive } else { - blending = raylib.BlendAlpha + blending = rl.BlendAlpha } } // Draw - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.DarkGray) + rl.ClearBackground(rl.DarkGray) - raylib.BeginBlendMode(blending) + rl.BeginBlendMode(blending) // Draw active particles for i := 0; i < maxParticles; i++ { if mouseTail[i].Active { - raylib.DrawTexturePro( + rl.DrawTexturePro( smoke, - raylib.NewRectangle(0, 0, float32(smoke.Width), float32(smoke.Height)), - raylib.NewRectangle(mouseTail[i].Position.X, mouseTail[i].Position.Y, float32(smoke.Width)*mouseTail[i].Size, float32(smoke.Height)*mouseTail[i].Size), - raylib.NewVector2(float32(smoke.Width)*mouseTail[i].Size/2, float32(smoke.Height)*mouseTail[i].Size/2), + rl.NewRectangle(0, 0, float32(smoke.Width), float32(smoke.Height)), + rl.NewRectangle(mouseTail[i].Position.X, mouseTail[i].Position.Y, float32(smoke.Width)*mouseTail[i].Size, float32(smoke.Height)*mouseTail[i].Size), + rl.NewVector2(float32(smoke.Width)*mouseTail[i].Size/2, float32(smoke.Height)*mouseTail[i].Size/2), mouseTail[i].Rotation, - raylib.Fade(mouseTail[i].Color, mouseTail[i].Alpha), + rl.Fade(mouseTail[i].Color, mouseTail[i].Alpha), ) } } - raylib.EndBlendMode() + rl.EndBlendMode() - raylib.DrawText("PRESS SPACE to CHANGE BLENDING MODE", 180, 20, 20, raylib.Black) + rl.DrawText("PRESS SPACE to CHANGE BLENDING MODE", 180, 20, 20, rl.Black) - if blending == raylib.BlendAlpha { - raylib.DrawText("ALPHA BLENDING", 290, screenHeight-40, 20, raylib.Black) + if blending == rl.BlendAlpha { + rl.DrawText("ALPHA BLENDING", 290, screenHeight-40, 20, rl.Black) } else { - raylib.DrawText("ADDITIVE BLENDING", 280, screenHeight-40, 20, raylib.RayWhite) + rl.DrawText("ADDITIVE BLENDING", 280, screenHeight-40, 20, rl.RayWhite) } - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(smoke) + rl.UnloadTexture(smoke) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/raw_data/main.go b/examples/textures/raw_data/main.go index c0f5203..6441f96 100644 --- a/examples/textures/raw_data/main.go +++ b/examples/textures/raw_data/main.go @@ -8,56 +8,56 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture from raw data") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture from raw data") // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) // Load RAW image data (384x512, 32bit RGBA, no file header) - fudesumiRaw := raylib.LoadImageRaw("texture_formats/fudesumi.raw", 384, 512, raylib.UncompressedR8g8b8a8, 0) - fudesumi := raylib.LoadTextureFromImage(fudesumiRaw) // Upload CPU (RAM) image to GPU (VRAM) - raylib.UnloadImage(fudesumiRaw) // Unload CPU (RAM) image data + fudesumiRaw := rl.LoadImageRaw("texture_formats/fudesumi.raw", 384, 512, rl.UncompressedR8g8b8a8, 0) + fudesumi := rl.LoadTextureFromImage(fudesumiRaw) // Upload CPU (RAM) image to GPU (VRAM) + rl.UnloadImage(fudesumiRaw) // Unload CPU (RAM) image data // Generate a checked texture by code (1024x1024 pixels) width := 1024 height := 1024 // Dynamic memory allocation to store pixels data (Color type) - pixels := make([]raylib.Color, width*height) + pixels := make([]rl.Color, width*height) for y := 0; y < height; y++ { for x := 0; x < width; x++ { if ((x/32+y/32)/1)%2 == 0 { - pixels[y*height+x] = raylib.Orange + pixels[y*height+x] = rl.Orange } else { - pixels[y*height+x] = raylib.Gold + pixels[y*height+x] = rl.Gold } } } // Load pixels data into an image structure and create texture - checkedIm := raylib.LoadImageEx(pixels, int32(width), int32(height)) - checked := raylib.LoadTextureFromImage(checkedIm) - raylib.UnloadImage(checkedIm) // Unload CPU (RAM) image data + checkedIm := rl.LoadImageEx(pixels, int32(width), int32(height)) + checked := rl.LoadTextureFromImage(checkedIm) + rl.UnloadImage(checkedIm) // Unload CPU (RAM) image data - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawTexture(checked, screenWidth/2-checked.Width/2, screenHeight/2-checked.Height/2, raylib.Fade(raylib.White, 0.5)) - raylib.DrawTexture(fudesumi, 430, -30, raylib.White) + rl.DrawTexture(checked, screenWidth/2-checked.Width/2, screenHeight/2-checked.Height/2, rl.Fade(rl.White, 0.5)) + rl.DrawTexture(fudesumi, 430, -30, rl.White) - raylib.DrawText("CHECKED TEXTURE ", 84, 100, 30, raylib.Brown) - raylib.DrawText("GENERATED by CODE", 72, 164, 30, raylib.Brown) - raylib.DrawText("and RAW IMAGE LOADING", 46, 226, 30, raylib.Brown) + rl.DrawText("CHECKED TEXTURE ", 84, 100, 30, rl.Brown) + rl.DrawText("GENERATED by CODE", 72, 164, 30, rl.Brown) + rl.DrawText("and RAW IMAGE LOADING", 46, 226, 30, rl.Brown) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(fudesumi) // Texture unloading - raylib.UnloadTexture(checked) // Texture unloading + rl.UnloadTexture(fudesumi) // Texture unloading + rl.UnloadTexture(checked) // Texture unloading - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/rectangle/main.go b/examples/textures/rectangle/main.go index 158f178..795989b 100644 --- a/examples/textures/rectangle/main.go +++ b/examples/textures/rectangle/main.go @@ -15,21 +15,21 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing") // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - scarfy := raylib.LoadTexture("scarfy.png") // Texture loading + scarfy := rl.LoadTexture("scarfy.png") // Texture loading - position := raylib.NewVector2(350.0, 280.0) - frameRec := raylib.NewRectangle(0, 0, float32(scarfy.Width/6), float32(scarfy.Height)) + position := rl.NewVector2(350.0, 280.0) + frameRec := rl.NewRectangle(0, 0, float32(scarfy.Width/6), float32(scarfy.Height)) currentFrame := float32(0) framesCounter := 0 framesSpeed := 8 // Number of spritesheet frames shown by second - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { framesCounter++ if framesCounter >= (60 / framesSpeed) { @@ -43,9 +43,9 @@ func main() { frameRec.X = currentFrame * float32(scarfy.Width) / 6 } - if raylib.IsKeyPressed(raylib.KeyRight) { + if rl.IsKeyPressed(rl.KeyRight) { framesSpeed++ - } else if raylib.IsKeyPressed(raylib.KeyLeft) { + } else if rl.IsKeyPressed(rl.KeyLeft) { framesSpeed-- } @@ -55,33 +55,33 @@ func main() { framesSpeed = minFrameSpeed } - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawTexture(scarfy, 15, 40, raylib.White) - raylib.DrawRectangleLines(15, 40, scarfy.Width, scarfy.Height, raylib.Lime) - raylib.DrawRectangleLines(15+int32(frameRec.X), 40+int32(frameRec.Y), int32(frameRec.Width), int32(frameRec.Height), raylib.Red) + rl.DrawTexture(scarfy, 15, 40, rl.White) + rl.DrawRectangleLines(15, 40, scarfy.Width, scarfy.Height, rl.Lime) + rl.DrawRectangleLines(15+int32(frameRec.X), 40+int32(frameRec.Y), int32(frameRec.Width), int32(frameRec.Height), rl.Red) - raylib.DrawText("FRAME SPEED: ", 165, 210, 10, raylib.DarkGray) - raylib.DrawText(fmt.Sprintf("%02d FPS", framesSpeed), 575, 210, 10, raylib.DarkGray) - raylib.DrawText("PRESS RIGHT/LEFT KEYS to CHANGE SPEED!", 290, 240, 10, raylib.DarkGray) + rl.DrawText("FRAME SPEED: ", 165, 210, 10, rl.DarkGray) + rl.DrawText(fmt.Sprintf("%02d FPS", framesSpeed), 575, 210, 10, rl.DarkGray) + rl.DrawText("PRESS RIGHT/LEFT KEYS to CHANGE SPEED!", 290, 240, 10, rl.DarkGray) for i := 0; i < maxFrameSpeed; i++ { if i < framesSpeed { - raylib.DrawRectangle(int32(250+21*i), 205, 20, 20, raylib.Red) + rl.DrawRectangle(int32(250+21*i), 205, 20, 20, rl.Red) } - raylib.DrawRectangleLines(int32(250+21*i), 205, 20, 20, raylib.Maroon) + rl.DrawRectangleLines(int32(250+21*i), 205, 20, 20, rl.Maroon) } - raylib.DrawTextureRec(scarfy, frameRec, position, raylib.White) // Draw part of the texture + rl.DrawTextureRec(scarfy, frameRec, position, rl.White) // Draw part of the texture - raylib.DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth-200, screenHeight-20, 10, raylib.Gray) + rl.DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth-200, screenHeight-20, 10, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(scarfy) + rl.UnloadTexture(scarfy) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/srcrec_dstrec/main.go b/examples/textures/srcrec_dstrec/main.go index 92ab73b..80768a2 100644 --- a/examples/textures/srcrec_dstrec/main.go +++ b/examples/textures/srcrec_dstrec/main.go @@ -8,52 +8,52 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] examples - texture source and destination rectangles") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] examples - texture source and destination rectangles") // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - scarfy := raylib.LoadTexture("scarfy.png") // Texture loading + scarfy := rl.LoadTexture("scarfy.png") // Texture loading frameWidth := float32(scarfy.Width) / 7 frameHeight := float32(scarfy.Height) // NOTE: Source rectangle (part of the texture to use for drawing) - sourceRec := raylib.NewRectangle(0, 0, frameWidth, frameHeight) + sourceRec := rl.NewRectangle(0, 0, frameWidth, frameHeight) // NOTE: Destination rectangle (screen rectangle where drawing part of texture) - destRec := raylib.NewRectangle(float32(screenWidth)/2, float32(screenHeight)/2, frameWidth*2, frameHeight*2) + destRec := rl.NewRectangle(float32(screenWidth)/2, float32(screenHeight)/2, frameWidth*2, frameHeight*2) // NOTE: Origin of the texture (rotation/scale point), it's relative to destination rectangle size - origin := raylib.NewVector2(float32(frameWidth), float32(frameHeight)) + origin := rl.NewVector2(float32(frameWidth), float32(frameHeight)) rotation := float32(0) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { + for !rl.WindowShouldClose() { // Update rotation++ // Draw - raylib.BeginDrawing() + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) // NOTE: Using DrawTexturePro() we can easily rotate and scale the part of the texture we draw // sourceRec defines the part of the texture we use for drawing // destRec defines the rectangle where our texture part will fit (scaling it to fit) // origin defines the point of the texture used as reference for rotation and scaling // rotation defines the texture rotation (using origin as rotation point) - raylib.DrawTexturePro(scarfy, sourceRec, destRec, origin, rotation, raylib.White) + rl.DrawTexturePro(scarfy, sourceRec, destRec, origin, rotation, rl.White) - raylib.DrawLine(int32(destRec.X), 0, int32(destRec.X), screenHeight, raylib.Gray) - raylib.DrawLine(0, int32(destRec.Y), screenWidth, int32(destRec.Y), raylib.Gray) + rl.DrawLine(int32(destRec.X), 0, int32(destRec.X), screenHeight, rl.Gray) + rl.DrawLine(0, int32(destRec.Y), screenWidth, int32(destRec.Y), rl.Gray) - raylib.DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth-200, screenHeight-20, 10, raylib.Gray) + rl.DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth-200, screenHeight-20, 10, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(scarfy) + rl.UnloadTexture(scarfy) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/examples/textures/to_image/main.go b/examples/textures/to_image/main.go index 56ef9da..8a88453 100644 --- a/examples/textures/to_image/main.go +++ b/examples/textures/to_image/main.go @@ -8,32 +8,32 @@ func main() { screenWidth := int32(800) screenHeight := int32(450) - raylib.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture to image") + rl.InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture to image") - image := raylib.LoadImage("raylib_logo.png") // Load image data into CPU memory (RAM) - texture := raylib.LoadTextureFromImage(image) // Image converted to texture, GPU memory (RAM -> VRAM) - raylib.UnloadImage(image) // Unload image data from CPU memory (RAM) + image := rl.LoadImage("raylib_logo.png") // Load image data into CPU memory (RAM) + texture := rl.LoadTextureFromImage(image) // Image converted to texture, GPU memory (RAM -> VRAM) + rl.UnloadImage(image) // Unload image data from CPU memory (RAM) - image = raylib.GetTextureData(texture) // Retrieve image data from GPU memory (VRAM -> RAM) - raylib.UnloadTexture(texture) // Unload texture from GPU memory (VRAM) + image = rl.GetTextureData(texture) // Retrieve image data from GPU memory (VRAM -> RAM) + rl.UnloadTexture(texture) // Unload texture from GPU memory (VRAM) - texture = raylib.LoadTextureFromImage(image) // Recreate texture from retrieved image data (RAM -> VRAM) - raylib.UnloadImage(image) // Unload retrieved image data from CPU memory (RAM) + texture = rl.LoadTextureFromImage(image) // Recreate texture from retrieved image data (RAM -> VRAM) + rl.UnloadImage(image) // Unload retrieved image data from CPU memory (RAM) - raylib.SetTargetFPS(60) + rl.SetTargetFPS(60) - for !raylib.WindowShouldClose() { - raylib.BeginDrawing() + for !rl.WindowShouldClose() { + rl.BeginDrawing() - raylib.ClearBackground(raylib.RayWhite) + rl.ClearBackground(rl.RayWhite) - raylib.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, raylib.White) - raylib.DrawText("this IS a texture loaded from an image!", 300, 370, 10, raylib.Gray) + rl.DrawTexture(texture, screenWidth/2-texture.Width/2, screenHeight/2-texture.Height/2, rl.White) + rl.DrawText("this IS a texture loaded from an image!", 300, 370, 10, rl.Gray) - raylib.EndDrawing() + rl.EndDrawing() } - raylib.UnloadTexture(texture) + rl.UnloadTexture(texture) - raylib.CloseWindow() + rl.CloseWindow() } diff --git a/physics/physics.go b/physics/physics.go index 2d80f7a..0d09a22 100644 --- a/physics/physics.go +++ b/physics/physics.go @@ -26,11 +26,11 @@ type Polygon struct { // Current used vertex and normals count VertexCount int // Polygon vertex positions vectors - Vertices [maxVertices]raylib.Vector2 + Vertices [maxVertices]rl.Vector2 // Polygon vertex normals vectors - Normals [maxVertices]raylib.Vector2 + Normals [maxVertices]rl.Vector2 // Vertices transform matrix 2x2 - Transform raylib.Mat2 + Transform rl.Mat2 } // Shape type @@ -50,11 +50,11 @@ type Body struct { // Enabled dynamics state (collisions are calculated anyway) Enabled bool // Physics body shape pivot - Position raylib.Vector2 + Position rl.Vector2 // Current linear velocity applied to position - Velocity raylib.Vector2 + Velocity rl.Vector2 // Current linear force (reset to 0 every step) - Force raylib.Vector2 + Force rl.Vector2 // Current angular velocity applied to orient AngularVelocity float32 // Current angular force (reset to 0 every step) @@ -94,9 +94,9 @@ type manifold struct { // Depth of penetration from collision Penetration float32 // Normal direction vector from 'a' to 'b' - Normal raylib.Vector2 + Normal rl.Vector2 // Points of contact during collision - Contacts [2]raylib.Vector2 + Contacts [2]rl.Vector2 // Current collision number of contacts ContactsCount int // Mixed restitution during collision @@ -132,20 +132,26 @@ var ( manifolds []*manifold // Physics world gravity force - gravityForce raylib.Vector2 + gravityForce rl.Vector2 - // Delta time used for physics steps + // Delta time used for physics steps, in milliseconds deltaTime float32 ) // Init - initializes physics values func Init() { - gravityForce = raylib.NewVector2(0, 9.81/1000) + deltaTime = 1.0 / 60.0 / 10.0 * 1000 + gravityForce = rl.NewVector2(0, 9.81) bodies = make([]*Body, 0, maxBodies) manifolds = make([]*manifold, 0, maxManifolds) } +// Sets physics fixed time step in milliseconds. 1.666666 by default +func SetPhysicsTimeStep(delta float32) { + deltaTime = delta +} + // SetGravity - Sets physics global gravity force func SetGravity(x, y float32) { gravityForce.X = x @@ -153,19 +159,19 @@ func SetGravity(x, y float32) { } // NewBodyCircle - Creates a new circle physics body with generic parameters -func NewBodyCircle(pos raylib.Vector2, radius, density float32) *Body { +func NewBodyCircle(pos rl.Vector2, radius, density float32) *Body { return NewBodyPolygon(pos, radius, circleVertices, density) } // NewBodyRectangle - Creates a new rectangle physics body with generic parameters -func NewBodyRectangle(pos raylib.Vector2, width, height, density float32) *Body { +func NewBodyRectangle(pos rl.Vector2, width, height, density float32) *Body { newBody := &Body{} // Initialize new body with generic values newBody.Enabled = true newBody.Position = pos - newBody.Velocity = raylib.Vector2{} - newBody.Force = raylib.Vector2{} + newBody.Velocity = rl.Vector2{} + newBody.Force = rl.Vector2{} newBody.AngularVelocity = 0 newBody.Torque = 0 newBody.Orient = 0 @@ -173,10 +179,10 @@ func NewBodyRectangle(pos raylib.Vector2, width, height, density float32) *Body newBody.Shape = Shape{} newBody.Shape.Type = PolygonShape newBody.Shape.Body = newBody - newBody.Shape.VertexData = newRectanglePolygon(pos, raylib.NewVector2(width, height)) + newBody.Shape.VertexData = newRectanglePolygon(pos, rl.NewVector2(width, height)) // Calculate centroid and moment of inertia - center := raylib.Vector2{} + center := rl.Vector2{} area := float32(0.0) inertia := float32(0.0) k := float32(1.0) / 3.0 @@ -238,14 +244,14 @@ func NewBodyRectangle(pos raylib.Vector2, width, height, density float32) *Body } // NewBodyPolygon - Creates a new polygon physics body with generic parameters -func NewBodyPolygon(pos raylib.Vector2, radius float32, sides int, density float32) *Body { +func NewBodyPolygon(pos rl.Vector2, radius float32, sides int, density float32) *Body { newBody := &Body{} // Initialize new body with generic values newBody.Enabled = true newBody.Position = pos - newBody.Velocity = raylib.Vector2{} - newBody.Force = raylib.Vector2{} + newBody.Velocity = rl.Vector2{} + newBody.Force = rl.Vector2{} newBody.AngularVelocity = 0 newBody.Torque = 0 newBody.Orient = 0 @@ -256,7 +262,7 @@ func NewBodyPolygon(pos raylib.Vector2, radius float32, sides int, density float newBody.Shape.VertexData = newRandomPolygon(radius, sides) // Calculate centroid and moment of inertia - center := raylib.Vector2{} + center := rl.Vector2{} area := float32(0.0) inertia := float32(0.0) alpha := float32(1.0) / 3.0 @@ -334,7 +340,7 @@ func GetBody(index int) *Body { if index < len(bodies) { body = bodies[index] } else { - raylib.TraceLog(raylib.LogDebug, "[PHYSAC] physics body index is out of bounds") + rl.TraceLog(rl.LogDebug, "[PHYSAC] physics body index is out of bounds") } return body @@ -347,7 +353,7 @@ func GetShapeType(index int) ShapeType { if index < len(bodies) { result = bodies[index].Shape.Type } else { - raylib.TraceLog(raylib.LogDebug, "[PHYSAC] physics body index is out of bounds") + rl.TraceLog(rl.LogDebug, "[PHYSAC] physics body index is out of bounds") } return result @@ -367,7 +373,7 @@ func GetShapeVerticesCount(index int) int { break } } else { - raylib.TraceLog(raylib.LogDebug, "[PHYSAC] physics body index is out of bounds") + rl.TraceLog(rl.LogDebug, "[PHYSAC] physics body index is out of bounds") } return result @@ -388,7 +394,7 @@ func DestroyBody(body *Body) bool { // Update - Physics steps calculations (dynamics, collisions and position corrections) func Update() { - deltaTime = raylib.GetFrameTime() * 1000 + deltaTime = rl.GetFrameTime() * 1000 // Clear previous generated collisions information for _, m := range manifolds { @@ -470,7 +476,7 @@ func Update() { // Clear physics bodies forces for _, b := range bodies { - b.Force = raylib.Vector2{} + b.Force = rl.Vector2{} b.Torque = 0 } } @@ -495,7 +501,7 @@ func Close() { } // AddForce - Adds a force to a physics body -func (b *Body) AddForce(force raylib.Vector2) { +func (b *Body) AddForce(force rl.Vector2) { b.Force = raymath.Vector2Add(b.Force, force) } @@ -505,7 +511,7 @@ func (b *Body) AddTorque(amount float32) { } // Shatter - Shatters a polygon shape physics body to little physics bodies with explosion force -func (b *Body) Shatter(position raylib.Vector2, force float32) { +func (b *Body) Shatter(position rl.Vector2, force float32) { if b.Shape.Type != PolygonShape { return } @@ -543,7 +549,7 @@ func (b *Body) Shatter(position raylib.Vector2, force float32) { count := vertexData.VertexCount bodyPos := b.Position - vertices := make([]raylib.Vector2, count) + vertices := make([]rl.Vector2, count) trans := vertexData.Transform for i := 0; i < count; i++ { vertices[i] = vertexData.Vertices[i] @@ -558,7 +564,7 @@ func (b *Body) Shatter(position raylib.Vector2, force float32) { nextIndex = i + 1 } - center := triangleBarycenter(vertices[i], vertices[nextIndex], raylib.NewVector2(0, 0)) + center := triangleBarycenter(vertices[i], vertices[nextIndex], rl.NewVector2(0, 0)) center = raymath.Vector2Add(bodyPos, center) offset := raymath.Vector2Subtract(center, bodyPos) @@ -589,7 +595,7 @@ func (b *Body) Shatter(position raylib.Vector2, force float32) { face := raymath.Vector2Subtract(newData.Vertices[nextVertex], newData.Vertices[j]) - newData.Normals[j] = raylib.NewVector2(face.Y, -face.X) + newData.Normals[j] = rl.NewVector2(face.Y, -face.X) normalize(&newData.Normals[j]) } @@ -597,7 +603,7 @@ func (b *Body) Shatter(position raylib.Vector2, force float32) { newBody.Shape.VertexData = newData // Calculate centroid and moment of inertia - center = raylib.NewVector2(0, 0) + center = rl.NewVector2(0, 0) area := float32(0.0) inertia := float32(0.0) k := float32(1.0) / 3.0 @@ -656,13 +662,13 @@ func (b *Body) Shatter(position raylib.Vector2, force float32) { } // GetShapeVertex - Returns transformed position of a body shape (body position + vertex transformed position) -func (b *Body) GetShapeVertex(vertex int) raylib.Vector2 { - position := raylib.Vector2{} +func (b *Body) GetShapeVertex(vertex int) rl.Vector2 { + position := rl.Vector2{} switch b.Shape.Type { case CircleShape: - position.X = b.Position.X + float32(math.Cos(360/float64(circleVertices)*float64(vertex)*raylib.Deg2rad))*b.Shape.Radius - position.Y = b.Position.Y + float32(math.Sin(360/float64(circleVertices)*float64(vertex)*raylib.Deg2rad))*b.Shape.Radius + position.X = b.Position.X + float32(math.Cos(360/float64(circleVertices)*float64(vertex)*rl.Deg2rad))*b.Shape.Radius + position.Y = b.Position.Y + float32(math.Sin(360/float64(circleVertices)*float64(vertex)*rl.Deg2rad))*b.Shape.Radius break case PolygonShape: position = raymath.Vector2Add(b.Position, raymath.Mat2MultiplyVector2(b.Shape.VertexData.Transform, b.Shape.VertexData.Vertices[vertex])) @@ -709,8 +715,8 @@ func (b *Body) integrateForces() { b.Velocity.Y += (b.Force.Y * b.InverseMass) * (deltaTime / 2) if b.UseGravity { - b.Velocity.X += gravityForce.X * (deltaTime / 2) - b.Velocity.Y += gravityForce.Y * (deltaTime / 2) + b.Velocity.X += gravityForce.X * (deltaTime / 1000 / 2) + b.Velocity.Y += gravityForce.Y * (deltaTime / 1000 / 2) } if !b.FreezeOrient { @@ -723,13 +729,13 @@ func newRandomPolygon(radius float32, sides int) Polygon { data := Polygon{} data.VertexCount = sides - orient := raylib.GetRandomValue(0, 360) - data.Transform = raymath.Mat2Radians(float32(orient) * raylib.Deg2rad) + orient := rl.GetRandomValue(0, 360) + data.Transform = raymath.Mat2Radians(float32(orient) * rl.Deg2rad) // Calculate polygon vertices positions for i := 0; i < data.VertexCount; i++ { - data.Vertices[i].X = float32(math.Cos(360/float64(sides)*float64(i)*raylib.Deg2rad)) * radius - data.Vertices[i].Y = float32(math.Sin(360/float64(sides)*float64(i)*raylib.Deg2rad)) * radius + data.Vertices[i].X = float32(math.Cos(360/float64(sides)*float64(i)*rl.Deg2rad)) * radius + data.Vertices[i].Y = float32(math.Sin(360/float64(sides)*float64(i)*rl.Deg2rad)) * radius } // Calculate polygon faces normals @@ -741,7 +747,7 @@ func newRandomPolygon(radius float32, sides int) Polygon { face := raymath.Vector2Subtract(data.Vertices[nextIndex], data.Vertices[i]) - data.Normals[i] = raylib.NewVector2(face.Y, -face.X) + data.Normals[i] = rl.NewVector2(face.Y, -face.X) normalize(&data.Normals[i]) } @@ -749,17 +755,17 @@ func newRandomPolygon(radius float32, sides int) Polygon { } // newRectanglePolygon - Creates a rectangle polygon shape based on a min and max positions -func newRectanglePolygon(pos, size raylib.Vector2) Polygon { +func newRectanglePolygon(pos, size rl.Vector2) Polygon { data := Polygon{} data.VertexCount = 4 data.Transform = raymath.Mat2Radians(0) // Calculate polygon vertices positions - data.Vertices[0] = raylib.NewVector2(pos.X+size.X/2, pos.Y-size.Y/2) - data.Vertices[1] = raylib.NewVector2(pos.X+size.X/2, pos.Y+size.Y/2) - data.Vertices[2] = raylib.NewVector2(pos.X-size.X/2, pos.Y+size.Y/2) - data.Vertices[3] = raylib.NewVector2(pos.X-size.X/2, pos.Y-size.Y/2) + data.Vertices[0] = rl.NewVector2(pos.X+size.X/2, pos.Y-size.Y/2) + data.Vertices[1] = rl.NewVector2(pos.X+size.X/2, pos.Y+size.Y/2) + data.Vertices[2] = rl.NewVector2(pos.X-size.X/2, pos.Y+size.Y/2) + data.Vertices[3] = rl.NewVector2(pos.X-size.X/2, pos.Y-size.Y/2) // Calculate polygon faces normals for i := 0; i < data.VertexCount; i++ { @@ -769,7 +775,7 @@ func newRectanglePolygon(pos, size raylib.Vector2) Polygon { } face := raymath.Vector2Subtract(data.Vertices[nextIndex], data.Vertices[i]) - data.Normals[i] = raylib.NewVector2(face.Y, -face.X) + data.Normals[i] = rl.NewVector2(face.Y, -face.X) normalize(&data.Normals[i]) } @@ -784,9 +790,9 @@ func newManifold(a, b *Body) *manifold { newManifold.BodyA = a newManifold.BodyB = b newManifold.Penetration = 0 - newManifold.Normal = raylib.Vector2{} - newManifold.Contacts[0] = raylib.Vector2{} - newManifold.Contacts[1] = raylib.Vector2{} + newManifold.Normal = rl.Vector2{} + newManifold.Contacts[0] = rl.Vector2{} + newManifold.Contacts[1] = rl.Vector2{} newManifold.ContactsCount = 0 newManifold.Restitution = 0 newManifold.DynamicFriction = 0 @@ -862,12 +868,12 @@ func (m *manifold) solveCircleToCircle() { if distance == 0 { m.Penetration = bodyA.Shape.Radius - m.Normal = raylib.NewVector2(1, 0) + m.Normal = rl.NewVector2(1, 0) m.Contacts[0] = bodyA.Position } else { m.Penetration = radius - distance - m.Normal = raylib.NewVector2(normal.X/distance, normal.Y/distance) // Faster than using normalize() due to sqrt is already performed - m.Contacts[0] = raylib.NewVector2(m.Normal.X*bodyA.Shape.Radius+bodyA.Position.X, m.Normal.Y*bodyA.Shape.Radius+bodyA.Position.Y) + m.Normal = rl.NewVector2(normal.X/distance, normal.Y/distance) // Faster than using normalize() due to sqrt is already performed + m.Contacts[0] = rl.NewVector2(m.Normal.X*bodyA.Shape.Radius+bodyA.Position.X, m.Normal.Y*bodyA.Shape.Radius+bodyA.Position.Y) } // Update physics body grounded state if normal direction is down @@ -915,8 +921,8 @@ func (m *manifold) solveCircleToPolygon() { if separation < epsilon { m.ContactsCount = 1 normal := raymath.Mat2MultiplyVector2(vertexData.Transform, vertexData.Normals[faceNormal]) - m.Normal = raylib.NewVector2(-normal.X, -normal.Y) - m.Contacts[0] = raylib.NewVector2(m.Normal.X*m.BodyA.Shape.Radius+m.BodyA.Position.X, m.Normal.Y*m.BodyA.Shape.Radius+m.BodyA.Position.Y) + m.Normal = rl.NewVector2(-normal.X, -normal.Y) + m.Contacts[0] = rl.NewVector2(m.Normal.X*m.BodyA.Shape.Radius+m.BodyA.Position.X, m.Normal.Y*m.BodyA.Shape.Radius+m.BodyA.Position.Y) m.Penetration = m.BodyA.Shape.Radius return } @@ -960,8 +966,8 @@ func (m *manifold) solveCircleToPolygon() { } normal = raymath.Mat2MultiplyVector2(vertexData.Transform, normal) - m.Normal = raylib.NewVector2(-normal.X, -normal.Y) - m.Contacts[0] = raylib.NewVector2(m.Normal.X*m.BodyA.Shape.Radius+m.BodyA.Position.X, m.Normal.Y*m.BodyA.Shape.Radius+m.BodyA.Position.Y) + m.Normal = rl.NewVector2(-normal.X, -normal.Y) + m.Contacts[0] = rl.NewVector2(m.Normal.X*m.BodyA.Shape.Radius+m.BodyA.Position.X, m.Normal.Y*m.BodyA.Shape.Radius+m.BodyA.Position.Y) m.ContactsCount = 1 } } @@ -1017,8 +1023,8 @@ func (m *manifold) solvePolygonToPolygon() { } // World space incident face - incidentFace0 := raylib.Vector2{} - incidentFace1 := raylib.Vector2{} + incidentFace0 := rl.Vector2{} + incidentFace1 := rl.Vector2{} findIncidentFace(&incidentFace0, &incidentFace1, refPoly, incPoly, referenceIndex) // Setup reference face vertices @@ -1042,13 +1048,13 @@ func (m *manifold) solvePolygonToPolygon() { normalize(&sidePlaneNormal) // Orthogonalize - refFaceNormal := raylib.NewVector2(sidePlaneNormal.Y, -sidePlaneNormal.X) + refFaceNormal := rl.NewVector2(sidePlaneNormal.Y, -sidePlaneNormal.X) refC := raymath.Vector2DotProduct(refFaceNormal, v1) negSide := raymath.Vector2DotProduct(sidePlaneNormal, v1) * -1 posSide := raymath.Vector2DotProduct(sidePlaneNormal, v2) // clip incident face to reference face side planes (due to floating point error, possible to not have required points - if clip(raylib.NewVector2(-sidePlaneNormal.X, -sidePlaneNormal.Y), negSide, &incidentFace0, &incidentFace1) < 2 { + if clip(rl.NewVector2(-sidePlaneNormal.X, -sidePlaneNormal.Y), negSide, &incidentFace0, &incidentFace1) < 2 { return } if clip(sidePlaneNormal, posSide, &incidentFace0, &incidentFace1) < 2 { @@ -1057,7 +1063,7 @@ func (m *manifold) solvePolygonToPolygon() { // Flip normal if required if flip { - m.Normal = raylib.NewVector2(-refFaceNormal.X, -refFaceNormal.Y) + m.Normal = rl.NewVector2(-refFaceNormal.X, -refFaceNormal.Y) } else { m.Normal = refFaceNormal } @@ -1105,13 +1111,13 @@ func (m *manifold) initializeManifolds() { crossA := raymath.Vector2Cross(bodyA.AngularVelocity, radiusA) crossB := raymath.Vector2Cross(bodyB.AngularVelocity, radiusB) - radiusV := raylib.Vector2{} + radiusV := rl.Vector2{} radiusV.X = bodyB.Velocity.X + crossB.X - bodyA.Velocity.X - crossA.X radiusV.Y = bodyB.Velocity.Y + crossB.Y - bodyA.Velocity.Y - crossA.Y // Determine if we should perform a resting collision or not; // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution - if raymath.Vector2LenSqr(radiusV) < (raymath.Vector2LenSqr(raylib.NewVector2(gravityForce.X*deltaTime, gravityForce.Y*deltaTime)) + epsilon) { + if raymath.Vector2LenSqr(radiusV) < (raymath.Vector2LenSqr(rl.NewVector2(gravityForce.X*deltaTime/1000, gravityForce.Y*deltaTime/1000)) + epsilon) { m.Restitution = 0 } } @@ -1124,8 +1130,8 @@ func (m *manifold) integrateImpulses() { // Early out and positional correct if both objects have infinite mass if math.Abs(float64(bodyA.InverseMass+bodyB.InverseMass)) <= epsilon { - bodyA.Velocity = raylib.Vector2{} - bodyB.Velocity = raylib.Vector2{} + bodyA.Velocity = rl.Vector2{} + bodyB.Velocity = rl.Vector2{} return } @@ -1135,7 +1141,7 @@ func (m *manifold) integrateImpulses() { radiusB := raymath.Vector2Subtract(m.Contacts[i], bodyB.Position) // Calculate relative velocity - radiusV := raylib.Vector2{} + radiusV := rl.Vector2{} radiusV.X = bodyB.Velocity.X + raymath.Vector2Cross(bodyB.AngularVelocity, radiusB).X - bodyA.Velocity.X - raymath.Vector2Cross(bodyA.AngularVelocity, radiusA).X radiusV.Y = bodyB.Velocity.Y + raymath.Vector2Cross(bodyB.AngularVelocity, radiusB).Y - bodyA.Velocity.Y - raymath.Vector2Cross(bodyA.AngularVelocity, radiusA).Y @@ -1158,13 +1164,13 @@ func (m *manifold) integrateImpulses() { impulse /= float32(m.ContactsCount) // Apply impulse to each physics body - impulseV := raylib.NewVector2(m.Normal.X*impulse, m.Normal.Y*impulse) + impulseV := rl.NewVector2(m.Normal.X*impulse, m.Normal.Y*impulse) if bodyA.Enabled { bodyA.Velocity.X += bodyA.InverseMass * (-impulseV.X) bodyA.Velocity.Y += bodyA.InverseMass * (-impulseV.Y) if !bodyA.FreezeOrient { - bodyA.AngularVelocity += bodyA.InverseInertia * raymath.Vector2CrossProduct(radiusA, raylib.NewVector2(-impulseV.X, -impulseV.Y)) + bodyA.AngularVelocity += bodyA.InverseInertia * raymath.Vector2CrossProduct(radiusA, rl.NewVector2(-impulseV.X, -impulseV.Y)) } } @@ -1180,7 +1186,7 @@ func (m *manifold) integrateImpulses() { radiusV.X = bodyB.Velocity.X + raymath.Vector2Cross(bodyB.AngularVelocity, radiusB).X - bodyA.Velocity.X - raymath.Vector2Cross(bodyA.AngularVelocity, radiusA).X radiusV.Y = bodyB.Velocity.Y + raymath.Vector2Cross(bodyB.AngularVelocity, radiusB).Y - bodyA.Velocity.Y - raymath.Vector2Cross(bodyA.AngularVelocity, radiusA).Y - tangent := raylib.NewVector2(radiusV.X-(m.Normal.X*raymath.Vector2DotProduct(radiusV, m.Normal)), radiusV.Y-(m.Normal.Y*raymath.Vector2DotProduct(radiusV, m.Normal))) + tangent := rl.NewVector2(radiusV.X-(m.Normal.X*raymath.Vector2DotProduct(radiusV, m.Normal)), radiusV.Y-(m.Normal.Y*raymath.Vector2DotProduct(radiusV, m.Normal))) normalize(&tangent) // Calculate impulse tangent magnitude @@ -1196,11 +1202,11 @@ func (m *manifold) integrateImpulses() { } // Apply coulumb's law - tangentImpulse := raylib.Vector2{} + tangentImpulse := rl.Vector2{} if absImpulseTangent < impulse*m.StaticFriction { - tangentImpulse = raylib.NewVector2(tangent.X*impulseTangent, tangent.Y*impulseTangent) + tangentImpulse = rl.NewVector2(tangent.X*impulseTangent, tangent.Y*impulseTangent) } else { - tangentImpulse = raylib.NewVector2(tangent.X*-impulse*m.DynamicFriction, tangent.Y*-impulse*m.DynamicFriction) + tangentImpulse = rl.NewVector2(tangent.X*-impulse*m.DynamicFriction, tangent.Y*-impulse*m.DynamicFriction) } // Apply friction impulse @@ -1209,7 +1215,7 @@ func (m *manifold) integrateImpulses() { bodyA.Velocity.Y += bodyA.InverseMass * (-tangentImpulse.Y) if !bodyA.FreezeOrient { - bodyA.AngularVelocity += bodyA.InverseInertia * raymath.Vector2CrossProduct(radiusA, raylib.NewVector2(-tangentImpulse.X, -tangentImpulse.Y)) + bodyA.AngularVelocity += bodyA.InverseInertia * raymath.Vector2CrossProduct(radiusA, rl.NewVector2(-tangentImpulse.X, -tangentImpulse.Y)) } } @@ -1229,7 +1235,7 @@ func (m *manifold) correctPositions() { bodyA := m.BodyA bodyB := m.BodyB - correction := raylib.Vector2{} + correction := rl.Vector2{} correction.X = float32(math.Max(float64(m.Penetration-penetrationAllowance), 0)) / (bodyA.InverseMass + bodyB.InverseMass) * m.Normal.X * penetrationCorrection correction.Y = float32(math.Max(float64(m.Penetration-penetrationAllowance), 0)) / (bodyA.InverseMass + bodyB.InverseMass) * m.Normal.Y * penetrationCorrection @@ -1245,9 +1251,9 @@ func (m *manifold) correctPositions() { } // getSupport - Returns the extreme point along a direction within a polygon -func getSupport(shape Shape, dir raylib.Vector2) raylib.Vector2 { +func getSupport(shape Shape, dir rl.Vector2) rl.Vector2 { bestProjection := float32(-fltMax) - bestVertex := raylib.Vector2{} + bestVertex := rl.Vector2{} for i := 0; i < shape.VertexData.VertexCount; i++ { vertex := shape.VertexData.Vertices[i] @@ -1280,7 +1286,7 @@ func findAxisLeastPenetration(shapeA, shapeB Shape) (int, float32) { normal = raymath.Mat2MultiplyVector2(buT, transNormal) // Retrieve support point from B shape along -n - support := getSupport(shapeB, raylib.NewVector2(-normal.X, -normal.Y)) + support := getSupport(shapeB, rl.NewVector2(-normal.X, -normal.Y)) // Retrieve vertex on face from A shape, transform into B shape's model space vertex := dataA.Vertices[i] @@ -1303,7 +1309,7 @@ func findAxisLeastPenetration(shapeA, shapeB Shape) (int, float32) { } // findIncidentFace - Finds two polygon shapes incident face -func findIncidentFace(v0, v1 *raylib.Vector2, ref, inc Shape, index int) { +func findIncidentFace(v0, v1 *rl.Vector2, ref, inc Shape, index int) { refData := ref.VertexData incData := inc.VertexData @@ -1341,10 +1347,10 @@ func findIncidentFace(v0, v1 *raylib.Vector2, ref, inc Shape, index int) { } // clip - Calculates clipping based on a normal and two faces -func clip(normal raylib.Vector2, clip float32, faceA, faceB *raylib.Vector2) int { +func clip(normal rl.Vector2, clip float32, faceA, faceB *rl.Vector2) int { sp := 0 - out := make([]raylib.Vector2, 2) + out := make([]rl.Vector2, 2) out[0] = *faceA out[1] = *faceB @@ -1387,8 +1393,8 @@ func biasGreaterThan(valueA, valueB float32) bool { } // triangleBarycenter - Returns the barycenter of a triangle given by 3 points -func triangleBarycenter(v1, v2, v3 raylib.Vector2) raylib.Vector2 { - result := raylib.Vector2{} +func triangleBarycenter(v1, v2, v3 rl.Vector2) rl.Vector2 { + result := rl.Vector2{} result.X = (v1.X + v2.X + v3.X) / 3 result.Y = (v1.Y + v2.Y + v3.Y) / 3 @@ -1397,7 +1403,7 @@ func triangleBarycenter(v1, v2, v3 raylib.Vector2) raylib.Vector2 { } // normalize - Normalize provided vector -func normalize(v *raylib.Vector2) { +func normalize(v *rl.Vector2) { var length, ilength float32 aux := *v diff --git a/raygui/raygui.go b/raygui/raygui.go index 31d0392..fda3f59 100644 --- a/raygui/raygui.go +++ b/raygui/raygui.go @@ -339,30 +339,30 @@ var ( ) // BackgroundColor - Get background color -func BackgroundColor() raylib.Color { - return raylib.GetColor(int32(style[GlobalBackgroundColor])) +func BackgroundColor() rl.Color { + return rl.GetColor(int32(style[GlobalBackgroundColor])) } // LinesColor - Get lines color -func LinesColor() raylib.Color { - return raylib.GetColor(int32(style[GlobalLinesColor])) +func LinesColor() rl.Color { + return rl.GetColor(int32(style[GlobalLinesColor])) } // TextColor - Get text color for normal state -func TextColor() raylib.Color { - return raylib.GetColor(int32(style[GlobalTextColor])) +func TextColor() rl.Color { + return rl.GetColor(int32(style[GlobalTextColor])) } // Label - Label element, show text -func Label(bounds raylib.Rectangle, text string) { - LabelEx(bounds, text, raylib.GetColor(int32(style[LabelTextColor])), raylib.NewColor(0, 0, 0, 0), raylib.NewColor(0, 0, 0, 0)) +func Label(bounds rl.Rectangle, text string) { + LabelEx(bounds, text, rl.GetColor(int32(style[LabelTextColor])), rl.NewColor(0, 0, 0, 0), rl.NewColor(0, 0, 0, 0)) } // LabelEx - Label element extended, configurable colors -func LabelEx(bounds raylib.Rectangle, text string, textColor, border, inner raylib.Color) { +func LabelEx(bounds rl.Rectangle, text string, textColor, border, inner rl.Color) { b := bounds.ToInt32() // Update control - textWidth := raylib.MeasureText(text, int32(style[GlobalTextFontsize])) + textWidth := rl.MeasureText(text, int32(style[GlobalTextFontsize])) textHeight := int32(style[GlobalTextFontsize]) if b.Width < textWidth { @@ -373,19 +373,19 @@ func LabelEx(bounds raylib.Rectangle, text string, textColor, border, inner rayl } // Draw control - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, border) - raylib.DrawRectangle(b.X+int32(style[LabelBorderWidth]), b.Y+int32(style[LabelBorderWidth]), b.Width-(2*int32(style[LabelBorderWidth])), b.Height-(2*int32(style[LabelBorderWidth])), inner) - raylib.DrawText(text, b.X+((b.Width/2)-(textWidth/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), textColor) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, border) + rl.DrawRectangle(b.X+int32(style[LabelBorderWidth]), b.Y+int32(style[LabelBorderWidth]), b.Width-(2*int32(style[LabelBorderWidth])), b.Height-(2*int32(style[LabelBorderWidth])), inner) + rl.DrawText(text, b.X+((b.Width/2)-(textWidth/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), textColor) } // Button - Button element, returns true when clicked -func Button(bounds raylib.Rectangle, text string) bool { +func Button(bounds rl.Rectangle, text string) bool { b := bounds.ToInt32() state := Normal - mousePoint := raylib.GetMousePosition() + mousePoint := rl.GetMousePosition() clicked := false - textWidth := raylib.MeasureText(text, int32(style[GlobalTextFontsize])) + textWidth := rl.MeasureText(text, int32(style[GlobalTextFontsize])) textHeight := int32(style[GlobalTextFontsize]) // Update control @@ -397,10 +397,10 @@ func Button(bounds raylib.Rectangle, text string) bool { b.Height = textHeight + int32(style[ButtonTextPadding])/2 } - if raylib.CheckCollisionPointRec(mousePoint, bounds) { - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.CheckCollisionPointRec(mousePoint, bounds) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { state = Pressed - } else if raylib.IsMouseButtonReleased(raylib.MouseLeftButton) || raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + } else if rl.IsMouseButtonReleased(rl.MouseLeftButton) || rl.IsMouseButtonPressed(rl.MouseLeftButton) { clicked = true } else { state = Focused @@ -410,21 +410,21 @@ func Button(bounds raylib.Rectangle, text string) bool { // Draw control switch state { case Normal: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ButtonDefaultBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ButtonBorderWidth]), b.Y+int32(style[ButtonBorderWidth]), b.Width-(2*int32(style[ButtonBorderWidth])), b.Height-(2*int32(style[ButtonBorderWidth])), raylib.GetColor(int32(style[ButtonDefaultInsideColor]))) - raylib.DrawText(text, b.X+((b.Width/2)-(raylib.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ButtonDefaultTextColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ButtonDefaultBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ButtonBorderWidth]), b.Y+int32(style[ButtonBorderWidth]), b.Width-(2*int32(style[ButtonBorderWidth])), b.Height-(2*int32(style[ButtonBorderWidth])), rl.GetColor(int32(style[ButtonDefaultInsideColor]))) + rl.DrawText(text, b.X+((b.Width/2)-(rl.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ButtonDefaultTextColor]))) break case Focused: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ButtonHoverBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ButtonBorderWidth]), b.Y+int32(style[ButtonBorderWidth]), b.Width-(2*int32(style[ButtonBorderWidth])), b.Height-(2*int32(style[ButtonBorderWidth])), raylib.GetColor(int32(style[ButtonHoverInsideColor]))) - raylib.DrawText(text, b.X+((b.Width/2)-(raylib.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ButtonHoverTextColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ButtonHoverBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ButtonBorderWidth]), b.Y+int32(style[ButtonBorderWidth]), b.Width-(2*int32(style[ButtonBorderWidth])), b.Height-(2*int32(style[ButtonBorderWidth])), rl.GetColor(int32(style[ButtonHoverInsideColor]))) + rl.DrawText(text, b.X+((b.Width/2)-(rl.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ButtonHoverTextColor]))) break case Pressed: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ButtonPressedBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ButtonBorderWidth]), b.Y+int32(style[ButtonBorderWidth]), b.Width-(2*int32(style[ButtonBorderWidth])), b.Height-(2*int32(style[ButtonBorderWidth])), raylib.GetColor(int32(style[ButtonPressedInsideColor]))) - raylib.DrawText(text, b.X+((b.Width/2)-(raylib.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ButtonPressedTextColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ButtonPressedBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ButtonBorderWidth]), b.Y+int32(style[ButtonBorderWidth]), b.Width-(2*int32(style[ButtonBorderWidth])), b.Height-(2*int32(style[ButtonBorderWidth])), rl.GetColor(int32(style[ButtonPressedInsideColor]))) + rl.DrawText(text, b.X+((b.Width/2)-(rl.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ButtonPressedTextColor]))) break default: @@ -439,12 +439,12 @@ func Button(bounds raylib.Rectangle, text string) bool { } // ToggleButton - Toggle Button element, returns true when active -func ToggleButton(bounds raylib.Rectangle, text string, active bool) bool { +func ToggleButton(bounds rl.Rectangle, text string, active bool) bool { b := bounds.ToInt32() state := Normal - mousePoint := raylib.GetMousePosition() + mousePoint := rl.GetMousePosition() - textWidth := raylib.MeasureText(text, int32(style[GlobalTextFontsize])) + textWidth := rl.MeasureText(text, int32(style[GlobalTextFontsize])) textHeight := int32(style[GlobalTextFontsize]) // Update control @@ -455,10 +455,10 @@ func ToggleButton(bounds raylib.Rectangle, text string, active bool) bool { b.Height = textHeight + int32(style[ToggleTextPadding])/2 } - if raylib.CheckCollisionPointRec(mousePoint, bounds) { - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.CheckCollisionPointRec(mousePoint, bounds) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { state = Pressed - } else if raylib.IsMouseButtonReleased(raylib.MouseLeftButton) || raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + } else if rl.IsMouseButtonReleased(rl.MouseLeftButton) || rl.IsMouseButtonPressed(rl.MouseLeftButton) { state = Normal active = !active } else { @@ -470,24 +470,24 @@ func ToggleButton(bounds raylib.Rectangle, text string, active bool) bool { switch state { case Normal: if active { - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ToggleActiveBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), raylib.GetColor(int32(style[ToggleActiveInsideColor]))) - raylib.DrawText(text, b.X+((b.Width/2)-(raylib.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ToggleDefaultTextColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ToggleActiveBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), rl.GetColor(int32(style[ToggleActiveInsideColor]))) + rl.DrawText(text, b.X+((b.Width/2)-(rl.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ToggleDefaultTextColor]))) } else { - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ToggleDefaultBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), raylib.GetColor(int32(style[ToggleDefaultInsideColor]))) - raylib.DrawText(text, b.X+((b.Width/2)-(raylib.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ToggleDefaultTextColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ToggleDefaultBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), rl.GetColor(int32(style[ToggleDefaultInsideColor]))) + rl.DrawText(text, b.X+((b.Width/2)-(rl.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ToggleDefaultTextColor]))) } break case Focused: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ToggleHoverBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), raylib.GetColor(int32(style[ToggleHoverInsideColor]))) - raylib.DrawText(text, b.X+((b.Width/2)-(raylib.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ToggleHoverTextColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ToggleHoverBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), rl.GetColor(int32(style[ToggleHoverInsideColor]))) + rl.DrawText(text, b.X+((b.Width/2)-(rl.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ToggleHoverTextColor]))) break case Pressed: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[TogglePressedBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), raylib.GetColor(int32(style[TogglePressedInsideColor]))) - raylib.DrawText(text, b.X+((b.Width/2)-(raylib.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[TogglePressedTextColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[TogglePressedBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), rl.GetColor(int32(style[TogglePressedInsideColor]))) + rl.DrawText(text, b.X+((b.Width/2)-(rl.MeasureText(text, int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[TogglePressedTextColor]))) break default: break @@ -497,11 +497,11 @@ func ToggleButton(bounds raylib.Rectangle, text string, active bool) bool { } // ToggleGroup - Toggle Group element, returns toggled button index -func ToggleGroup(bounds raylib.Rectangle, toggleText []string, active int) int { +func ToggleGroup(bounds rl.Rectangle, toggleText []string, active int) int { for i := 0; i < len(toggleText); i++ { if i == active { - ToggleButton(raylib.NewRectangle(bounds.X+float32(i)*(bounds.Width+float32(style[TogglegroupPadding])), bounds.Y, bounds.Width, bounds.Height), toggleText[i], true) - } else if ToggleButton(raylib.NewRectangle(bounds.X+float32(i)*(bounds.Width+float32(style[TogglegroupPadding])), bounds.Y, bounds.Width, bounds.Height), toggleText[i], false) { + ToggleButton(rl.NewRectangle(bounds.X+float32(i)*(bounds.Width+float32(style[TogglegroupPadding])), bounds.Y, bounds.Width, bounds.Height), toggleText[i], true) + } else if ToggleButton(rl.NewRectangle(bounds.X+float32(i)*(bounds.Width+float32(style[TogglegroupPadding])), bounds.Y, bounds.Width, bounds.Height), toggleText[i], false) { active = i } } @@ -510,15 +510,15 @@ func ToggleGroup(bounds raylib.Rectangle, toggleText []string, active int) int { } // ComboBox - Combo Box element, returns selected item index -func ComboBox(bounds raylib.Rectangle, comboText []string, active int) int { +func ComboBox(bounds rl.Rectangle, comboText []string, active int) int { b := bounds.ToInt32() state := Normal clicked := false - click := raylib.NewRectangle(bounds.X+bounds.Width+float32(style[ComboboxPadding]), bounds.Y, float32(style[boundsWidth]), float32(style[boundsHeight])) + click := rl.NewRectangle(bounds.X+bounds.Width+float32(style[ComboboxPadding]), bounds.Y, float32(style[boundsWidth]), float32(style[boundsHeight])) c := click.ToInt32() - mousePoint := raylib.GetMousePosition() + mousePoint := rl.GetMousePosition() textWidth := int32(0) textHeight := int32(style[GlobalTextFontsize]) @@ -528,7 +528,7 @@ func ComboBox(bounds raylib.Rectangle, comboText []string, active int) int { for i := 0; i < comboCount; i++ { if i == active { // Update control - textWidth = raylib.MeasureText(comboText[i], int32(style[GlobalTextFontsize])) + textWidth = rl.MeasureText(comboText[i], int32(style[GlobalTextFontsize])) if b.Width < textWidth { b.Width = textWidth + int32(style[ToggleTextPadding]) @@ -537,10 +537,10 @@ func ComboBox(bounds raylib.Rectangle, comboText []string, active int) int { b.Height = textHeight + int32(style[ToggleTextPadding])/2 } - if raylib.CheckCollisionPointRec(mousePoint, bounds) || raylib.CheckCollisionPointRec(mousePoint, click) { - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.CheckCollisionPointRec(mousePoint, bounds) || rl.CheckCollisionPointRec(mousePoint, click) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { state = Pressed - } else if raylib.IsMouseButtonReleased(raylib.MouseLeftButton) || raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + } else if rl.IsMouseButtonReleased(rl.MouseLeftButton) || rl.IsMouseButtonPressed(rl.MouseLeftButton) { clicked = true } else { state = Focused @@ -550,51 +550,51 @@ func ComboBox(bounds raylib.Rectangle, comboText []string, active int) int { // Draw control switch state { case Normal: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ComboboxDefaultBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ComboboxBorderWidth]), b.Y+int32(style[ComboboxBorderWidth]), b.Width-(2*int32(style[ComboboxBorderWidth])), b.Height-(2*int32(style[ComboboxBorderWidth])), raylib.GetColor(int32(style[ComboboxDefaultInsideColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ComboboxDefaultBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ComboboxBorderWidth]), b.Y+int32(style[ComboboxBorderWidth]), b.Width-(2*int32(style[ComboboxBorderWidth])), b.Height-(2*int32(style[ComboboxBorderWidth])), rl.GetColor(int32(style[ComboboxDefaultInsideColor]))) - raylib.DrawRectangle(c.X, c.Y, c.Width, c.Height, raylib.GetColor(int32(style[ComboboxDefaultBorderColor]))) - raylib.DrawRectangle(c.X+int32(style[ComboboxBorderWidth]), c.Y+int32(style[ComboboxBorderWidth]), c.Width-(2*int32(style[ComboboxBorderWidth])), c.Height-(2*int32(style[ComboboxBorderWidth])), raylib.GetColor(int32(style[ComboboxDefaultInsideColor]))) - raylib.DrawText(fmt.Sprintf("%d/%d", active+1, comboCount), c.X+((c.Width/2)-(raylib.MeasureText(fmt.Sprintf("%d/%d", active+1, comboCount), int32(style[GlobalTextFontsize]))/2)), c.Y+((c.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ComboboxDefaultListTextColor]))) - raylib.DrawText(comboText[i], b.X+((b.Width/2)-(raylib.MeasureText(comboText[i], int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ComboboxDefaultTextColor]))) + rl.DrawRectangle(c.X, c.Y, c.Width, c.Height, rl.GetColor(int32(style[ComboboxDefaultBorderColor]))) + rl.DrawRectangle(c.X+int32(style[ComboboxBorderWidth]), c.Y+int32(style[ComboboxBorderWidth]), c.Width-(2*int32(style[ComboboxBorderWidth])), c.Height-(2*int32(style[ComboboxBorderWidth])), rl.GetColor(int32(style[ComboboxDefaultInsideColor]))) + rl.DrawText(fmt.Sprintf("%d/%d", active+1, comboCount), c.X+((c.Width/2)-(rl.MeasureText(fmt.Sprintf("%d/%d", active+1, comboCount), int32(style[GlobalTextFontsize]))/2)), c.Y+((c.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ComboboxDefaultListTextColor]))) + rl.DrawText(comboText[i], b.X+((b.Width/2)-(rl.MeasureText(comboText[i], int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ComboboxDefaultTextColor]))) break case Focused: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ComboboxHoverBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ComboboxBorderWidth]), b.Y+int32(style[ComboboxBorderWidth]), b.Width-(2*int32(style[ComboboxBorderWidth])), b.Height-(2*int32(style[ComboboxBorderWidth])), raylib.GetColor(int32(style[ComboboxHoverInsideColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ComboboxHoverBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ComboboxBorderWidth]), b.Y+int32(style[ComboboxBorderWidth]), b.Width-(2*int32(style[ComboboxBorderWidth])), b.Height-(2*int32(style[ComboboxBorderWidth])), rl.GetColor(int32(style[ComboboxHoverInsideColor]))) - raylib.DrawRectangle(c.X, c.Y, c.Width, c.Height, raylib.GetColor(int32(style[ComboboxHoverBorderColor]))) - raylib.DrawRectangle(c.X+int32(style[ComboboxBorderWidth]), c.Y+int32(style[ComboboxBorderWidth]), c.Width-(2*int32(style[ComboboxBorderWidth])), c.Height-(2*int32(style[ComboboxBorderWidth])), raylib.GetColor(int32(style[ComboboxHoverInsideColor]))) - raylib.DrawText(fmt.Sprintf("%d/%d", active+1, comboCount), c.X+((c.Width/2)-(raylib.MeasureText(fmt.Sprintf("%d/%d", active+1, comboCount), int32(style[GlobalTextFontsize]))/2)), c.Y+((c.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ComboboxHoverListTextColor]))) - raylib.DrawText(comboText[i], b.X+((b.Width/2)-(raylib.MeasureText(comboText[i], int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ComboboxHoverTextColor]))) + rl.DrawRectangle(c.X, c.Y, c.Width, c.Height, rl.GetColor(int32(style[ComboboxHoverBorderColor]))) + rl.DrawRectangle(c.X+int32(style[ComboboxBorderWidth]), c.Y+int32(style[ComboboxBorderWidth]), c.Width-(2*int32(style[ComboboxBorderWidth])), c.Height-(2*int32(style[ComboboxBorderWidth])), rl.GetColor(int32(style[ComboboxHoverInsideColor]))) + rl.DrawText(fmt.Sprintf("%d/%d", active+1, comboCount), c.X+((c.Width/2)-(rl.MeasureText(fmt.Sprintf("%d/%d", active+1, comboCount), int32(style[GlobalTextFontsize]))/2)), c.Y+((c.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ComboboxHoverListTextColor]))) + rl.DrawText(comboText[i], b.X+((b.Width/2)-(rl.MeasureText(comboText[i], int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ComboboxHoverTextColor]))) break case Pressed: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ComboboxPressedBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ComboboxBorderWidth]), b.Y+int32(style[ComboboxBorderWidth]), b.Width-(2*int32(style[ComboboxBorderWidth])), b.Height-(2*int32(style[ComboboxBorderWidth])), raylib.GetColor(int32(style[ComboboxPressedInsideColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ComboboxPressedBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ComboboxBorderWidth]), b.Y+int32(style[ComboboxBorderWidth]), b.Width-(2*int32(style[ComboboxBorderWidth])), b.Height-(2*int32(style[ComboboxBorderWidth])), rl.GetColor(int32(style[ComboboxPressedInsideColor]))) - raylib.DrawRectangle(c.X, c.Y, c.Width, c.Height, raylib.GetColor(int32(style[ComboboxPressedListBorderColor]))) - raylib.DrawRectangle(c.X+int32(style[ComboboxBorderWidth]), c.Y+int32(style[ComboboxBorderWidth]), c.Width-(2*int32(style[ComboboxBorderWidth])), c.Height-(2*int32(style[ComboboxBorderWidth])), raylib.GetColor(int32(style[ComboboxPressedListInsideColor]))) - raylib.DrawText(fmt.Sprintf("%d/%d", active+1, comboCount), c.X+((c.Width/2)-(raylib.MeasureText(fmt.Sprintf("%d/%d", active+1, comboCount), int32(style[GlobalTextFontsize]))/2)), c.Y+((c.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ComboboxPressedListTextColor]))) - raylib.DrawText(comboText[i], b.X+((b.Width/2)-(raylib.MeasureText(comboText[i], int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ComboboxPressedTextColor]))) + rl.DrawRectangle(c.X, c.Y, c.Width, c.Height, rl.GetColor(int32(style[ComboboxPressedListBorderColor]))) + rl.DrawRectangle(c.X+int32(style[ComboboxBorderWidth]), c.Y+int32(style[ComboboxBorderWidth]), c.Width-(2*int32(style[ComboboxBorderWidth])), c.Height-(2*int32(style[ComboboxBorderWidth])), rl.GetColor(int32(style[ComboboxPressedListInsideColor]))) + rl.DrawText(fmt.Sprintf("%d/%d", active+1, comboCount), c.X+((c.Width/2)-(rl.MeasureText(fmt.Sprintf("%d/%d", active+1, comboCount), int32(style[GlobalTextFontsize]))/2)), c.Y+((c.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ComboboxPressedListTextColor]))) + rl.DrawText(comboText[i], b.X+((b.Width/2)-(rl.MeasureText(comboText[i], int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ComboboxPressedTextColor]))) break default: break } if clicked { - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ComboboxPressedBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ComboboxBorderWidth]), b.Y+int32(style[ComboboxBorderWidth]), b.Width-(2*int32(style[ComboboxBorderWidth])), b.Height-(2*int32(style[ComboboxBorderWidth])), raylib.GetColor(int32(style[ComboboxPressedInsideColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ComboboxPressedBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ComboboxBorderWidth]), b.Y+int32(style[ComboboxBorderWidth]), b.Width-(2*int32(style[ComboboxBorderWidth])), b.Height-(2*int32(style[ComboboxBorderWidth])), rl.GetColor(int32(style[ComboboxPressedInsideColor]))) - raylib.DrawRectangle(c.X, c.Y, c.Width, c.Height, raylib.GetColor(int32(style[ComboboxPressedListBorderColor]))) - raylib.DrawRectangle(c.X+int32(style[ComboboxBorderWidth]), c.Y+int32(style[ComboboxBorderWidth]), c.Width-(2*int32(style[ComboboxBorderWidth])), c.Height-(2*int32(style[ComboboxBorderWidth])), raylib.GetColor(int32(style[ComboboxPressedListInsideColor]))) - raylib.DrawText(fmt.Sprintf("%d/%d", active+1, comboCount), c.X+((c.Width/2)-(raylib.MeasureText(fmt.Sprintf("%d/%d", active+1, comboCount), int32(style[GlobalTextFontsize]))/2)), c.Y+((c.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ComboboxPressedListTextColor]))) - raylib.DrawText(comboText[i], b.X+((b.Width/2)-(raylib.MeasureText(comboText[i], int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[ComboboxPressedTextColor]))) + rl.DrawRectangle(c.X, c.Y, c.Width, c.Height, rl.GetColor(int32(style[ComboboxPressedListBorderColor]))) + rl.DrawRectangle(c.X+int32(style[ComboboxBorderWidth]), c.Y+int32(style[ComboboxBorderWidth]), c.Width-(2*int32(style[ComboboxBorderWidth])), c.Height-(2*int32(style[ComboboxBorderWidth])), rl.GetColor(int32(style[ComboboxPressedListInsideColor]))) + rl.DrawText(fmt.Sprintf("%d/%d", active+1, comboCount), c.X+((c.Width/2)-(rl.MeasureText(fmt.Sprintf("%d/%d", active+1, comboCount), int32(style[GlobalTextFontsize]))/2)), c.Y+((c.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ComboboxPressedListTextColor]))) + rl.DrawText(comboText[i], b.X+((b.Width/2)-(rl.MeasureText(comboText[i], int32(style[GlobalTextFontsize]))/2)), b.Y+((b.Height/2)-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[ComboboxPressedTextColor]))) } } } - if raylib.CheckCollisionPointRec(mousePoint, bounds) || raylib.CheckCollisionPointRec(mousePoint, click) { - if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + if rl.CheckCollisionPointRec(mousePoint, bounds) || rl.CheckCollisionPointRec(mousePoint, click) { + if rl.IsMouseButtonPressed(rl.MouseLeftButton) { active++ if active >= comboCount { active = 0 @@ -606,16 +606,16 @@ func ComboBox(bounds raylib.Rectangle, comboText []string, active int) int { } // CheckBox - Check Box element, returns true when active -func CheckBox(bounds raylib.Rectangle, checked bool) bool { +func CheckBox(bounds rl.Rectangle, checked bool) bool { b := bounds.ToInt32() state := Normal - mousePoint := raylib.GetMousePosition() + mousePoint := rl.GetMousePosition() // Update control - if raylib.CheckCollisionPointRec(mousePoint, bounds) { - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.CheckCollisionPointRec(mousePoint, bounds) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { state = Pressed - } else if raylib.IsMouseButtonReleased(raylib.MouseLeftButton) || raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + } else if rl.IsMouseButtonReleased(rl.MouseLeftButton) || rl.IsMouseButtonPressed(rl.MouseLeftButton) { state = Normal checked = !checked } else { @@ -626,36 +626,36 @@ func CheckBox(bounds raylib.Rectangle, checked bool) bool { // Draw control switch state { case Normal: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[CheckboxDefaultBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), raylib.GetColor(int32(style[CheckboxDefaultInsideColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[CheckboxDefaultBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), rl.GetColor(int32(style[CheckboxDefaultInsideColor]))) break case Focused: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[CheckboxHoverBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), raylib.GetColor(int32(style[CheckboxHoverInsideColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[CheckboxHoverBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), rl.GetColor(int32(style[CheckboxHoverInsideColor]))) break case Pressed: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[CheckboxClickBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), raylib.GetColor(int32(style[CheckboxClickInsideColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[CheckboxClickBorderColor]))) + rl.DrawRectangle(b.X+int32(style[ToggleBorderWidth]), b.Y+int32(style[ToggleBorderWidth]), b.Width-(2*int32(style[ToggleBorderWidth])), b.Height-(2*int32(style[ToggleBorderWidth])), rl.GetColor(int32(style[CheckboxClickInsideColor]))) break default: break } if checked { - raylib.DrawRectangle(b.X+int32(style[CheckboxInsideWidth]), b.Y+int32(style[CheckboxInsideWidth]), b.Width-(2*int32(style[CheckboxInsideWidth])), b.Height-(2*int32(style[CheckboxInsideWidth])), raylib.GetColor(int32(style[CheckboxDefaultActiveColor]))) + rl.DrawRectangle(b.X+int32(style[CheckboxInsideWidth]), b.Y+int32(style[CheckboxInsideWidth]), b.Width-(2*int32(style[CheckboxInsideWidth])), b.Height-(2*int32(style[CheckboxInsideWidth])), rl.GetColor(int32(style[CheckboxDefaultActiveColor]))) } return checked } // Slider - Slider element, returns selected value -func Slider(bounds raylib.Rectangle, value, minValue, maxValue float32) float32 { +func Slider(bounds rl.Rectangle, value, minValue, maxValue float32) float32 { b := bounds.ToInt32() sliderPos := float32(0) state := Normal buttonTravelDistance := float32(0) - mousePoint := raylib.GetMousePosition() + mousePoint := rl.GetMousePosition() // Update control if value < minValue { @@ -666,7 +666,7 @@ func Slider(bounds raylib.Rectangle, value, minValue, maxValue float32) float32 sliderPos = (value - minValue) / (maxValue - minValue) - sliderButton := raylib.RectangleInt32{} + sliderButton := rl.RectangleInt32{} sliderButton.Width = (b.Width-(2*int32(style[SliderButtonBorderWidth])))/10 - 8 sliderButton.Height = b.Height - (2 * int32(style[SliderBorderWidth]+2*style[SliderButtonBorderWidth])) @@ -678,14 +678,14 @@ func Slider(bounds raylib.Rectangle, value, minValue, maxValue float32) float32 sliderButton.X = b.X + int32(style[SliderBorderWidth]) + int32(style[SliderButtonBorderWidth]) + int32(sliderPos*buttonTravelDistance) sliderButton.Y = b.Y + int32(style[SliderBorderWidth]) + int32(style[SliderButtonBorderWidth]) - if raylib.CheckCollisionPointRec(mousePoint, bounds) { + if rl.CheckCollisionPointRec(mousePoint, bounds) { state = Focused - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { state = Pressed } - if state == Pressed && raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if state == Pressed && rl.IsMouseButtonDown(rl.MouseLeftButton) { sliderButton.X = int32(mousePoint.X) - sliderButton.Width/2 if sliderButton.X <= sliderButtonMinPos { @@ -701,18 +701,18 @@ func Slider(bounds raylib.Rectangle, value, minValue, maxValue float32) float32 } // Draw control - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[SliderBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[SliderBorderWidth]), b.Y+int32(style[SliderBorderWidth]), b.Width-(2*int32(style[SliderBorderWidth])), b.Height-(2*int32(style[SliderBorderWidth])), raylib.GetColor(int32(style[SliderInsideColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[SliderBorderColor]))) + rl.DrawRectangle(b.X+int32(style[SliderBorderWidth]), b.Y+int32(style[SliderBorderWidth]), b.Width-(2*int32(style[SliderBorderWidth])), b.Height-(2*int32(style[SliderBorderWidth])), rl.GetColor(int32(style[SliderInsideColor]))) switch state { case Normal: - raylib.DrawRectangle(sliderButton.X, sliderButton.Y, sliderButton.Width, sliderButton.Height, raylib.GetColor(int32(style[SliderDefaultColor]))) + rl.DrawRectangle(sliderButton.X, sliderButton.Y, sliderButton.Width, sliderButton.Height, rl.GetColor(int32(style[SliderDefaultColor]))) break case Focused: - raylib.DrawRectangle(sliderButton.X, sliderButton.Y, sliderButton.Width, sliderButton.Height, raylib.GetColor(int32(style[SliderHoverColor]))) + rl.DrawRectangle(sliderButton.X, sliderButton.Y, sliderButton.Width, sliderButton.Height, rl.GetColor(int32(style[SliderHoverColor]))) break case Pressed: - raylib.DrawRectangle(sliderButton.X, sliderButton.Y, sliderButton.Width, sliderButton.Height, raylib.GetColor(int32(style[SliderActiveColor]))) + rl.DrawRectangle(sliderButton.X, sliderButton.Y, sliderButton.Width, sliderButton.Height, rl.GetColor(int32(style[SliderActiveColor]))) break default: break @@ -722,11 +722,11 @@ func Slider(bounds raylib.Rectangle, value, minValue, maxValue float32) float32 } // SliderBar - Slider Bar element, returns selected value -func SliderBar(bounds raylib.Rectangle, value, minValue, maxValue float32) float32 { +func SliderBar(bounds rl.Rectangle, value, minValue, maxValue float32) float32 { b := bounds.ToInt32() state := Normal - mousePoint := raylib.GetMousePosition() + mousePoint := rl.GetMousePosition() fixedValue := float32(0) fixedMinValue := float32(0) @@ -742,17 +742,17 @@ func SliderBar(bounds raylib.Rectangle, value, minValue, maxValue float32) float fixedValue = maxValue } - sliderBar := raylib.RectangleInt32{} + sliderBar := rl.RectangleInt32{} sliderBar.X = b.X + int32(style[SliderBorderWidth]) sliderBar.Y = b.Y + int32(style[SliderBorderWidth]) sliderBar.Width = int32((fixedValue * (float32(b.Width) - 2*float32(style[SliderBorderWidth]))) / (maxValue - fixedMinValue)) sliderBar.Height = b.Height - 2*int32(style[SliderBorderWidth]) - if raylib.CheckCollisionPointRec(mousePoint, bounds) { + if rl.CheckCollisionPointRec(mousePoint, bounds) { state = Focused - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { state = Pressed sliderBar.Width = (int32(mousePoint.X) - b.X - int32(style[SliderBorderWidth])) @@ -770,32 +770,32 @@ func SliderBar(bounds raylib.Rectangle, value, minValue, maxValue float32) float fixedValue = (float32(sliderBar.Width) * (maxValue - fixedMinValue)) / (float32(b.Width) - 2*float32(style[SliderBorderWidth])) // Draw control - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[SliderbarBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[SliderBorderWidth]), b.Y+int32(style[SliderBorderWidth]), b.Width-(2*int32(style[SliderBorderWidth])), b.Height-(2*int32(style[SliderBorderWidth])), raylib.GetColor(int32(style[SliderbarInsideColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[SliderbarBorderColor]))) + rl.DrawRectangle(b.X+int32(style[SliderBorderWidth]), b.Y+int32(style[SliderBorderWidth]), b.Width-(2*int32(style[SliderBorderWidth])), b.Height-(2*int32(style[SliderBorderWidth])), rl.GetColor(int32(style[SliderbarInsideColor]))) switch state { case Normal: - raylib.DrawRectangle(sliderBar.X, sliderBar.Y, sliderBar.Width, sliderBar.Height, raylib.GetColor(int32(style[SliderbarDefaultColor]))) + rl.DrawRectangle(sliderBar.X, sliderBar.Y, sliderBar.Width, sliderBar.Height, rl.GetColor(int32(style[SliderbarDefaultColor]))) break case Focused: - raylib.DrawRectangle(sliderBar.X, sliderBar.Y, sliderBar.Width, sliderBar.Height, raylib.GetColor(int32(style[SliderbarHoverColor]))) + rl.DrawRectangle(sliderBar.X, sliderBar.Y, sliderBar.Width, sliderBar.Height, rl.GetColor(int32(style[SliderbarHoverColor]))) break case Pressed: - raylib.DrawRectangle(sliderBar.X, sliderBar.Y, sliderBar.Width, sliderBar.Height, raylib.GetColor(int32(style[SliderbarActiveColor]))) + rl.DrawRectangle(sliderBar.X, sliderBar.Y, sliderBar.Width, sliderBar.Height, rl.GetColor(int32(style[SliderbarActiveColor]))) break default: break } if minValue < 0 && maxValue > 0 { - raylib.DrawRectangle((b.X+int32(style[SliderBorderWidth]))-int32(minValue*(float32(b.Width-(int32(style[SliderBorderWidth])*2))/maxValue)), sliderBar.Y, 1, sliderBar.Height, raylib.GetColor(int32(style[SliderbarZeroLineColor]))) + rl.DrawRectangle((b.X+int32(style[SliderBorderWidth]))-int32(minValue*(float32(b.Width-(int32(style[SliderBorderWidth])*2))/maxValue)), sliderBar.Y, 1, sliderBar.Height, rl.GetColor(int32(style[SliderbarZeroLineColor]))) } return fixedValue + minValue } // ProgressBar - Progress Bar element, shows current progress value -func ProgressBar(bounds raylib.Rectangle, value float32) { +func ProgressBar(bounds rl.Rectangle, value float32) { b := bounds.ToInt32() if value > 1.0 { value = 1.0 @@ -803,39 +803,39 @@ func ProgressBar(bounds raylib.Rectangle, value float32) { value = 0.0 } - progressBar := raylib.RectangleInt32{b.X + int32(style[ProgressbarBorderWidth]), b.Y + int32(style[ProgressbarBorderWidth]), b.Width - (int32(style[ProgressbarBorderWidth]) * 2), b.Height - (int32(style[ProgressbarBorderWidth]) * 2)} - progressValue := raylib.RectangleInt32{b.X + int32(style[ProgressbarBorderWidth]), b.Y + int32(style[ProgressbarBorderWidth]), int32(value * float32(b.Width-int32(style[ProgressbarBorderWidth])*2)), b.Height - (int32(style[ProgressbarBorderWidth]) * 2)} + progressBar := rl.RectangleInt32{b.X + int32(style[ProgressbarBorderWidth]), b.Y + int32(style[ProgressbarBorderWidth]), b.Width - (int32(style[ProgressbarBorderWidth]) * 2), b.Height - (int32(style[ProgressbarBorderWidth]) * 2)} + progressValue := rl.RectangleInt32{b.X + int32(style[ProgressbarBorderWidth]), b.Y + int32(style[ProgressbarBorderWidth]), int32(value * float32(b.Width-int32(style[ProgressbarBorderWidth])*2)), b.Height - (int32(style[ProgressbarBorderWidth]) * 2)} // Draw control - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ProgressbarBorderColor]))) - raylib.DrawRectangle(progressBar.X, progressBar.Y, progressBar.Width, progressBar.Height, raylib.GetColor(int32(style[ProgressbarInsideColor]))) - raylib.DrawRectangle(progressValue.X, progressValue.Y, progressValue.Width, progressValue.Height, raylib.GetColor(int32(style[ProgressbarProgressColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ProgressbarBorderColor]))) + rl.DrawRectangle(progressBar.X, progressBar.Y, progressBar.Width, progressBar.Height, rl.GetColor(int32(style[ProgressbarInsideColor]))) + rl.DrawRectangle(progressValue.X, progressValue.Y, progressValue.Width, progressValue.Height, rl.GetColor(int32(style[ProgressbarProgressColor]))) } // Spinner - Spinner element, returns selected value -func Spinner(bounds raylib.Rectangle, value, minValue, maxValue int) int { +func Spinner(bounds rl.Rectangle, value, minValue, maxValue int) int { b := bounds.ToInt32() state := Normal - mousePoint := raylib.GetMousePosition() - labelBoxBound := raylib.RectangleInt32{b.X + b.Width/4 + 1, b.Y, b.Width / 2, b.Height} - leftButtonBound := raylib.RectangleInt32{b.X, b.Y, b.Width / 4, b.Height} - rightButtonBound := raylib.RectangleInt32{b.X + b.Width - b.Width/4 + 1, b.Y, b.Width / 4, b.Height} + mousePoint := rl.GetMousePosition() + labelBoxBound := rl.RectangleInt32{b.X + b.Width/4 + 1, b.Y, b.Width / 2, b.Height} + leftButtonBound := rl.RectangleInt32{b.X, b.Y, b.Width / 4, b.Height} + rightButtonBound := rl.RectangleInt32{b.X + b.Width - b.Width/4 + 1, b.Y, b.Width / 4, b.Height} - textWidth := raylib.MeasureText(fmt.Sprintf("%d", value), int32(style[GlobalTextFontsize])) + textWidth := rl.MeasureText(fmt.Sprintf("%d", value), int32(style[GlobalTextFontsize])) buttonSide := 0 // Update control - if raylib.CheckCollisionPointRec(mousePoint, leftButtonBound.ToFloat32()) || raylib.CheckCollisionPointRec(mousePoint, rightButtonBound.ToFloat32()) || raylib.CheckCollisionPointRec(mousePoint, labelBoxBound.ToFloat32()) { - if raylib.IsKeyDown(raylib.KeyLeft) { + if rl.CheckCollisionPointRec(mousePoint, leftButtonBound.ToFloat32()) || rl.CheckCollisionPointRec(mousePoint, rightButtonBound.ToFloat32()) || rl.CheckCollisionPointRec(mousePoint, labelBoxBound.ToFloat32()) { + if rl.IsKeyDown(rl.KeyLeft) { state = Pressed buttonSide = 1 if value > minValue { value-- } - } else if raylib.IsKeyDown(raylib.KeyRight) { + } else if rl.IsKeyDown(rl.KeyRight) { state = Pressed buttonSide = 2 @@ -845,11 +845,11 @@ func Spinner(bounds raylib.Rectangle, value, minValue, maxValue int) int { } } - if raylib.CheckCollisionPointRec(mousePoint, leftButtonBound.ToFloat32()) { + if rl.CheckCollisionPointRec(mousePoint, leftButtonBound.ToFloat32()) { buttonSide = 1 state = Focused - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { if !valueSpeed { if value > minValue { value-- @@ -867,11 +867,11 @@ func Spinner(bounds raylib.Rectangle, value, minValue, maxValue int) int { } } } - } else if raylib.CheckCollisionPointRec(mousePoint, rightButtonBound.ToFloat32()) { + } else if rl.CheckCollisionPointRec(mousePoint, rightButtonBound.ToFloat32()) { buttonSide = 2 state = Focused - if raylib.IsMouseButtonDown(raylib.MouseLeftButton) { + if rl.IsMouseButtonDown(rl.MouseLeftButton) { if !valueSpeed { if value < maxValue { value++ @@ -889,11 +889,11 @@ func Spinner(bounds raylib.Rectangle, value, minValue, maxValue int) int { } } } - } else if !raylib.CheckCollisionPointRec(mousePoint, labelBoxBound.ToFloat32()) { + } else if !rl.CheckCollisionPointRec(mousePoint, labelBoxBound.ToFloat32()) { buttonSide = 0 } - if raylib.IsMouseButtonUp(raylib.MouseLeftButton) { + if rl.IsMouseButtonUp(rl.MouseLeftButton) { valueSpeed = false framesCounter = 0 } @@ -901,71 +901,71 @@ func Spinner(bounds raylib.Rectangle, value, minValue, maxValue int) int { // Draw control switch state { case Normal: - raylib.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, raylib.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) - raylib.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) - raylib.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, raylib.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) - raylib.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) - raylib.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(raylib.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerDefaultSymbolColor]))) - raylib.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(raylib.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerDefaultSymbolColor]))) + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerDefaultSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerDefaultSymbolColor]))) - raylib.DrawRectangle(labelBoxBound.X, labelBoxBound.Y, labelBoxBound.Width, labelBoxBound.Height, raylib.GetColor(int32(style[SpinnerLabelBorderColor]))) - raylib.DrawRectangle(labelBoxBound.X+1, labelBoxBound.Y+1, labelBoxBound.Width-2, labelBoxBound.Height-2, raylib.GetColor(int32(style[SpinnerLabelInsideColor]))) + rl.DrawRectangle(labelBoxBound.X, labelBoxBound.Y, labelBoxBound.Width, labelBoxBound.Height, rl.GetColor(int32(style[SpinnerLabelBorderColor]))) + rl.DrawRectangle(labelBoxBound.X+1, labelBoxBound.Y+1, labelBoxBound.Width-2, labelBoxBound.Height-2, rl.GetColor(int32(style[SpinnerLabelInsideColor]))) - raylib.DrawText(fmt.Sprintf("%d", value), labelBoxBound.X+(labelBoxBound.Width/2-textWidth/2), labelBoxBound.Y+(labelBoxBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerDefaultTextColor]))) + rl.DrawText(fmt.Sprintf("%d", value), labelBoxBound.X+(labelBoxBound.Width/2-textWidth/2), labelBoxBound.Y+(labelBoxBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerDefaultTextColor]))) break case Focused: if buttonSide == 1 { - raylib.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, raylib.GetColor(int32(style[SpinnerHoverButtonBorderColor]))) - raylib.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerHoverButtonInsideColor]))) + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(int32(style[SpinnerHoverButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(int32(style[SpinnerHoverButtonInsideColor]))) - raylib.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, raylib.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) - raylib.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) - raylib.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(raylib.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerHoverSymbolColor]))) - raylib.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(raylib.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerDefaultSymbolColor]))) + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerHoverSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerDefaultSymbolColor]))) } else if buttonSide == 2 { - raylib.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, raylib.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) - raylib.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) - raylib.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, raylib.GetColor(int32(style[SpinnerHoverButtonBorderColor]))) - raylib.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerHoverButtonInsideColor]))) + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(int32(style[SpinnerHoverButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(int32(style[SpinnerHoverButtonInsideColor]))) - raylib.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(raylib.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerDefaultSymbolColor]))) - raylib.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(raylib.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerHoverSymbolColor]))) + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerDefaultSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerHoverSymbolColor]))) } - raylib.DrawRectangle(labelBoxBound.X, labelBoxBound.Y, labelBoxBound.Width, labelBoxBound.Height, raylib.GetColor(int32(style[SpinnerLabelBorderColor]))) - raylib.DrawRectangle(labelBoxBound.X+1, labelBoxBound.Y+1, labelBoxBound.Width-2, labelBoxBound.Height-2, raylib.GetColor(int32(style[SpinnerLabelInsideColor]))) + rl.DrawRectangle(labelBoxBound.X, labelBoxBound.Y, labelBoxBound.Width, labelBoxBound.Height, rl.GetColor(int32(style[SpinnerLabelBorderColor]))) + rl.DrawRectangle(labelBoxBound.X+1, labelBoxBound.Y+1, labelBoxBound.Width-2, labelBoxBound.Height-2, rl.GetColor(int32(style[SpinnerLabelInsideColor]))) - raylib.DrawText(fmt.Sprintf("%d", value), labelBoxBound.X+(labelBoxBound.Width/2-textWidth/2), labelBoxBound.Y+(labelBoxBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerHoverTextColor]))) + rl.DrawText(fmt.Sprintf("%d", value), labelBoxBound.X+(labelBoxBound.Width/2-textWidth/2), labelBoxBound.Y+(labelBoxBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerHoverTextColor]))) break case Pressed: if buttonSide == 1 { - raylib.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, raylib.GetColor(int32(style[SpinnerPressedButtonBorderColor]))) - raylib.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerPressedButtonInsideColor]))) + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(int32(style[SpinnerPressedButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(int32(style[SpinnerPressedButtonInsideColor]))) - raylib.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, raylib.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) - raylib.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) - raylib.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(raylib.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerPressedSymbolColor]))) - raylib.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(raylib.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerDefaultSymbolColor]))) + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerPressedSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerDefaultSymbolColor]))) } else if buttonSide == 2 { - raylib.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, raylib.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) - raylib.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(int32(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(int32(style[SpinnerDefaultButtonInsideColor]))) - raylib.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, raylib.GetColor(int32(style[SpinnerPressedButtonBorderColor]))) - raylib.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, raylib.GetColor(int32(style[SpinnerPressedButtonInsideColor]))) + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(int32(style[SpinnerPressedButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(int32(style[SpinnerPressedButtonInsideColor]))) - raylib.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(raylib.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerDefaultSymbolColor]))) - raylib.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(raylib.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerPressedSymbolColor]))) + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", int32(style[GlobalTextFontsize])))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerDefaultSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", int32(style[GlobalTextFontsize])))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerPressedSymbolColor]))) } - raylib.DrawRectangle(labelBoxBound.X, labelBoxBound.Y, labelBoxBound.Width, labelBoxBound.Height, raylib.GetColor(int32(style[SpinnerLabelBorderColor]))) - raylib.DrawRectangle(labelBoxBound.X+1, labelBoxBound.Y+1, labelBoxBound.Width-2, labelBoxBound.Height-2, raylib.GetColor(int32(style[SpinnerLabelInsideColor]))) + rl.DrawRectangle(labelBoxBound.X, labelBoxBound.Y, labelBoxBound.Width, labelBoxBound.Height, rl.GetColor(int32(style[SpinnerLabelBorderColor]))) + rl.DrawRectangle(labelBoxBound.X+1, labelBoxBound.Y+1, labelBoxBound.Width-2, labelBoxBound.Height-2, rl.GetColor(int32(style[SpinnerLabelInsideColor]))) - raylib.DrawText(fmt.Sprintf("%d", value), labelBoxBound.X+(labelBoxBound.Width/2-textWidth/2), labelBoxBound.Y+(labelBoxBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), raylib.GetColor(int32(style[SpinnerPressedTextColor]))) + rl.DrawText(fmt.Sprintf("%d", value), labelBoxBound.X+(labelBoxBound.Width/2-textWidth/2), labelBoxBound.Y+(labelBoxBound.Height/2-(int32(style[GlobalTextFontsize])/2)), int32(style[GlobalTextFontsize]), rl.GetColor(int32(style[SpinnerPressedTextColor]))) break default: break @@ -975,27 +975,27 @@ func Spinner(bounds raylib.Rectangle, value, minValue, maxValue int) int { } // TextBox - Text Box element, updates input text -func TextBox(bounds raylib.Rectangle, text string) string { +func TextBox(bounds rl.Rectangle, text string) string { b := bounds.ToInt32() state := Normal - mousePoint := raylib.GetMousePosition() + mousePoint := rl.GetMousePosition() letter := int32(-1) // Update control - if raylib.CheckCollisionPointRec(mousePoint, bounds) { + if rl.CheckCollisionPointRec(mousePoint, bounds) { state = Focused // NOTE: PRESSED state is not used on this control framesCounter2++ - letter = raylib.GetKeyPressed() + letter = rl.GetKeyPressed() if letter != -1 { if letter >= 32 && letter < 127 { text = fmt.Sprintf("%s%c", text, letter) } } - if raylib.IsKeyPressed(raylib.KeyBackspace) { + if rl.IsKeyPressed(rl.KeyBackspace) { if len(text) > 0 { text = text[:len(text)-1] } @@ -1005,17 +1005,17 @@ func TextBox(bounds raylib.Rectangle, text string) string { // Draw control switch state { case Normal: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[TextboxBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[TextboxBorderWidth]), b.Y+int32(style[TextboxBorderWidth]), b.Width-(int32(style[TextboxBorderWidth])*2), b.Height-(int32(style[TextboxBorderWidth])*2), raylib.GetColor(int32(style[TextboxInsideColor]))) - raylib.DrawText(text, b.X+2, b.Y+int32(style[TextboxBorderWidth])+b.Height/2-int32(style[TextboxTextFontsize])/2, int32(style[TextboxTextFontsize]), raylib.GetColor(int32(style[TextboxTextColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[TextboxBorderColor]))) + rl.DrawRectangle(b.X+int32(style[TextboxBorderWidth]), b.Y+int32(style[TextboxBorderWidth]), b.Width-(int32(style[TextboxBorderWidth])*2), b.Height-(int32(style[TextboxBorderWidth])*2), rl.GetColor(int32(style[TextboxInsideColor]))) + rl.DrawText(text, b.X+2, b.Y+int32(style[TextboxBorderWidth])+b.Height/2-int32(style[TextboxTextFontsize])/2, int32(style[TextboxTextFontsize]), rl.GetColor(int32(style[TextboxTextColor]))) break case Focused: - raylib.DrawRectangle(b.X, b.Y, b.Width, b.Height, raylib.GetColor(int32(style[ToggleActiveBorderColor]))) - raylib.DrawRectangle(b.X+int32(style[TextboxBorderWidth]), b.Y+int32(style[TextboxBorderWidth]), b.Width-(int32(style[TextboxBorderWidth])*2), b.Height-(int32(style[TextboxBorderWidth])*2), raylib.GetColor(int32(style[TextboxInsideColor]))) - raylib.DrawText(text, b.X+2, b.Y+int32(style[TextboxBorderWidth])+b.Height/2-int32(style[TextboxTextFontsize])/2, int32(style[TextboxTextFontsize]), raylib.GetColor(int32(style[TextboxTextColor]))) + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(int32(style[ToggleActiveBorderColor]))) + rl.DrawRectangle(b.X+int32(style[TextboxBorderWidth]), b.Y+int32(style[TextboxBorderWidth]), b.Width-(int32(style[TextboxBorderWidth])*2), b.Height-(int32(style[TextboxBorderWidth])*2), rl.GetColor(int32(style[TextboxInsideColor]))) + rl.DrawText(text, b.X+2, b.Y+int32(style[TextboxBorderWidth])+b.Height/2-int32(style[TextboxTextFontsize])/2, int32(style[TextboxTextFontsize]), rl.GetColor(int32(style[TextboxTextColor]))) - if (framesCounter2/20)%2 == 0 && raylib.CheckCollisionPointRec(mousePoint, bounds) { - raylib.DrawRectangle(b.X+4+raylib.MeasureText(text, int32(style[GlobalTextFontsize])), b.Y+2, 1, b.Height-4, raylib.GetColor(int32(style[TextboxLineColor]))) + if (framesCounter2/20)%2 == 0 && rl.CheckCollisionPointRec(mousePoint, bounds) { + rl.DrawRectangle(b.X+4+rl.MeasureText(text, int32(style[GlobalTextFontsize])), b.Y+2, 1, b.Height-4, rl.GetColor(int32(style[TextboxLineColor]))) } break case Pressed: @@ -1039,9 +1039,9 @@ func SaveGuiStyle(fileName string) { // LoadGuiStyle - Load GUI style file func LoadGuiStyle(fileName string) { - file, err := raylib.OpenAsset(fileName) + file, err := rl.OpenAsset(fileName) if err != nil { - raylib.TraceLog(raylib.LogWarning, "[%s] GUI style file could not be opened", fileName) + rl.TraceLog(rl.LogWarning, "[%s] GUI style file could not be opened", fileName) return } defer file.Close() diff --git a/raylib/audio.c b/raylib/audio.c index 980d3cc..abb536f 100644 --- a/raylib/audio.c +++ b/raylib/audio.c @@ -19,7 +19,7 @@ * Required types and functions are defined in the same module. * * #define USE_OPENAL_BACKEND -* Use OpenAL Soft audio backend usage +* Use OpenAL Soft audio backend * * #define SUPPORT_FILEFORMAT_WAV * #define SUPPORT_FILEFORMAT_OGG @@ -75,20 +75,19 @@ * **********************************************************************************************/ -#include "config.h" - -#if !defined(USE_OPENAL_BACKEND) - #define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. -#endif - #if defined(AUDIO_STANDALONE) #include "audio.h" #include // Required for: va_list, va_start(), vfprintf(), va_end() #else - #include "raylib.h" + #include "config.h" // Defines module configuration flags + #include "raylib.h" // Declares module functions #include "utils.h" // Required for: fopen() Android mapping #endif +#if !defined(USE_OPENAL_BACKEND) + #define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. +#endif + #include "external/mini_al.h" // Implemented in mini_al.c. Cannot implement this here because it conflicts with Win32 APIs such as CloseWindow(), etc. #if !defined(USE_MINI_AL) || (USE_MINI_AL == 0) @@ -166,6 +165,7 @@ typedef enum { MUSIC_AUDIO_OGG = 0, MUSIC_AUDIO_FLAC, + MUSIC_AUDIO_MP3, MUSIC_MODULE_XM, MUSIC_MODULE_MOD } MusicContextType; @@ -179,6 +179,9 @@ typedef struct MusicData { #if defined(SUPPORT_FILEFORMAT_FLAC) drflac *ctxFlac; // FLAC audio context #endif +#if defined(SUPPORT_FILEFORMAT_MP3) + drmp3 ctxMp3; // MP3 audio context +#endif #if defined(SUPPORT_FILEFORMAT_XM) jar_xm_context_t *ctxXm; // XM chiptune context #endif @@ -220,6 +223,9 @@ static Wave LoadOGG(const char *fileName); // Load OGG file #if defined(SUPPORT_FILEFORMAT_FLAC) static Wave LoadFLAC(const char *fileName); // Load FLAC file #endif +#if defined(SUPPORT_FILEFORMAT_MP3) +static Wave LoadMP3(const char *fileName); // Load MP3 file +#endif #if defined(AUDIO_STANDALONE) bool IsFileExtension(const char *fileName, const char *ext); // Check file extension @@ -304,7 +310,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC (void)pDevice; // Mixing is basically just an accumulation. We need to initialize the output buffer to 0. - memset(pFramesOut, 0, frameCount*pDevice->channels*mal_get_sample_size_in_bytes(pDevice->format)); + memset(pFramesOut, 0, frameCount*pDevice->channels*mal_get_bytes_per_sample(pDevice->format)); // Using a mutex here for thread-safety which makes things not real-time. This is unlikely to be necessary for this project, but may // want to consider how you might want to avoid this. @@ -338,11 +344,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS; } - // If we're not looping, we need to make sure we flush the internal buffers of the DSP pipeline to ensure we get the - // last few samples. - bool flushDSP = !audioBuffer->looping; - - mal_uint32 framesJustRead = mal_dsp_read_frames_ex(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, flushDSP); + mal_uint32 framesJustRead = (mal_uint32)mal_dsp_read(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, audioBuffer->dsp.pUserData); if (framesJustRead > 0) { float *framesOut = (float *)pFramesOut + (framesRead*device.channels); @@ -402,7 +404,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; - mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn)*audioBuffer->dsp.config.channelsIn; + mal_uint32 frameSizeInBytes = mal_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn)*audioBuffer->dsp.formatConverterIn.config.channels; // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0. mal_uint32 framesRead = 0; @@ -648,7 +650,7 @@ void SetMasterVolume(float volume) // Create a new audio buffer. Initially filled with silence AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage) { - AudioBuffer *audioBuffer = (AudioBuffer *)calloc(sizeof(*audioBuffer) + (bufferSizeInFrames*channels*mal_get_sample_size_in_bytes(format)), 1); + AudioBuffer *audioBuffer = (AudioBuffer *)calloc(sizeof(*audioBuffer) + (bufferSizeInFrames*channels*mal_get_bytes_per_sample(format)), 1); if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to allocate memory for audio buffer"); @@ -664,10 +666,13 @@ AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint3 dspConfig.channelsOut = DEVICE_CHANNELS; dspConfig.sampleRateIn = sampleRate; dspConfig.sampleRateOut = DEVICE_SAMPLE_RATE; - mal_result resultMAL = mal_dsp_init(&dspConfig, OnAudioBufferDSPRead, audioBuffer, &audioBuffer->dsp); + dspConfig.onRead = OnAudioBufferDSPRead; + dspConfig.pUserData = audioBuffer; + dspConfig.allowDynamicSampleRate = MAL_TRUE; // <-- Required for pitch shifting. + mal_result resultMAL = mal_dsp_init(&dspConfig, &audioBuffer->dsp); if (resultMAL != MAL_SUCCESS) { - TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create data conversion pipeline"); + TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to create data conversion pipeline"); free(audioBuffer); return NULL; } @@ -695,7 +700,7 @@ void DeleteAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + TraceLog(LOG_ERROR, "DeleteAudioBuffer() : No audio buffer"); return; } @@ -708,7 +713,7 @@ bool IsAudioBufferPlaying(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + TraceLog(LOG_ERROR, "IsAudioBufferPlaying() : No audio buffer"); return false; } @@ -736,7 +741,7 @@ void StopAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + TraceLog(LOG_ERROR, "StopAudioBuffer() : No audio buffer"); return; } @@ -755,7 +760,7 @@ void PauseAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer"); return; } @@ -767,7 +772,7 @@ void ResumeAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + TraceLog(LOG_ERROR, "ResumeAudioBuffer() : No audio buffer"); return; } @@ -779,7 +784,7 @@ void SetAudioBufferVolume(AudioBuffer *audioBuffer, float volume) { if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + TraceLog(LOG_ERROR, "SetAudioBufferVolume() : No audio buffer"); return; } @@ -791,7 +796,7 @@ void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch) { if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + TraceLog(LOG_ERROR, "SetAudioBufferPitch() : No audio buffer"); return; } @@ -799,7 +804,7 @@ void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch) // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches // will make the sound faster; lower pitches make it slower. - mal_uint32 newOutputSampleRate = (mal_uint32)((((float)audioBuffer->dsp.config.sampleRateOut / (float)audioBuffer->dsp.config.sampleRateIn) / pitch) * audioBuffer->dsp.config.sampleRateIn); + mal_uint32 newOutputSampleRate = (mal_uint32)((((float)audioBuffer->dsp.src.config.sampleRateOut / (float)audioBuffer->dsp.src.config.sampleRateIn) / pitch) * audioBuffer->dsp.src.config.sampleRateIn); mal_dsp_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate); } @@ -857,6 +862,9 @@ Wave LoadWave(const char *fileName) #endif #if defined(SUPPORT_FILEFORMAT_FLAC) else if (IsFileExtension(fileName, ".flac")) wave = LoadFLAC(fileName); +#endif +#if defined(SUPPORT_FILEFORMAT_MP3) + else if (IsFileExtension(fileName, ".mp3")) wave = LoadMP3(fileName); #endif else TraceLog(LOG_WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName); @@ -915,13 +923,13 @@ Sound LoadSoundFromWave(Wave wave) mal_format formatIn = ((wave.sampleSize == 8) ? mal_format_u8 : ((wave.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); mal_uint32 frameCountIn = wave.sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. - mal_uint32 frameCount = mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn); + mal_uint32 frameCount = (mal_uint32)mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn); if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Failed to get frame count for format conversion"); AudioBuffer* audioBuffer = CreateAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC); if (audioBuffer == NULL) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Failed to create audio buffer"); - frameCount = mal_convert_frames(audioBuffer->buffer, audioBuffer->dsp.config.formatIn, audioBuffer->dsp.config.channelsIn, audioBuffer->dsp.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn); + frameCount = (mal_uint32)mal_convert_frames(audioBuffer->buffer, audioBuffer->dsp.formatConverterIn.config.formatIn, audioBuffer->dsp.formatConverterIn.config.channels, audioBuffer->dsp.src.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn); if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed"); sound.audioBuffer = audioBuffer; @@ -1023,7 +1031,7 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) StopAudioBuffer(audioBuffer); // TODO: May want to lock/unlock this since this data buffer is read at mixing time. - memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.config.channelsIn*mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn)); + memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.formatConverterIn.config.channels*mal_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn)); #else ALint sampleRate, sampleSize, channels; alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); @@ -1049,6 +1057,89 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) #endif } +// Export wave data to file +void ExportWave(Wave wave, const char *fileName) +{ + bool success = false; + + if (IsFileExtension(fileName, ".wav")) + { + // Basic WAV headers structs + typedef struct { + char chunkID[4]; + int chunkSize; + char format[4]; + } RiffHeader; + + typedef struct { + char subChunkID[4]; + int subChunkSize; + short audioFormat; + short numChannels; + int sampleRate; + int byteRate; + short blockAlign; + short bitsPerSample; + } WaveFormat; + + typedef struct { + char subChunkID[4]; + int subChunkSize; + } WaveData; + + RiffHeader riffHeader; + WaveFormat waveFormat; + WaveData waveData; + + // Fill structs with data + riffHeader.chunkID[0] = 'R'; + riffHeader.chunkID[1] = 'I'; + riffHeader.chunkID[2] = 'F'; + riffHeader.chunkID[3] = 'F'; + riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8; + riffHeader.format[0] = 'W'; + riffHeader.format[1] = 'A'; + riffHeader.format[2] = 'V'; + riffHeader.format[3] = 'E'; + + waveFormat.subChunkID[0] = 'f'; + waveFormat.subChunkID[1] = 'm'; + waveFormat.subChunkID[2] = 't'; + waveFormat.subChunkID[3] = ' '; + waveFormat.subChunkSize = 16; + waveFormat.audioFormat = 1; + waveFormat.numChannels = wave.channels; + waveFormat.sampleRate = wave.sampleRate; + waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8; + waveFormat.blockAlign = wave.sampleSize/8; + waveFormat.bitsPerSample = wave.sampleSize; + + waveData.subChunkID[0] = 'd'; + waveData.subChunkID[1] = 'a'; + waveData.subChunkID[2] = 't'; + waveData.subChunkID[3] = 'a'; + waveData.subChunkSize = wave.sampleCount*wave.channels*wave.sampleSize/8; + + FILE *wavFile = fopen(fileName, "wb"); + + if (wavFile == NULL) return; + + fwrite(&riffHeader, 1, sizeof(RiffHeader), wavFile); + fwrite(&waveFormat, 1, sizeof(WaveFormat), wavFile); + fwrite(&waveData, 1, sizeof(WaveData), wavFile); + + fwrite(wave.data, 1, wave.sampleCount*wave.channels*wave.sampleSize/8, wavFile); + + fclose(wavFile); + + success = true; + } + else if (IsFileExtension(fileName, ".raw")) { } // TODO: Support additional file formats to export wave sample data + + if (success) TraceLog(LOG_INFO, "Wave exported successfully: %s", fileName); + else TraceLog(LOG_WARNING, "Wave could not be exported."); +} + // Play a sound void PlaySound(Sound sound) { @@ -1153,7 +1244,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) mal_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. - mal_uint32 frameCount = mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn); + mal_uint32 frameCount = (mal_uint32)mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn); if (frameCount == 0) { TraceLog(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion."); @@ -1162,7 +1253,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) void *data = malloc(frameCount*channels*(sampleSize/8)); - frameCount = mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn); + frameCount = (mal_uint32)mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn); if (frameCount == 0) { TraceLog(LOG_ERROR, "WaveFormat() : Format conversion failed."); @@ -1284,7 +1375,7 @@ Wave WaveCopy(Wave wave) void WaveCrop(Wave *wave, int initSample, int finalSample) { if ((initSample >= 0) && (initSample < finalSample) && - (finalSample > 0) && (finalSample < wave->sampleCount)) + (finalSample > 0) && ((unsigned int)finalSample < wave->sampleCount)) { int sampleCount = finalSample - initSample; @@ -1304,9 +1395,9 @@ float *GetWaveData(Wave wave) { float *samples = (float *)malloc(wave.sampleCount*wave.channels*sizeof(float)); - for (int i = 0; i < wave.sampleCount; i++) + for (unsigned int i = 0; i < wave.sampleCount; i++) { - for (int j = 0; j < wave.channels; j++) + for (unsigned int j = 0; j < wave.channels; j++) { if (wave.sampleSize == 8) samples[wave.channels*i + j] = (float)(((unsigned char *)wave.data)[wave.channels*i + j] - 127)/256.0f; else if (wave.sampleSize == 16) samples[wave.channels*i + j] = (float)((short *)wave.data)[wave.channels*i + j]/32767.0f; @@ -1325,13 +1416,14 @@ float *GetWaveData(Wave wave) Music LoadMusicStream(const char *fileName) { Music music = (MusicData *)malloc(sizeof(MusicData)); + bool musicLoaded = true; if (IsFileExtension(fileName, ".ogg")) { // Open ogg audio stream music->ctxOgg = stb_vorbis_open_filename(fileName, NULL, NULL); - if (music->ctxOgg == NULL) TraceLog(LOG_WARNING, "[%s] OGG audio file could not be opened", fileName); + if (music->ctxOgg == NULL) musicLoaded = false; else { stb_vorbis_info info = stb_vorbis_get_info(music->ctxOgg); // Get Ogg file info @@ -1343,7 +1435,7 @@ Music LoadMusicStream(const char *fileName) music->ctxType = MUSIC_AUDIO_OGG; music->loopCount = -1; // Infinite loop by default - TraceLog(LOG_DEBUG, "[%s] FLAC total samples: %i", fileName, music->totalSamples); + TraceLog(LOG_DEBUG, "[%s] OGG total samples: %i", fileName, music->totalSamples); TraceLog(LOG_DEBUG, "[%s] OGG sample rate: %i", fileName, info.sample_rate); TraceLog(LOG_DEBUG, "[%s] OGG channels: %i", fileName, info.channels); TraceLog(LOG_DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required); @@ -1354,7 +1446,7 @@ Music LoadMusicStream(const char *fileName) { music->ctxFlac = drflac_open_file(fileName); - if (music->ctxFlac == NULL) TraceLog(LOG_WARNING, "[%s] FLAC audio file could not be opened", fileName); + if (music->ctxFlac == NULL) musicLoaded = false; else { music->stream = InitAudioStream(music->ctxFlac->sampleRate, music->ctxFlac->bitsPerSample, music->ctxFlac->channels); @@ -1370,6 +1462,27 @@ Music LoadMusicStream(const char *fileName) } } #endif +#if defined(SUPPORT_FILEFORMAT_MP3) + else if (IsFileExtension(fileName, ".mp3")) + { + drmp3_init_file(&music->ctxMp3, fileName, NULL); + + if (music->ctxMp3.framesRemaining <= 0) musicLoaded = false; + else + { + music->stream = InitAudioStream(music->ctxMp3.sampleRate, 16, music->ctxMp3.channels); + music->totalSamples = (unsigned int)music->ctxMp3.framesRemaining*music->ctxMp3.channels; + music->samplesLeft = music->totalSamples; + music->ctxType = MUSIC_AUDIO_MP3; + music->loopCount = -1; // Infinite loop by default + + TraceLog(LOG_DEBUG, "[%s] MP3 total samples: %i", fileName, music->totalSamples); + TraceLog(LOG_DEBUG, "[%s] MP3 sample rate: %i", fileName, music->ctxMp3.sampleRate); + //TraceLog(LOG_DEBUG, "[%s] MP3 bits per sample: %i", fileName, music->ctxMp3.bitsPerSample); + TraceLog(LOG_DEBUG, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels); + } + } +#endif #if defined(SUPPORT_FILEFORMAT_XM) else if (IsFileExtension(fileName, ".xm")) { @@ -1389,7 +1502,7 @@ Music LoadMusicStream(const char *fileName) TraceLog(LOG_DEBUG, "[%s] XM number of samples: %i", fileName, music->totalSamples); TraceLog(LOG_DEBUG, "[%s] XM track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); } - else TraceLog(LOG_WARNING, "[%s] XM file could not be opened", fileName); + else musicLoaded = false; } #endif #if defined(SUPPORT_FILEFORMAT_MOD) @@ -1408,10 +1521,32 @@ Music LoadMusicStream(const char *fileName) TraceLog(LOG_DEBUG, "[%s] MOD number of samples: %i", fileName, music->samplesLeft); TraceLog(LOG_DEBUG, "[%s] MOD track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); } - else TraceLog(LOG_WARNING, "[%s] MOD file could not be opened", fileName); + else musicLoaded = false; } #endif - else TraceLog(LOG_WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName); + else musicLoaded = false; + + if (!musicLoaded) + { + if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg); + #if defined(SUPPORT_FILEFORMAT_FLAC) + else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac); + #endif + #if defined(SUPPORT_FILEFORMAT_MP3) + else if (music->ctxType == MUSIC_AUDIO_MP3) drmp3_uninit(&music->ctxMp3); + #endif + #if defined(SUPPORT_FILEFORMAT_XM) + else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context(music->ctxXm); + #endif + #if defined(SUPPORT_FILEFORMAT_MOD) + else if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_unload(&music->ctxMod); + #endif + + free(music); + music = NULL; + + TraceLog(LOG_WARNING, "[%s] Music file could not be opened", fileName); + } return music; } @@ -1425,6 +1560,9 @@ void UnloadMusicStream(Music music) #if defined(SUPPORT_FILEFORMAT_FLAC) else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac); #endif +#if defined(SUPPORT_FILEFORMAT_MP3) + else if (music->ctxType == MUSIC_AUDIO_MP3) drmp3_uninit(&music->ctxMp3); +#endif #if defined(SUPPORT_FILEFORMAT_XM) else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context(music->ctxXm); #endif @@ -1516,7 +1654,10 @@ void StopMusicStream(Music music) { case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break; #if defined(SUPPORT_FILEFORMAT_FLAC) - case MUSIC_MODULE_FLAC: /* TODO: Restart FLAC context */ break; + case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break; +#endif +#if defined(SUPPORT_FILEFORMAT_MP3) + case MUSIC_AUDIO_MP3: /* TODO: Restart MP3 context */ break; #endif #if defined(SUPPORT_FILEFORMAT_XM) case MUSIC_MODULE_XM: /* TODO: Restart XM context */ break; @@ -1566,6 +1707,14 @@ void UpdateMusicStream(Music music) } break; #endif + #if defined(SUPPORT_FILEFORMAT_MP3) + case MUSIC_AUDIO_MP3: + { + // NOTE: Returns the number of samples to process + unsigned int numSamplesMp3 = (unsigned int)drmp3_read_f32(&music->ctxMp3, samplesCount*music->stream.channels, (float *)pcm); + + } break; + #endif #if defined(SUPPORT_FILEFORMAT_XM) case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break; #endif @@ -1650,6 +1799,13 @@ void UpdateMusicStream(Music music) } break; #endif + #if defined(SUPPORT_FILEFORMAT_MP3) + case MUSIC_AUDIO_MP3: + { + // NOTE: Returns the number of samples to process + unsigned int numSamplesMp3 = (unsigned int)drmp3_read_f32(&music->ctxMp3, samplesCount*music->stream.channels, (float *)pcm); + } break; + #endif #if defined(SUPPORT_FILEFORMAT_XM) case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break; #endif @@ -2239,6 +2395,33 @@ static Wave LoadFLAC(const char *fileName) } #endif +#if defined(SUPPORT_FILEFORMAT_MP3) +// Load MP3 file into Wave structure +// NOTE: Using dr_mp3 library +static Wave LoadMP3(const char *fileName) +{ + Wave wave; + + // Decode an entire MP3 file in one go + uint64_t totalSampleCount; + drmp3_config *config; + wave.data = drmp3_open_and_decode_file_f32(fileName, config, &totalSampleCount); + + wave.channels = config->outputChannels; + wave.sampleRate = config->outputSampleRate; + wave.sampleCount = (int)totalSampleCount/wave.channels; + wave.sampleSize = 16; + + // NOTE: Only support up to 2 channels (mono, stereo) + if (wave.channels > 2) TraceLog(LOG_WARNING, "[%s] MP3 channels number (%i) not supported", fileName, wave.channels); + + if (wave.data == NULL) TraceLog(LOG_WARNING, "[%s] MP3 data could not be loaded", fileName); + else TraceLog(LOG_INFO, "[%s] MP3 file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); + + return wave; +} +#endif + // Some required functions for audio standalone module version #if defined(AUDIO_STANDALONE) // Check file extension diff --git a/raylib/audio.go b/raylib/audio.go index 1068816..ccc16b7 100644 --- a/raylib/audio.go +++ b/raylib/audio.go @@ -1,6 +1,6 @@ // +build !noaudio -package raylib +package rl /* #include "external/stb_vorbis.c" diff --git a/raylib/camera.go b/raylib/camera.go index 960cf7b..2429b44 100644 --- a/raylib/camera.go +++ b/raylib/camera.go @@ -1,6 +1,6 @@ // +build !android -package raylib +package rl /* #include "raylib.h" diff --git a/raylib/camera.h b/raylib/camera.h index 8067e7b..2ed35f3 100644 --- a/raylib/camera.h +++ b/raylib/camera.h @@ -244,8 +244,8 @@ void SetCameraMode(Camera camera, int mode) distance.y = sqrtf(dx*dx + dy*dy); // Camera angle calculation - cameraAngle.x = asinf(fabsf(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) - cameraAngle.y = -asinf(fabsf(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW) + cameraAngle.x = asinf( (float)fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) + cameraAngle.y = -asinf( (float)fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW) // NOTE: Just testing what cameraAngle means //cameraAngle.x = 0.0f*DEG2RAD; // Camera angle in plane XZ (0 aligned with Z, move positive CCW) diff --git a/raylib/cgo.go b/raylib/cgo.go index 4d85a97..40e4663 100644 --- a/raylib/cgo.go +++ b/raylib/cgo.go @@ -1,6 +1,6 @@ -package raylib +package rl /* -#cgo CFLAGS: -std=gnu99 -Wno-missing-braces -Wno-unused-result +#cgo CFLAGS: -std=gnu99 -Wno-missing-braces -Wno-unused-result -Wno-stringop-overflow */ import "C" diff --git a/raylib/cgo_android.go b/raylib/cgo_android.go index 161695d..a5657ef 100644 --- a/raylib/cgo_android.go +++ b/raylib/cgo_android.go @@ -1,6 +1,6 @@ // +build android -package raylib +package rl /* #include "external/android/native_app_glue/android_native_app_glue.c" diff --git a/raylib/cgo_darwin.go b/raylib/cgo_darwin.go index 8b7bf07..5e1bb56 100644 --- a/raylib/cgo_darwin.go +++ b/raylib/cgo_darwin.go @@ -1,6 +1,6 @@ // +build darwin -package raylib +package rl /* #include "external/glfw/src/context.c" diff --git a/raylib/cgo_linux.go b/raylib/cgo_linux.go index 5c7f502..0b3960b 100644 --- a/raylib/cgo_linux.go +++ b/raylib/cgo_linux.go @@ -1,6 +1,6 @@ // +build linux,!arm,!arm64 -package raylib +package rl /* #include "external/glfw/src/context.c" diff --git a/raylib/cgo_linux_arm.go b/raylib/cgo_linux_arm.go index 1b5f4ee..deb69a0 100644 --- a/raylib/cgo_linux_arm.go +++ b/raylib/cgo_linux_arm.go @@ -1,6 +1,6 @@ // +build linux,arm,!android -package raylib +package rl /* #cgo linux,arm LDFLAGS: -L/opt/vc/lib -L/opt/vc/lib64 -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -lvcos -lvchiq_arm -ldl diff --git a/raylib/cgo_windows.go b/raylib/cgo_windows.go index 6615cc2..08442d4 100644 --- a/raylib/cgo_windows.go +++ b/raylib/cgo_windows.go @@ -1,6 +1,6 @@ // +build windows -package raylib +package rl /* #include "external/glfw/src/context.c" diff --git a/raylib/config.h b/raylib/config.h index 8e6f74c..b4bc561 100644 --- a/raylib/config.h +++ b/raylib/config.h @@ -25,7 +25,7 @@ * **********************************************************************************************/ -#define RAYLIB_VERSION "2.0-dev" +#define RAYLIB_VERSION "2.1-dev" // Edit to control what features Makefile'd raylib is compiled with #if defined(RAYLIB_CMAKE) @@ -86,10 +86,12 @@ //#define SUPPORT_FILEFORMAT_PKM 1 //#define SUPPORT_FILEFORMAT_PVR 1 -// Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... +// Support image export functionality (.png, .bmp, .tga, .jpg) +#define SUPPORT_IMAGE_EXPORT 1 +// Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... // If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT() #define SUPPORT_IMAGE_MANIPULATION 1 -// Support proedural image generation functionality (gradient, spot, perlin-noise, cellular) +// Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) #define SUPPORT_IMAGE_GENERATION 1 @@ -124,7 +126,7 @@ #define SUPPORT_FILEFORMAT_XM 1 #define SUPPORT_FILEFORMAT_MOD 1 //#define SUPPORT_FILEFORMAT_FLAC 1 -//#define SUPPORT_FILEFORMAT_MP3 1 +#define SUPPORT_FILEFORMAT_MP3 1 //------------------------------------------------------------------------------------ @@ -133,10 +135,6 @@ // Show TraceLog() output messages // NOTE: By default LOG_DEBUG traces not shown #define SUPPORT_TRACELOG 1 -// Support saving image data fileformats -// NOTE: Requires stb_image_write library -#define SUPPORT_SAVE_PNG 1 -//#define SUPPORT_SAVE_BMP 1 #endif //defined(RAYLIB_CMAKE) diff --git a/raylib/core.c b/raylib/core.c index 91ff1a9..4f6969c 100644 --- a/raylib/core.c +++ b/raylib/core.c @@ -5,7 +5,7 @@ * PLATFORMS SUPPORTED: * - PLATFORM_DESKTOP: Windows (Win32, Win64) * - PLATFORM_DESKTOP: Linux (X11 desktop mode) -* - PLATFORM_DESKTOP: FreeBSD (X11 desktop) +* - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) * - PLATFORM_DESKTOP: OSX/macOS * - PLATFORM_ANDROID: Android 4.0 (ARM, ARM64) * - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian) @@ -15,7 +15,7 @@ * CONFIGURATION: * * #define PLATFORM_DESKTOP -* Windowing and input system configured for desktop platforms: Windows, Linux, OSX, FreeBSD +* Windowing and input system configured for desktop platforms: Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly * NOTE: Oculus Rift CV1 requires PLATFORM_DESKTOP for mirror rendering - View [rlgl] module to enable it * * #define PLATFORM_ANDROID @@ -57,7 +57,7 @@ * Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() * * DEPENDENCIES: -* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD) +* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly) * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) * camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person) * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) @@ -95,7 +95,9 @@ #define RAYMATH_IMPLEMENTATION // Define external out-of-line implementation of raymath here #include "raymath.h" // Required for: Vector3 and Matrix functions +#define RLGL_IMPLEMENTATION #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 + #include "utils.h" // Required for: fopen() Android mapping #if defined(SUPPORT_GESTURES_SYSTEM) @@ -121,6 +123,7 @@ #include // Required for: strrchr(), strcmp() //#include // Macros for reporting and retrieving error conditions through error codes #include // Required for: tolower() [Used in IsFileExtension()] +#include // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()] #if defined(_WIN32) #include // Required for: _getch(), _chdir() @@ -132,16 +135,24 @@ #define CHDIR chdir #endif -#if defined(__linux__) || defined(PLATFORM_WEB) - #include // Required for: timespec, nanosleep(), select() - POSIX -#elif defined(__APPLE__) - #include // Required for: usleep() -#endif - #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + #if defined(PLATFORM_WEB) + #define GLFW_INCLUDE_ES2 + #endif //#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 #include // GLFW3 library: Windows, OpenGL context and Input management // NOTE: GLFW3 already includes gl.h (OpenGL) headers + + // Support retrieving native window handlers + #if defined(_WIN32) + #define GLFW_EXPOSE_NATIVE_WIN32 + #include // WARNING: It requires customization to avoid windows.h inclusion! + #elif defined(__linux__) + //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type + //GLFW_EXPOSE_NATIVE_WAYLAND + //GLFW_EXPOSE_NATIVE_MIR + #endif + #if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32) // NOTE: Those functions require linking with winmm library @@ -150,6 +161,16 @@ #endif #endif +#if defined(__linux__) || defined(PLATFORM_WEB) + #include // Required for: timespec, nanosleep(), select() - POSIX +#elif defined(__APPLE__) + #include // Required for: usleep() + #include // Required for: objc_msgsend(), sel_registerName() + #define GLFW_EXPOSE_NATIVE_COCOA + #define GLFW_EXPOSE_NATIVE_NSGL + #include // Required for: glfwGetCocoaWindow(), glfwGetNSGLContext() +#endif + #if defined(PLATFORM_ANDROID) //#include // Android sensors functions (accelerometer, gyroscope, light...) #include // Defines AWINDOW_FLAG_FULLSCREEN and others @@ -220,14 +241,46 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- + +// Window/Graphics related variables +//----------------------------------------------------------------------------------- #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static GLFWwindow *window; // Native window (graphic device) #endif - static bool windowReady = false; // Check if window has been initialized successfully static bool windowMinimized = false; // Check if window has been minimized static const char *windowTitle = NULL; // Window text title... +#if defined(__APPLE__) +static int windowNeedsUpdating = 2; // Times the Cocoa window needs to be updated initially +#endif + +static unsigned int displayWidth, displayHeight;// Display width and height (monitor, device-screen, LCD, ...) +static int screenWidth, screenHeight; // Screen width and height (used render area) +static int renderWidth, renderHeight; // Framebuffer width and height (render area, including black bars if required) +static int renderOffsetX = 0; // Offset X from render area (must be divided by 2) +static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2) +static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) +static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) + +#if defined(PLATFORM_RPI) +static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device) +#endif + +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) +static EGLDisplay display; // Native display device (physical screen connection) +static EGLSurface surface; // Surface to draw on, framebuffers (connected to context) +static EGLContext context; // Graphic context, mode in which drawing can be done +static EGLConfig config; // Graphic config +static uint64_t baseTime; // Base time measure for hi-res timer +static bool windowShouldClose = false; // Flag to set window for closing +#endif + +#if defined(PLATFORM_UWP) +extern EGLNativeWindowType uwpWindow; // Native EGL window handler for UWP (external, defined in UWP App) +#endif +//----------------------------------------------------------------------------------- + #if defined(PLATFORM_ANDROID) static struct android_app *androidApp; // Android activity static struct android_poll_source *source; // Android events polling source @@ -238,104 +291,86 @@ static bool appEnabled = true; // Used to detec if app is activ static bool contextRebindRequired = false; // Used to know context rebind required #endif -#if defined(PLATFORM_RPI) -static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device) +// Inputs related variables +//----------------------------------------------------------------------------------- +// Keyboard states +static char previousKeyState[512] = { 0 }; // Registers previous frame key state +static char currentKeyState[512] = { 0 }; // Registers current frame key state +static int lastKeyPressed = -1; // Register last key pressed +static int exitKey = KEY_ESCAPE; // Default exit key (ESC) -// Keyboard input variables +#if defined(PLATFORM_RPI) // NOTE: For keyboard we will use the standard input (but reconfigured...) static struct termios defaultKeyboardSettings; // Used to store default keyboard settings static int defaultKeyboardMode; // Used to store default keyboard mode +#endif -// Mouse input variables +// Mouse states +static Vector2 mousePosition; // Mouse position on screen +static float mouseScale = 1.0f; // Mouse default scale +static bool cursorHidden = false; // Track if cursor is hidden +static bool cursorOnScreen = false; // Tracks if cursor is inside client area +static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP) +static char previousMouseState[3] = { 0 }; // Registers previous mouse button state +static char currentMouseState[3] = { 0 }; // Registers current mouse button state +static int previousMouseWheelY = 0; // Registers previous mouse wheel variation +static int currentMouseWheelY = 0; // Registers current mouse wheel variation +#endif + +#if defined(PLATFORM_RPI) static int mouseStream = -1; // Mouse device file descriptor static bool mouseReady = false; // Flag to know if mouse is ready static pthread_t mouseThreadId; // Mouse reading thread id - -// Touch input variables static int touchStream = -1; // Touch device file descriptor static bool touchReady = false; // Flag to know if touch interface is ready static pthread_t touchThreadId; // Touch reading thread id - -// Gamepad input variables -static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor -static pthread_t gamepadThreadId; // Gamepad reading thread id -static char gamepadName[64]; // Gamepad name holder +#endif +#if defined(PLATFORM_WEB) +static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) -static EGLDisplay display; // Native display device (physical screen connection) -static EGLSurface surface; // Surface to draw on, framebuffers (connected to context) -static EGLContext context; // Graphic context, mode in which drawing can be done -static EGLConfig config; // Graphic config -static uint64_t baseTime; // Base time measure for hi-res timer -static bool windowShouldClose = false; // Flag to set window for closing -#endif - -#if defined(PLATFORM_UWP) -extern EGLNativeWindowType uwpWindow; // Native EGL window handler for UWP (external, defined in UWP App) -#endif - -// Screen related variables -static unsigned int displayWidth, displayHeight; // Display width and height (monitor, device-screen, LCD, ...) -static int screenWidth, screenHeight; // Screen width and height (used render area) -static int renderWidth, renderHeight; // Framebuffer width and height (render area, including black bars if required) -static int renderOffsetX = 0; // Offset X from render area (must be divided by 2) -static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2) -static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) -static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) - -static bool cursorHidden = false; // Track if cursor is hidden -static bool cursorOnScreen = false; // Tracks if cursor is inside client area +// Gamepads states +static int lastGamepadButtonPressed = -1; // Register last gamepad button pressed +static int gamepadAxisCount = 0; // Register number of available gamepad axis #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP) -// Register mouse states -static char previousMouseState[3] = { 0 }; // Registers previous mouse button state -static char currentMouseState[3] = { 0 }; // Registers current mouse button state -static int previousMouseWheelY = 0; // Registers previous mouse wheel variation -static int currentMouseWheelY = 0; // Registers current mouse wheel variation - -// Register gamepads states static bool gamepadReady[MAX_GAMEPADS] = { false }; // Flag to know if gamepad is ready static float gamepadAxisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state static char previousGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state static char currentGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state - -// Keyboard configuration -static int exitKey = KEY_ESCAPE; // Default exit key (ESC) #endif -// Register keyboard states -static char previousKeyState[512] = { 0 }; // Registers previous frame key state -static char currentKeyState[512] = { 0 }; // Registers current frame key state - -static int lastKeyPressed = -1; // Register last key pressed -static int lastGamepadButtonPressed = -1; // Register last gamepad button pressed -static int gamepadAxisCount = 0; // Register number of available gamepad axis - -static Vector2 mousePosition; // Mouse position on screen -static float mouseScale = 1.0f; // Mouse default scale - -#if defined(PLATFORM_WEB) -static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click -#endif - -static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen - -#if defined(PLATFORM_DESKTOP) -static char **dropFilesPath; // Store dropped files paths as strings -static int dropFilesCount = 0; // Count stored strings +#if defined(PLATFORM_RPI) +static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor +static pthread_t gamepadThreadId; // Gamepad reading thread id +static char gamepadName[64]; // Gamepad name holder #endif +//----------------------------------------------------------------------------------- +// Timming system variables +//----------------------------------------------------------------------------------- static double currentTime = 0.0; // Current time measure static double previousTime = 0.0; // Previous time measure static double updateTime = 0.0; // Time measure for frame update static double drawTime = 0.0; // Time measure for frame draw static double frameTime = 0.0; // Time measure for one frame static double targetTime = 0.0; // Desired time for one frame, if 0 not applied +//----------------------------------------------------------------------------------- +// Config internal variables +//----------------------------------------------------------------------------------- static unsigned char configFlags = 0; // Configuration flags (bit based) static bool showLogo = false; // Track if showing logo at init is enabled +#if defined(PLATFORM_DESKTOP) +static char **dropFilesPath; // Store dropped files paths as strings +static int dropFilesCount = 0; // Count dropped files strings +#endif +static char **dirFilesPath; // Store directory files paths as strings +static int dirFilesCount = 0; // Count directory files strings + #if defined(SUPPORT_SCREEN_CAPTURE) static int screenshotCounter = 0; // Screenshots counter #endif @@ -344,6 +379,7 @@ static int screenshotCounter = 0; // Screenshots counter static int gifFramesCounter = 0; // GIF frames counter static bool gifRecording = false; // GIF recording state #endif +//----------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by core) @@ -357,15 +393,18 @@ extern void UnloadDefaultFont(void); // [Module: text] Unloads default fo // Module specific Functions Declaration //---------------------------------------------------------------------------------- static bool InitGraphicsDevice(int width, int height); // Initialize graphics device -static void SetupFramebufferSize(int displayWidth, int displayHeight); +static void SetupFramebuffer(int width, int height); // Setup main framebuffer +static void SetupViewport(void); // Set viewport parameters +static void SwapBuffers(void); // Copy back buffer to front buffers + static void InitTimer(void); // Initialize timer static void Wait(float ms); // Wait for some milliseconds (stop program execution) + static bool GetKeyStatus(int key); // Returns if a key has been pressed static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed static void PollInputEvents(void); // Register user events -static void SwapBuffers(void); // Copy back buffer to front buffers + static void LogoAnimation(void); // Plays raylib logo appearing animation -static void SetupViewport(void); // Set viewport parameters #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error @@ -378,7 +417,6 @@ static void CursorEnterCallback(GLFWwindow *window, int enter); static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored #endif - #if defined(PLATFORM_DESKTOP) static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window #endif @@ -634,7 +672,17 @@ bool IsWindowReady(void) // Check if KEY_ESCAPE pressed or Close icon pressed bool WindowShouldClose(void) { -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) +#if defined(PLATFORM_WEB) + // Emterpreter-Async required to run sync code + // https://github.com/kripken/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code + // By default, this function is never called on a web-ready raylib example because we encapsulate + // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously + // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter! + emscripten_sleep(16); + return false; +#endif + +#if defined(PLATFORM_DESKTOP) if (windowReady) { // While window minimized, stop loop execution @@ -757,6 +805,124 @@ int GetScreenHeight(void) return screenHeight; } +// Get native window handle +void *GetWindowHandle(void) +{ +#if defined(_WIN32) + // NOTE: Returned handle is: void *HWND (windows.h) + return glfwGetWin32Window(window); +#elif defined(__linux__) + // NOTE: Returned handle is: unsigned long Window (X.h) + // typedef unsigned long XID; + // typedef XID Window; + //unsigned long id = (unsigned long)glfwGetX11Window(window); + return NULL; // TODO: Find a way to return value... cast to void *? +#elif defined(__APPLE__) + // NOTE: Returned handle is: void *id + return glfwGetCocoaWindow(window); +#else + return NULL; +#endif +} + +// Get number of monitors +int GetMonitorCount(void) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + glfwGetMonitors(&monitorCount); + return monitorCount; +#else + return 1; +#endif +} + +// Get primary monitor width +int GetMonitorWidth(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + return mode->width; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get primary monitor width +int GetMonitorHeight(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + return mode->height; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get primary montior physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + int physicalWidth; + glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL); + return physicalWidth; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get primary monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + int physicalHeight; + glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight); + return physicalHeight; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the primary monitor +const char *GetMonitorName(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + return glfwGetMonitorName(monitors[monitor]); + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return ""; +} + // Show mouse cursor void ShowCursor() { @@ -873,7 +1039,7 @@ void EndDrawing(void) // Wait for some milliseconds... if (frameTime < targetTime) { - Wait((targetTime - frameTime)*1000.0f); + Wait((float)(targetTime - frameTime)*1000.0f); currentTime = GetTime(); double extraTime = currentTime - previousTime; @@ -1028,7 +1194,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) // Calculate view matrix from camera look at Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); - Matrix matProj; + Matrix matProj = MatrixIdentity(); if (camera.type == CAMERA_PERSPECTIVE) { @@ -1040,6 +1206,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) float aspect = (float)screenWidth/(float)screenHeight; double top = camera.fovy/2.0; double right = top*aspect; + // Calculate projection matrix from orthographic matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0); } @@ -1069,18 +1236,19 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) Vector2 GetWorldToScreen(Vector3 position, Camera camera) { // Calculate projection matrix (from perspective instead of frustum - Matrix matProj; + Matrix matProj = MatrixIdentity(); - if(camera.type == CAMERA_PERSPECTIVE) + if (camera.type == CAMERA_PERSPECTIVE) { // Calculate projection matrix from perspective matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)GetScreenWidth()/(double)GetScreenHeight()), 0.01, 1000.0); } - else if(camera.type == CAMERA_ORTHOGRAPHIC) + else if (camera.type == CAMERA_ORTHOGRAPHIC) { float aspect = (float)screenWidth/(float)screenHeight; double top = camera.fovy/2.0; double right = top*aspect; + // Calculate projection matrix from orthographic matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0); } @@ -1282,7 +1450,9 @@ void TakeScreenshot(const char *fileName) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) unsigned char *imgData = rlReadScreenPixels(renderWidth, renderHeight); - SavePNG(fileName, imgData, renderWidth, renderHeight, 4); // Save image as PNG + + Image image = { imgData, renderWidth, renderHeight, 1, UNCOMPRESSED_R8G8B8A8 }; + ExportImage(image, fileName); free(imgData); TraceLog(LOG_INFO, "Screenshot taken: %s", fileName); @@ -1312,6 +1482,7 @@ bool IsFileExtension(const char *fileName, const char *ext) } } } + else result = false; #else if (strcmp(fileExt, ext) == 0) result = true; #endif @@ -1330,11 +1501,19 @@ const char *GetExtension(const char *fileName) return (dot + 1); } +// String pointer reverse break: returns right-most occurrence of charset in s +static const char *strprbrk(const char *s, const char *charset) +{ + const char *latestMatch = NULL; + for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { } + return latestMatch; +} + // Get pointer to filename for a path string const char *GetFileName(const char *filePath) { - const char *fileName = strrchr(filePath, '\\'); - + const char *fileName = strprbrk(filePath, "\\/"); + if (!fileName || fileName == filePath) return filePath; return fileName + 1; @@ -1344,14 +1523,16 @@ const char *GetFileName(const char *filePath) // Get directory for a given fileName (with path) const char *GetDirectoryPath(const char *fileName) { - char *lastSlash = NULL; + const char *lastSlash = NULL; static char filePath[256]; // MAX_DIRECTORY_PATH_SIZE = 256 memset(filePath, 0, 256); - - lastSlash = strrchr(fileName, '\\'); + + lastSlash = strprbrk(fileName, "\\/"); + if (!lastSlash) return NULL; + strncpy(filePath, fileName, strlen(fileName) - (strlen(lastSlash) - 1)); filePath[strlen(fileName) - strlen(lastSlash)] = '\0'; - + return filePath; } @@ -1366,6 +1547,57 @@ const char *GetWorkingDirectory(void) return currentDir; } +// Get filenames in a directory path (max 256 files) +// NOTE: Files count is returned by parameters pointer +char **GetDirectoryFiles(const char *dirPath, int *fileCount) +{ + #define MAX_FILEPATH_LENGTH 256 + #define MAX_DIRECTORY_FILES 512 + + ClearDirectoryFiles(); + + // Memory allocation for MAX_DIRECTORY_FILES + dirFilesPath = (char **)malloc(sizeof(char *)*MAX_DIRECTORY_FILES); + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH); + + int counter = 0; + struct dirent *ent; + DIR *dir = opendir(dirPath); + + if (dir != NULL) // It's a directory + { + // TODO: Reading could be done in two passes, + // first one to count files and second one to read names + // That way we can allocate required memory, instead of a limited pool + + while ((ent = readdir(dir)) != NULL) + { + strcpy(dirFilesPath[counter], ent->d_name); + counter++; + } + + closedir(dir); + } + else TraceLog(LOG_WARNING, "Can not open directory...\n"); // Maybe it's a file... + + dirFilesCount = counter; + *fileCount = dirFilesCount; + + return dirFilesPath; +} + +// Clear directory files paths buffers +void ClearDirectoryFiles(void) +{ + if (dirFilesCount > 0) + { + for (int i = 0; i < dirFilesCount; i++) free(dirFilesPath[i]); + + free(dirFilesPath); + dirFilesCount = 0; + } +} + // Change working directory, returns true if success bool ChangeDirectory(const char *dir) { @@ -1853,6 +2085,10 @@ static bool InitGraphicsDevice(int width, int height) #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSetErrorCallback(ErrorCallback); +#if defined(__APPLE__) + glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); +#endif + if (!glfwInit()) { TraceLog(LOG_WARNING, "Failed to initialize GLFW"); @@ -1883,30 +2119,29 @@ static bool InitGraphicsDevice(int width, int height) displayHeight = screenHeight; #endif // defined(PLATFORM_WEB) - glfwDefaultWindowHints(); // Set default windows hints + glfwDefaultWindowHints(); // Set default windows hints: + //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits + //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits + //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits + //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits + //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits + //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window + //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API + //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers // Check some Window creation flags - if (configFlags & FLAG_WINDOW_RESIZABLE) glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // Resizable window + if (configFlags & FLAG_WINDOW_RESIZABLE) glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // Resizable window else glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable - if (configFlags & FLAG_WINDOW_DECORATED) glfwWindowHint(GLFW_DECORATED, GL_TRUE); // Border and buttons on Window - - if (configFlags & FLAG_WINDOW_TRANSPARENT) - { - // TODO: Enable transparent window (not ready yet on GLFW 3.2) - } + if (configFlags & FLAG_WINDOW_UNDECORATED) glfwWindowHint(GLFW_DECORATED, GL_FALSE); // Border and buttons on Window + else glfwWindowHint(GLFW_DECORATED, GL_TRUE); // Decorated window + // FLAG_WINDOW_TRANSPARENT not supported on HTML5 and not included in any released GLFW version yet +#if defined(GLFW_TRANSPARENT_FRAMEBUFFER) + if (configFlags & FLAG_WINDOW_TRANSPARENT) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer + else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer +#endif - if (configFlags & FLAG_MSAA_4X_HINT) - { - glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 - TraceLog(LOG_INFO, "Trying to enable MSAA x4"); - } - - //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits - //glfwWindowHint(GLFW_DEPTH_BITS, 16); // Depthbuffer bits (24 by default) - //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window - //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // Default OpenGL API to use. Alternative: GLFW_OPENGL_ES_API - //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers + if (configFlags & FLAG_MSAA_4X_HINT) glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version // with forward compatibility to older OpenGL versions. @@ -1925,11 +2160,11 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! // Other values: GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE #if defined(__APPLE__) - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // OSX Requires + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // OSX Requires fordward compatibility #else glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); // Fordward Compatibility Hint: Only 3.3 and above! #endif - //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); + //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); // Request OpenGL DEBUG context } if (fullscreen) @@ -1962,7 +2197,7 @@ static bool InitGraphicsDevice(int width, int height) // At this point we need to manage render size vs screen size // NOTE: This function uses and modifies global module variables: // screenWidth/screenHeight - renderWidth/renderHeight - downscaleView - SetupFramebufferSize(displayWidth, displayHeight); + SetupFramebuffer(displayWidth, displayHeight); window = glfwCreateWindow(displayWidth, displayHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); @@ -2196,7 +2431,7 @@ static bool InitGraphicsDevice(int width, int height) } } - //SetupFramebufferSize(displayWidth, displayHeight); + //SetupFramebuffer(displayWidth, displayHeight); EGLint numConfigs = 0; if ((eglChooseConfig(display, framebufferAttribs, &config, 1, &numConfigs) == EGL_FALSE) || (numConfigs == 0)) @@ -2302,7 +2537,7 @@ static bool InitGraphicsDevice(int width, int height) // At this point we need to manage render size vs screen size // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView - SetupFramebufferSize(displayWidth, displayHeight); + SetupFramebuffer(displayWidth, displayHeight); ANativeWindow_setBuffersGeometry(androidApp->window, renderWidth, renderHeight, displayFormat); //ANativeWindow_setBuffersGeometry(androidApp->window, 0, 0, displayFormat); // Force use of native display size @@ -2315,7 +2550,7 @@ static bool InitGraphicsDevice(int width, int height) // At this point we need to manage render size vs screen size // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView - SetupFramebufferSize(displayWidth, displayHeight); + SetupFramebuffer(displayWidth, displayHeight); dstRect.x = 0; dstRect.y = 0; @@ -2417,7 +2652,7 @@ static void SetupViewport(void) // Compute framebuffer size relative to screen size and display size // NOTE: Global variables renderWidth/renderHeight and renderOffsetX/renderOffsetY can be modified -static void SetupFramebufferSize(int displayWidth, int displayHeight) +static void SetupFramebuffer(int width, int height) { // Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var) if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) @@ -2491,7 +2726,7 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight) // Initialize hi-resolution timer static void InitTimer(void) { - srand(time(NULL)); // Initialize random seed + srand((unsigned int)time(NULL)); // Initialize random seed #if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) @@ -2741,6 +2976,15 @@ static void SwapBuffers(void) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSwapBuffers(window); +#if __APPLE__ + // workaround for missing/erroneous initial rendering on macOS + if (windowNeedsUpdating) { + // Desugared version of Objective C: [glfwGetNSGLContext(window) update] + ((id (*)(id, SEL))objc_msgSend)(glfwGetNSGLContext(window), + sel_registerName("update")); + windowNeedsUpdating--; + } +#endif #endif #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) @@ -3079,7 +3323,8 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Android: Get input events static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) { - //http://developer.android.com/ndk/reference/index.html + // If additional inputs are required check: + // https://developer.android.com/ndk/reference/group/input int type = AInputEvent_getType(event); @@ -3092,6 +3337,23 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) // Get second touch position touchPosition[1].x = AMotionEvent_getX(event, 1); touchPosition[1].y = AMotionEvent_getY(event, 1); + + // Useful functions for gamepad inputs: + //AMotionEvent_getAction() + //AMotionEvent_getAxisValue() + //AMotionEvent_getButtonState() + + // Gamepad dpad button presses capturing + // TODO: That's weird, key input (or button) + // shouldn't come as a TYPE_MOTION event... + int32_t keycode = AKeyEvent_getKeyCode(event); + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + currentKeyState[keycode] = 1; // Key down + lastKeyPressed = keycode; + } + else currentKeyState[keycode] = 0; // Key up + } else if (type == AINPUT_EVENT_TYPE_KEY) { @@ -3100,9 +3362,9 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) // Save current button and its state // NOTE: Android key action is 0 for down and 1 for up - if (AKeyEvent_getAction(event) == 0) + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) { - currentKeyState[keycode] = 1; // Key down + currentKeyState[keycode] = 1; // Key down lastKeyPressed = keycode; } else currentKeyState[keycode] = 0; // Key up @@ -3140,26 +3402,32 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_MOVE; // Register touch points count + // NOTE: Documentation says pointerCount is Always >= 1, + // but in practice it can be 0 or over a million gestureEvent.pointCount = AMotionEvent_getPointerCount(event); - // Register touch points id - gestureEvent.pointerId[0] = AMotionEvent_getPointerId(event, 0); - gestureEvent.pointerId[1] = AMotionEvent_getPointerId(event, 1); + // Only enable gestures for 1-3 touch points + if ((gestureEvent.pointCount > 0) && (gestureEvent.pointCount < 4)) + { + // Register touch points id + // NOTE: Only two points registered + gestureEvent.pointerId[0] = AMotionEvent_getPointerId(event, 0); + gestureEvent.pointerId[1] = AMotionEvent_getPointerId(event, 1); - // Register touch points position - // NOTE: Only two points registered - gestureEvent.position[0] = (Vector2){ AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0) }; - gestureEvent.position[1] = (Vector2){ AMotionEvent_getX(event, 1), AMotionEvent_getY(event, 1) }; + // Register touch points position + gestureEvent.position[0] = (Vector2){ AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0) }; + gestureEvent.position[1] = (Vector2){ AMotionEvent_getX(event, 1), AMotionEvent_getY(event, 1) }; - // Normalize gestureEvent.position[x] for screenWidth and screenHeight - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); + // Normalize gestureEvent.position[x] for screenWidth and screenHeight + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + gestureEvent.position[1].x /= (float)GetScreenWidth(); + gestureEvent.position[1].y /= (float)GetScreenHeight(); - gestureEvent.position[1].x /= (float)GetScreenWidth(); - gestureEvent.position[1].y /= (float)GetScreenHeight(); - - // Gesture data is sent to gestures system for processing - ProcessGestureEvent(gestureEvent); + // Gesture data is sent to gestures system for processing + ProcessGestureEvent(gestureEvent); + } #else // Support only simple touch position diff --git a/raylib/core.go b/raylib/core.go index ce807c5..b17f75d 100644 --- a/raylib/core.go +++ b/raylib/core.go @@ -1,4 +1,4 @@ -package raylib +package rl /* #include "raylib.h" @@ -95,43 +95,90 @@ func SetWindowTitle(title string) { } // SetWindowPosition - Set window position on screen (only PLATFORM_DESKTOP) -func SetWindowPosition(x, y int32) { +func SetWindowPosition(x, y int) { cx := (C.int)(x) cy := (C.int)(y) C.SetWindowPosition(cx, cy) } // SetWindowMonitor - Set monitor for the current window (fullscreen mode) -func SetWindowMonitor(monitor int32) { +func SetWindowMonitor(monitor int) { cmonitor := (C.int)(monitor) C.SetWindowMonitor(cmonitor) } // SetWindowMinSize - Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) -func SetWindowMinSize(w, h int32) { +func SetWindowMinSize(w, h int) { cw := (C.int)(w) ch := (C.int)(h) C.SetWindowMinSize(cw, ch) } // SetWindowSize - Set window dimensions -func SetWindowSize(w, h int32) { +func SetWindowSize(w, h int) { cw := (C.int)(w) ch := (C.int)(h) C.SetWindowSize(cw, ch) } // GetScreenWidth - Get current screen width -func GetScreenWidth() int32 { +func GetScreenWidth() int { ret := C.GetScreenWidth() - v := (int32)(ret) + v := (int)(ret) return v } // GetScreenHeight - Get current screen height -func GetScreenHeight() int32 { +func GetScreenHeight() int { ret := C.GetScreenHeight() - v := (int32)(ret) + v := (int)(ret) + return v +} + +// GetMonitorCount - Get number of connected monitors +func GetMonitorCount() int { + ret := C.GetMonitorCount() + v := (int)(ret) + return v +} + +// GetMonitorWidth - Get primary monitor width +func GetMonitorWidth(monitor int) int { + cmonitor := (C.int)(monitor) + ret := C.GetMonitorWidth(cmonitor) + v := (int)(ret) + return v +} + +// GetMonitorHeight - Get primary monitor height +func GetMonitorHeight(monitor int) int { + cmonitor := (C.int)(monitor) + ret := C.GetMonitorHeight(cmonitor) + v := (int)(ret) + return v +} + +// GetMonitorPhysicalWidth - Get primary monitor physical width in millimetres +func GetMonitorPhysicalWidth(monitor int) int { + cmonitor := (C.int)(monitor) + ret := C.GetMonitorPhysicalWidth(cmonitor) + v := (int)(ret) + return v +} + +// GetMonitorPhysicalHeight - Get primary monitor physical height in millimetres +func GetMonitorPhysicalHeight(monitor int) int { + cmonitor := (C.int)(monitor) + ret := C.GetMonitorPhysicalHeight(cmonitor) + v := (int)(ret) + return v +} + +// GetMonitorName - Get the human-readable, UTF-8 encoded name of the primary monitor +func GetMonitorName(monitor int) string { + cmonitor := (C.int)(monitor) + ret := C.GetMonitorName(cmonitor) + v := C.GoString(ret) return v } @@ -275,7 +322,7 @@ func ColorNormalize(color Color) Vector4 { // Vector3ToFloat - Converts Vector3 to float32 slice func Vector3ToFloat(vec Vector3) []float32 { - data := make([]float32, 3) + data := make([]float32, 0) data[0] = vec.X data[1] = vec.Y data[2] = vec.Z @@ -285,7 +332,7 @@ func Vector3ToFloat(vec Vector3) []float32 { // MatrixToFloat - Converts Matrix to float32 slice func MatrixToFloat(mat Matrix) []float32 { - data := make([]float32, 16) + data := make([]float32, 0) data[0] = mat.M0 data[1] = mat.M4 diff --git a/raylib/external/alsa/alisp.h b/raylib/external/alsa/alisp.h deleted file mode 100644 index 407ed64..0000000 --- a/raylib/external/alsa/alisp.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * ALSA lisp implementation - * Copyright (c) 2003 by Jaroslav Kysela - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -struct alisp_cfg { - int verbose: 1, - warning: 1, - debug: 1; - snd_input_t *in; /* program code */ - snd_output_t *out; /* program output */ - snd_output_t *eout; /* error output */ - snd_output_t *vout; /* verbose output */ - snd_output_t *wout; /* warning output */ - snd_output_t *dout; /* debug output */ -}; - -struct alisp_instance; -struct alisp_object; -struct alisp_seq_iterator; - -struct alisp_cfg *alsa_lisp_default_cfg(snd_input_t *input); -void alsa_lisp_default_cfg_free(struct alisp_cfg *cfg); -int alsa_lisp(struct alisp_cfg *cfg, struct alisp_instance **instance); -void alsa_lisp_free(struct alisp_instance *instance); -int alsa_lisp_function(struct alisp_instance *instance, struct alisp_seq_iterator **result, - const char *id, const char *args, ...) -#ifndef DOC_HIDDEN - __attribute__ ((format (printf, 4, 5))) -#endif - ; -void alsa_lisp_result_free(struct alisp_instance *instance, - struct alisp_seq_iterator *result); -int alsa_lisp_seq_first(struct alisp_instance *instance, const char *id, - struct alisp_seq_iterator **seq); -int alsa_lisp_seq_next(struct alisp_seq_iterator **seq); -int alsa_lisp_seq_count(struct alisp_seq_iterator *seq); -int alsa_lisp_seq_integer(struct alisp_seq_iterator *seq, long *val); -int alsa_lisp_seq_pointer(struct alisp_seq_iterator *seq, const char *ptr_id, void **ptr); diff --git a/raylib/external/alsa/asoundef.h b/raylib/external/alsa/asoundef.h deleted file mode 100644 index c6c4eec..0000000 --- a/raylib/external/alsa/asoundef.h +++ /dev/null @@ -1,310 +0,0 @@ -/** - * \file include/asoundef.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Definitions of constants for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_ASOUNDEF_H -#define __ALSA_ASOUNDEF_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Digital_Audio_Interface Constants for Digital Audio Interfaces - * AES/IEC958 channel status bits. - * \{ - */ - -#define IEC958_AES0_PROFESSIONAL (1<<0) /**< 0 = consumer, 1 = professional */ -#define IEC958_AES0_NONAUDIO (1<<1) /**< 0 = audio, 1 = non-audio */ -#define IEC958_AES0_PRO_EMPHASIS (7<<2) /**< mask - emphasis */ -#define IEC958_AES0_PRO_EMPHASIS_NOTID (0<<2) /**< emphasis not indicated */ -#define IEC958_AES0_PRO_EMPHASIS_NONE (1<<2) /**< no emphasis */ -#define IEC958_AES0_PRO_EMPHASIS_5015 (3<<2) /**< 50/15us emphasis */ -#define IEC958_AES0_PRO_EMPHASIS_CCITT (7<<2) /**< CCITT J.17 emphasis */ -#define IEC958_AES0_PRO_FREQ_UNLOCKED (1<<5) /**< source sample frequency: 0 = locked, 1 = unlocked */ -#define IEC958_AES0_PRO_FS (3<<6) /**< mask - sample frequency */ -#define IEC958_AES0_PRO_FS_NOTID (0<<6) /**< fs not indicated */ -#define IEC958_AES0_PRO_FS_44100 (1<<6) /**< 44.1kHz */ -#define IEC958_AES0_PRO_FS_48000 (2<<6) /**< 48kHz */ -#define IEC958_AES0_PRO_FS_32000 (3<<6) /**< 32kHz */ -#define IEC958_AES0_CON_NOT_COPYRIGHT (1<<2) /**< 0 = copyright, 1 = not copyright */ -#define IEC958_AES0_CON_EMPHASIS (7<<3) /**< mask - emphasis */ -#define IEC958_AES0_CON_EMPHASIS_NONE (0<<3) /**< no emphasis */ -#define IEC958_AES0_CON_EMPHASIS_5015 (1<<3) /**< 50/15us emphasis */ -#define IEC958_AES0_CON_MODE (3<<6) /**< mask - mode */ -#define IEC958_AES1_PRO_MODE (15<<0) /**< mask - channel mode */ -#define IEC958_AES1_PRO_MODE_NOTID (0<<0) /**< mode not indicated */ -#define IEC958_AES1_PRO_MODE_STEREOPHONIC (2<<0) /**< stereophonic - ch A is left */ -#define IEC958_AES1_PRO_MODE_SINGLE (4<<0) /**< single channel */ -#define IEC958_AES1_PRO_MODE_TWO (8<<0) /**< two channels */ -#define IEC958_AES1_PRO_MODE_PRIMARY (12<<0) /**< primary/secondary */ -#define IEC958_AES1_PRO_MODE_BYTE3 (15<<0) /**< vector to byte 3 */ -#define IEC958_AES1_PRO_USERBITS (15<<4) /**< mask - user bits */ -#define IEC958_AES1_PRO_USERBITS_NOTID (0<<4) /**< user bits not indicated */ -#define IEC958_AES1_PRO_USERBITS_192 (8<<4) /**< 192-bit structure */ -#define IEC958_AES1_PRO_USERBITS_UDEF (12<<4) /**< user defined application */ -#define IEC958_AES1_CON_CATEGORY 0x7f /**< consumer category */ -#define IEC958_AES1_CON_GENERAL 0x00 /**< general category */ -#define IEC958_AES1_CON_LASEROPT_MASK 0x07 /**< Laser-optical mask */ -#define IEC958_AES1_CON_LASEROPT_ID 0x01 /**< Laser-optical ID */ -#define IEC958_AES1_CON_IEC908_CD (IEC958_AES1_CON_LASEROPT_ID|0x00) /**< IEC958 CD compatible device */ -#define IEC958_AES1_CON_NON_IEC908_CD (IEC958_AES1_CON_LASEROPT_ID|0x08) /**< non-IEC958 CD compatible device */ -#define IEC958_AES1_CON_MINI_DISC (IEC958_AES1_CON_LASEROPT_ID|0x48) /**< Mini-Disc device */ -#define IEC958_AES1_CON_DVD (IEC958_AES1_CON_LASEROPT_ID|0x18) /**< DVD device */ -#define IEC958_AES1_CON_LASTEROPT_OTHER (IEC958_AES1_CON_LASEROPT_ID|0x78) /**< Other laser-optical product */ -#define IEC958_AES1_CON_DIGDIGCONV_MASK 0x07 /**< digital<->digital converter mask */ -#define IEC958_AES1_CON_DIGDIGCONV_ID 0x02 /**< digital<->digital converter id */ -#define IEC958_AES1_CON_PCM_CODER (IEC958_AES1_CON_DIGDIGCONV_ID|0x00) /**< PCM coder */ -#define IEC958_AES1_CON_MIXER (IEC958_AES1_CON_DIGDIGCONV_ID|0x10) /**< Digital signal mixer */ -#define IEC958_AES1_CON_RATE_CONVERTER (IEC958_AES1_CON_DIGDIGCONV_ID|0x18) /**< Rate converter */ -#define IEC958_AES1_CON_SAMPLER (IEC958_AES1_CON_DIGDIGCONV_ID|0x20) /**< PCM sampler */ -#define IEC958_AES1_CON_DSP (IEC958_AES1_CON_DIGDIGCONV_ID|0x28) /**< Digital sound processor */ -#define IEC958_AES1_CON_DIGDIGCONV_OTHER (IEC958_AES1_CON_DIGDIGCONV_ID|0x78) /**< Other digital<->digital product */ -#define IEC958_AES1_CON_MAGNETIC_MASK 0x07 /**< Magnetic device mask */ -#define IEC958_AES1_CON_MAGNETIC_ID 0x03 /**< Magnetic device ID */ -#define IEC958_AES1_CON_DAT (IEC958_AES1_CON_MAGNETIC_ID|0x00) /**< Digital Audio Tape */ -#define IEC958_AES1_CON_VCR (IEC958_AES1_CON_MAGNETIC_ID|0x08) /**< Video recorder */ -#define IEC958_AES1_CON_DCC (IEC958_AES1_CON_MAGNETIC_ID|0x40) /**< Digital compact cassette */ -#define IEC958_AES1_CON_MAGNETIC_DISC (IEC958_AES1_CON_MAGNETIC_ID|0x18) /**< Magnetic disc digital audio device */ -#define IEC958_AES1_CON_MAGNETIC_OTHER (IEC958_AES1_CON_MAGNETIC_ID|0x78) /**< Other magnetic device */ -#define IEC958_AES1_CON_BROADCAST1_MASK 0x07 /**< Broadcast mask */ -#define IEC958_AES1_CON_BROADCAST1_ID 0x04 /**< Broadcast ID */ -#define IEC958_AES1_CON_DAB_JAPAN (IEC958_AES1_CON_BROADCAST1_ID|0x00) /**< Digital audio broadcast (Japan) */ -#define IEC958_AES1_CON_DAB_EUROPE (IEC958_AES1_CON_BROADCAST1_ID|0x08) /**< Digital audio broadcast (Europe) */ -#define IEC958_AES1_CON_DAB_USA (IEC958_AES1_CON_BROADCAST1_ID|0x60) /**< Digital audio broadcast (USA) */ -#define IEC958_AES1_CON_SOFTWARE (IEC958_AES1_CON_BROADCAST1_ID|0x40) /**< Electronic software delivery */ -#define IEC958_AES1_CON_IEC62105 (IEC958_AES1_CON_BROADCAST1_ID|0x20) /**< Used by another standard (IEC 62105) */ -#define IEC958_AES1_CON_BROADCAST1_OTHER (IEC958_AES1_CON_BROADCAST1_ID|0x78) /**< Other broadcast product */ -#define IEC958_AES1_CON_BROADCAST2_MASK 0x0f /**< Broadcast alternative mask */ -#define IEC958_AES1_CON_BROADCAST2_ID 0x0e /**< Broadcast alternative ID */ -#define IEC958_AES1_CON_MUSICAL_MASK 0x07 /**< Musical device mask */ -#define IEC958_AES1_CON_MUSICAL_ID 0x05 /**< Musical device ID */ -#define IEC958_AES1_CON_SYNTHESIZER (IEC958_AES1_CON_MUSICAL_ID|0x00) /**< Synthesizer */ -#define IEC958_AES1_CON_MICROPHONE (IEC958_AES1_CON_MUSICAL_ID|0x08) /**< Microphone */ -#define IEC958_AES1_CON_MUSICAL_OTHER (IEC958_AES1_CON_MUSICAL_ID|0x78) /**< Other musical device */ -#define IEC958_AES1_CON_ADC_MASK 0x1f /**< ADC Mask */ -#define IEC958_AES1_CON_ADC_ID 0x06 /**< ADC ID */ -#define IEC958_AES1_CON_ADC (IEC958_AES1_CON_ADC_ID|0x00) /**< ADC without copyright information */ -#define IEC958_AES1_CON_ADC_OTHER (IEC958_AES1_CON_ADC_ID|0x60) /**< Other ADC product (with no copyright information) */ -#define IEC958_AES1_CON_ADC_COPYRIGHT_MASK 0x1f /**< ADC Copyright mask */ -#define IEC958_AES1_CON_ADC_COPYRIGHT_ID 0x16 /**< ADC Copyright ID */ -#define IEC958_AES1_CON_ADC_COPYRIGHT (IEC958_AES1_CON_ADC_COPYRIGHT_ID|0x00) /**< ADC with copyright information */ -#define IEC958_AES1_CON_ADC_COPYRIGHT_OTHER (IEC958_AES1_CON_ADC_COPYRIGHT_ID|0x60) /**< Other ADC with copyright information product */ -#define IEC958_AES1_CON_SOLIDMEM_MASK 0x0f /**< Solid memory based products mask */ -#define IEC958_AES1_CON_SOLIDMEM_ID 0x08 /**< Solid memory based products ID */ -#define IEC958_AES1_CON_SOLIDMEM_DIGITAL_RECORDER_PLAYER (IEC958_AES1_CON_SOLIDMEM_ID|0x00) /**< Digital audio recorder and player using solid state memory */ -#define IEC958_AES1_CON_SOLIDMEM_OTHER (IEC958_AES1_CON_SOLIDMEM_ID|0x70) /**< Other solid state memory based product */ -#define IEC958_AES1_CON_EXPERIMENTAL 0x40 /**< experimental category */ -#define IEC958_AES1_CON_ORIGINAL (1<<7) /**< this bits depends on the category code */ -#define IEC958_AES2_PRO_SBITS (7<<0) /**< mask - sample bits */ -#define IEC958_AES2_PRO_SBITS_20 (2<<0) /**< 20-bit - coordination */ -#define IEC958_AES2_PRO_SBITS_24 (4<<0) /**< 24-bit - main audio */ -#define IEC958_AES2_PRO_SBITS_UDEF (6<<0) /**< user defined application */ -#define IEC958_AES2_PRO_WORDLEN (7<<3) /**< mask - source word length */ -#define IEC958_AES2_PRO_WORDLEN_NOTID (0<<3) /**< source word length not indicated */ -#define IEC958_AES2_PRO_WORDLEN_22_18 (2<<3) /**< 22-bit or 18-bit */ -#define IEC958_AES2_PRO_WORDLEN_23_19 (4<<3) /**< 23-bit or 19-bit */ -#define IEC958_AES2_PRO_WORDLEN_24_20 (5<<3) /**< 24-bit or 20-bit */ -#define IEC958_AES2_PRO_WORDLEN_20_16 (6<<3) /**< 20-bit or 16-bit */ -#define IEC958_AES2_CON_SOURCE (15<<0) /**< mask - source number */ -#define IEC958_AES2_CON_SOURCE_UNSPEC (0<<0) /**< source number unspecified */ -#define IEC958_AES2_CON_CHANNEL (15<<4) /**< mask - channel number */ -#define IEC958_AES2_CON_CHANNEL_UNSPEC (0<<4) /**< channel number unspecified */ -#define IEC958_AES3_CON_FS (15<<0) /**< mask - sample frequency */ -#define IEC958_AES3_CON_FS_44100 (0<<0) /**< 44.1kHz */ -#define IEC958_AES3_CON_FS_NOTID (1<<0) /**< sample frequency non indicated */ -#define IEC958_AES3_CON_FS_48000 (2<<0) /**< 48kHz */ -#define IEC958_AES3_CON_FS_32000 (3<<0) /**< 32kHz */ -#define IEC958_AES3_CON_FS_22050 (4<<0) /**< 22.05kHz */ -#define IEC958_AES3_CON_FS_24000 (6<<0) /**< 24kHz */ -#define IEC958_AES3_CON_FS_88200 (8<<0) /**< 88.2kHz */ -#define IEC958_AES3_CON_FS_768000 (9<<0) /**< 768kHz */ -#define IEC958_AES3_CON_FS_96000 (10<<0) /**< 96kHz */ -#define IEC958_AES3_CON_FS_176400 (12<<0) /**< 176.4kHz */ -#define IEC958_AES3_CON_FS_192000 (14<<0) /**< 192kHz */ -#define IEC958_AES3_CON_CLOCK (3<<4) /**< mask - clock accuracy */ -#define IEC958_AES3_CON_CLOCK_1000PPM (0<<4) /**< 1000 ppm */ -#define IEC958_AES3_CON_CLOCK_50PPM (1<<4) /**< 50 ppm */ -#define IEC958_AES3_CON_CLOCK_VARIABLE (2<<4) /**< variable pitch */ -#define IEC958_AES4_CON_MAX_WORDLEN_24 (1<<0) /**< 0 = 20-bit, 1 = 24-bit */ -#define IEC958_AES4_CON_WORDLEN (7<<1) /**< mask - sample word length */ -#define IEC958_AES4_CON_WORDLEN_NOTID (0<<1) /**< not indicated */ -#define IEC958_AES4_CON_WORDLEN_20_16 (1<<1) /**< 20-bit or 16-bit */ -#define IEC958_AES4_CON_WORDLEN_22_18 (2<<1) /**< 22-bit or 18-bit */ -#define IEC958_AES4_CON_WORDLEN_23_19 (4<<1) /**< 23-bit or 19-bit */ -#define IEC958_AES4_CON_WORDLEN_24_20 (5<<1) /**< 24-bit or 20-bit */ -#define IEC958_AES4_CON_WORDLEN_21_17 (6<<1) /**< 21-bit or 17-bit */ -#define IEC958_AES4_CON_ORIGFS (15<<4) /**< mask - original sample frequency */ -#define IEC958_AES4_CON_ORIGFS_NOTID (0<<4) /**< original sample frequency not indicated */ -#define IEC958_AES4_CON_ORIGFS_192000 (1<<4) /**< 192kHz */ -#define IEC958_AES4_CON_ORIGFS_12000 (2<<4) /**< 12kHz */ -#define IEC958_AES4_CON_ORIGFS_176400 (3<<4) /**< 176.4kHz */ -#define IEC958_AES4_CON_ORIGFS_96000 (5<<4) /**< 96kHz */ -#define IEC958_AES4_CON_ORIGFS_8000 (6<<4) /**< 8kHz */ -#define IEC958_AES4_CON_ORIGFS_88200 (7<<4) /**< 88.2kHz */ -#define IEC958_AES4_CON_ORIGFS_16000 (8<<4) /**< 16kHz */ -#define IEC958_AES4_CON_ORIGFS_24000 (9<<4) /**< 24kHz */ -#define IEC958_AES4_CON_ORIGFS_11025 (10<<4) /**< 11.025kHz */ -#define IEC958_AES4_CON_ORIGFS_22050 (11<<4) /**< 22.05kHz */ -#define IEC958_AES4_CON_ORIGFS_32000 (12<<4) /**< 32kHz */ -#define IEC958_AES4_CON_ORIGFS_48000 (13<<4) /**< 48kHz */ -#define IEC958_AES4_CON_ORIGFS_44100 (15<<4) /**< 44.1kHz */ -#define IEC958_AES5_CON_CGMSA (3<<0) /**< mask - CGMS-A */ -#define IEC958_AES5_CON_CGMSA_COPYFREELY (0<<0) /**< copying is permitted without restriction */ -#define IEC958_AES5_CON_CGMSA_COPYONCE (1<<0) /**< one generation of copies may be made */ -#define IEC958_AES5_CON_CGMSA_COPYNOMORE (2<<0) /**< condition not be used */ -#define IEC958_AES5_CON_CGMSA_COPYNEVER (3<<0) /**< no copying is permitted */ - -/** \} */ - -/** - * \defgroup MIDI_Interface Constants for MIDI v1.0 - * Constants for MIDI v1.0. - * \{ - */ - -#define MIDI_CHANNELS 16 /**< Number of channels per port/cable. */ -#define MIDI_GM_DRUM_CHANNEL (10-1) /**< Channel number for GM drums. */ - -/** - * \defgroup MIDI_Commands MIDI Commands - * MIDI command codes. - * \{ - */ - -#define MIDI_CMD_NOTE_OFF 0x80 /**< note off */ -#define MIDI_CMD_NOTE_ON 0x90 /**< note on */ -#define MIDI_CMD_NOTE_PRESSURE 0xa0 /**< key pressure */ -#define MIDI_CMD_CONTROL 0xb0 /**< control change */ -#define MIDI_CMD_PGM_CHANGE 0xc0 /**< program change */ -#define MIDI_CMD_CHANNEL_PRESSURE 0xd0 /**< channel pressure */ -#define MIDI_CMD_BENDER 0xe0 /**< pitch bender */ - -#define MIDI_CMD_COMMON_SYSEX 0xf0 /**< sysex (system exclusive) begin */ -#define MIDI_CMD_COMMON_MTC_QUARTER 0xf1 /**< MTC quarter frame */ -#define MIDI_CMD_COMMON_SONG_POS 0xf2 /**< song position */ -#define MIDI_CMD_COMMON_SONG_SELECT 0xf3 /**< song select */ -#define MIDI_CMD_COMMON_TUNE_REQUEST 0xf6 /**< tune request */ -#define MIDI_CMD_COMMON_SYSEX_END 0xf7 /**< end of sysex */ -#define MIDI_CMD_COMMON_CLOCK 0xf8 /**< clock */ -#define MIDI_CMD_COMMON_START 0xfa /**< start */ -#define MIDI_CMD_COMMON_CONTINUE 0xfb /**< continue */ -#define MIDI_CMD_COMMON_STOP 0xfc /**< stop */ -#define MIDI_CMD_COMMON_SENSING 0xfe /**< active sensing */ -#define MIDI_CMD_COMMON_RESET 0xff /**< reset */ - -/** \} */ - -/** - * \defgroup MIDI_Controllers MIDI Controllers - * MIDI controller numbers. - * \{ - */ - -#define MIDI_CTL_MSB_BANK 0x00 /**< Bank selection */ -#define MIDI_CTL_MSB_MODWHEEL 0x01 /**< Modulation */ -#define MIDI_CTL_MSB_BREATH 0x02 /**< Breath */ -#define MIDI_CTL_MSB_FOOT 0x04 /**< Foot */ -#define MIDI_CTL_MSB_PORTAMENTO_TIME 0x05 /**< Portamento time */ -#define MIDI_CTL_MSB_DATA_ENTRY 0x06 /**< Data entry */ -#define MIDI_CTL_MSB_MAIN_VOLUME 0x07 /**< Main volume */ -#define MIDI_CTL_MSB_BALANCE 0x08 /**< Balance */ -#define MIDI_CTL_MSB_PAN 0x0a /**< Panpot */ -#define MIDI_CTL_MSB_EXPRESSION 0x0b /**< Expression */ -#define MIDI_CTL_MSB_EFFECT1 0x0c /**< Effect1 */ -#define MIDI_CTL_MSB_EFFECT2 0x0d /**< Effect2 */ -#define MIDI_CTL_MSB_GENERAL_PURPOSE1 0x10 /**< General purpose 1 */ -#define MIDI_CTL_MSB_GENERAL_PURPOSE2 0x11 /**< General purpose 2 */ -#define MIDI_CTL_MSB_GENERAL_PURPOSE3 0x12 /**< General purpose 3 */ -#define MIDI_CTL_MSB_GENERAL_PURPOSE4 0x13 /**< General purpose 4 */ -#define MIDI_CTL_LSB_BANK 0x20 /**< Bank selection */ -#define MIDI_CTL_LSB_MODWHEEL 0x21 /**< Modulation */ -#define MIDI_CTL_LSB_BREATH 0x22 /**< Breath */ -#define MIDI_CTL_LSB_FOOT 0x24 /**< Foot */ -#define MIDI_CTL_LSB_PORTAMENTO_TIME 0x25 /**< Portamento time */ -#define MIDI_CTL_LSB_DATA_ENTRY 0x26 /**< Data entry */ -#define MIDI_CTL_LSB_MAIN_VOLUME 0x27 /**< Main volume */ -#define MIDI_CTL_LSB_BALANCE 0x28 /**< Balance */ -#define MIDI_CTL_LSB_PAN 0x2a /**< Panpot */ -#define MIDI_CTL_LSB_EXPRESSION 0x2b /**< Expression */ -#define MIDI_CTL_LSB_EFFECT1 0x2c /**< Effect1 */ -#define MIDI_CTL_LSB_EFFECT2 0x2d /**< Effect2 */ -#define MIDI_CTL_LSB_GENERAL_PURPOSE1 0x30 /**< General purpose 1 */ -#define MIDI_CTL_LSB_GENERAL_PURPOSE2 0x31 /**< General purpose 2 */ -#define MIDI_CTL_LSB_GENERAL_PURPOSE3 0x32 /**< General purpose 3 */ -#define MIDI_CTL_LSB_GENERAL_PURPOSE4 0x33 /**< General purpose 4 */ -#define MIDI_CTL_SUSTAIN 0x40 /**< Sustain pedal */ -#define MIDI_CTL_PORTAMENTO 0x41 /**< Portamento */ -#define MIDI_CTL_SOSTENUTO 0x42 /**< Sostenuto */ -#define MIDI_CTL_SUSTENUTO 0x42 /**< Sostenuto (a typo in the older version) */ -#define MIDI_CTL_SOFT_PEDAL 0x43 /**< Soft pedal */ -#define MIDI_CTL_LEGATO_FOOTSWITCH 0x44 /**< Legato foot switch */ -#define MIDI_CTL_HOLD2 0x45 /**< Hold2 */ -#define MIDI_CTL_SC1_SOUND_VARIATION 0x46 /**< SC1 Sound Variation */ -#define MIDI_CTL_SC2_TIMBRE 0x47 /**< SC2 Timbre */ -#define MIDI_CTL_SC3_RELEASE_TIME 0x48 /**< SC3 Release Time */ -#define MIDI_CTL_SC4_ATTACK_TIME 0x49 /**< SC4 Attack Time */ -#define MIDI_CTL_SC5_BRIGHTNESS 0x4a /**< SC5 Brightness */ -#define MIDI_CTL_SC6 0x4b /**< SC6 */ -#define MIDI_CTL_SC7 0x4c /**< SC7 */ -#define MIDI_CTL_SC8 0x4d /**< SC8 */ -#define MIDI_CTL_SC9 0x4e /**< SC9 */ -#define MIDI_CTL_SC10 0x4f /**< SC10 */ -#define MIDI_CTL_GENERAL_PURPOSE5 0x50 /**< General purpose 5 */ -#define MIDI_CTL_GENERAL_PURPOSE6 0x51 /**< General purpose 6 */ -#define MIDI_CTL_GENERAL_PURPOSE7 0x52 /**< General purpose 7 */ -#define MIDI_CTL_GENERAL_PURPOSE8 0x53 /**< General purpose 8 */ -#define MIDI_CTL_PORTAMENTO_CONTROL 0x54 /**< Portamento control */ -#define MIDI_CTL_E1_REVERB_DEPTH 0x5b /**< E1 Reverb Depth */ -#define MIDI_CTL_E2_TREMOLO_DEPTH 0x5c /**< E2 Tremolo Depth */ -#define MIDI_CTL_E3_CHORUS_DEPTH 0x5d /**< E3 Chorus Depth */ -#define MIDI_CTL_E4_DETUNE_DEPTH 0x5e /**< E4 Detune Depth */ -#define MIDI_CTL_E5_PHASER_DEPTH 0x5f /**< E5 Phaser Depth */ -#define MIDI_CTL_DATA_INCREMENT 0x60 /**< Data Increment */ -#define MIDI_CTL_DATA_DECREMENT 0x61 /**< Data Decrement */ -#define MIDI_CTL_NONREG_PARM_NUM_LSB 0x62 /**< Non-registered parameter number */ -#define MIDI_CTL_NONREG_PARM_NUM_MSB 0x63 /**< Non-registered parameter number */ -#define MIDI_CTL_REGIST_PARM_NUM_LSB 0x64 /**< Registered parameter number */ -#define MIDI_CTL_REGIST_PARM_NUM_MSB 0x65 /**< Registered parameter number */ -#define MIDI_CTL_ALL_SOUNDS_OFF 0x78 /**< All sounds off */ -#define MIDI_CTL_RESET_CONTROLLERS 0x79 /**< Reset Controllers */ -#define MIDI_CTL_LOCAL_CONTROL_SWITCH 0x7a /**< Local control switch */ -#define MIDI_CTL_ALL_NOTES_OFF 0x7b /**< All notes off */ -#define MIDI_CTL_OMNI_OFF 0x7c /**< Omni off */ -#define MIDI_CTL_OMNI_ON 0x7d /**< Omni on */ -#define MIDI_CTL_MONO1 0x7e /**< Mono1 */ -#define MIDI_CTL_MONO2 0x7f /**< Mono2 */ - -/** \} */ - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_ASOUNDEF_H */ diff --git a/raylib/external/alsa/asoundlib.h b/raylib/external/alsa/asoundlib.h deleted file mode 100644 index a546194..0000000 --- a/raylib/external/alsa/asoundlib.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * \file include/asoundlib.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ASOUNDLIB_H -#define __ASOUNDLIB_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef __GNUC__ -#define __inline__ inline -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#endif /* __ASOUNDLIB_H */ diff --git a/raylib/external/alsa/conf.h b/raylib/external/alsa/conf.h deleted file mode 100644 index efb1176..0000000 --- a/raylib/external/alsa/conf.h +++ /dev/null @@ -1,214 +0,0 @@ -/** - * \file include/conf.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_CONF_H -#define __ALSA_CONF_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Config Configuration Interface - * The configuration functions and types allow you to read, enumerate, - * modify and write the contents of ALSA configuration files. - * \{ - */ - -/** \brief \c dlsym version for the config evaluate callback. */ -#define SND_CONFIG_DLSYM_VERSION_EVALUATE _dlsym_config_evaluate_001 -/** \brief \c dlsym version for the config hook callback. */ -#define SND_CONFIG_DLSYM_VERSION_HOOK _dlsym_config_hook_001 - -/** \brief Configuration node type. */ -typedef enum _snd_config_type { - /** Integer number. */ - SND_CONFIG_TYPE_INTEGER, - /** 64-bit integer number. */ - SND_CONFIG_TYPE_INTEGER64, - /** Real number. */ - SND_CONFIG_TYPE_REAL, - /** Character string. */ - SND_CONFIG_TYPE_STRING, - /** Pointer (runtime only, cannot be saved). */ - SND_CONFIG_TYPE_POINTER, - /** Compound node. */ - SND_CONFIG_TYPE_COMPOUND = 1024 -} snd_config_type_t; - -/** - * \brief Internal structure for a configuration node object. - * - * The ALSA library uses a pointer to this structure as a handle to a - * configuration node. Applications don't access its contents directly. - */ -typedef struct _snd_config snd_config_t; -/** - * \brief Type for a configuration compound iterator. - * - * The ALSA library uses this pointer type as a handle to a configuration - * compound iterator. Applications don't directly access the contents of - * the structure pointed to by this type. - */ -typedef struct _snd_config_iterator *snd_config_iterator_t; -/** - * \brief Internal structure for a configuration private update object. - * - * The ALSA library uses this structure to save private update information. - */ -typedef struct _snd_config_update snd_config_update_t; - -extern snd_config_t *snd_config; - -const char *snd_config_topdir(void); - -int snd_config_top(snd_config_t **config); - -int snd_config_load(snd_config_t *config, snd_input_t *in); -int snd_config_load_override(snd_config_t *config, snd_input_t *in); -int snd_config_save(snd_config_t *config, snd_output_t *out); -int snd_config_update(void); -int snd_config_update_r(snd_config_t **top, snd_config_update_t **update, const char *path); -int snd_config_update_free(snd_config_update_t *update); -int snd_config_update_free_global(void); - -int snd_config_update_ref(snd_config_t **top); -void snd_config_ref(snd_config_t *top); -void snd_config_unref(snd_config_t *top); - -int snd_config_search(snd_config_t *config, const char *key, - snd_config_t **result); -int snd_config_searchv(snd_config_t *config, - snd_config_t **result, ...); -int snd_config_search_definition(snd_config_t *config, - const char *base, const char *key, - snd_config_t **result); - -int snd_config_expand(snd_config_t *config, snd_config_t *root, - const char *args, snd_config_t *private_data, - snd_config_t **result); -int snd_config_evaluate(snd_config_t *config, snd_config_t *root, - snd_config_t *private_data, snd_config_t **result); - -int snd_config_add(snd_config_t *config, snd_config_t *leaf); -int snd_config_delete(snd_config_t *config); -int snd_config_delete_compound_members(const snd_config_t *config); -int snd_config_copy(snd_config_t **dst, snd_config_t *src); - -int snd_config_make(snd_config_t **config, const char *key, - snd_config_type_t type); -int snd_config_make_integer(snd_config_t **config, const char *key); -int snd_config_make_integer64(snd_config_t **config, const char *key); -int snd_config_make_real(snd_config_t **config, const char *key); -int snd_config_make_string(snd_config_t **config, const char *key); -int snd_config_make_pointer(snd_config_t **config, const char *key); -int snd_config_make_compound(snd_config_t **config, const char *key, int join); - -int snd_config_imake_integer(snd_config_t **config, const char *key, const long value); -int snd_config_imake_integer64(snd_config_t **config, const char *key, const long long value); -int snd_config_imake_real(snd_config_t **config, const char *key, const double value); -int snd_config_imake_string(snd_config_t **config, const char *key, const char *ascii); -int snd_config_imake_safe_string(snd_config_t **config, const char *key, const char *ascii); -int snd_config_imake_pointer(snd_config_t **config, const char *key, const void *ptr); - -snd_config_type_t snd_config_get_type(const snd_config_t *config); - -int snd_config_set_id(snd_config_t *config, const char *id); -int snd_config_set_integer(snd_config_t *config, long value); -int snd_config_set_integer64(snd_config_t *config, long long value); -int snd_config_set_real(snd_config_t *config, double value); -int snd_config_set_string(snd_config_t *config, const char *value); -int snd_config_set_ascii(snd_config_t *config, const char *ascii); -int snd_config_set_pointer(snd_config_t *config, const void *ptr); -int snd_config_get_id(const snd_config_t *config, const char **value); -int snd_config_get_integer(const snd_config_t *config, long *value); -int snd_config_get_integer64(const snd_config_t *config, long long *value); -int snd_config_get_real(const snd_config_t *config, double *value); -int snd_config_get_ireal(const snd_config_t *config, double *value); -int snd_config_get_string(const snd_config_t *config, const char **value); -int snd_config_get_ascii(const snd_config_t *config, char **value); -int snd_config_get_pointer(const snd_config_t *config, const void **value); -int snd_config_test_id(const snd_config_t *config, const char *id); - -snd_config_iterator_t snd_config_iterator_first(const snd_config_t *node); -snd_config_iterator_t snd_config_iterator_next(const snd_config_iterator_t iterator); -snd_config_iterator_t snd_config_iterator_end(const snd_config_t *node); -snd_config_t *snd_config_iterator_entry(const snd_config_iterator_t iterator); - -/** - * \brief Helper macro to iterate over the children of a compound node. - * \param[in,out] pos Iterator variable for the current node. - * \param[in,out] next Temporary iterator variable for the next node. - * \param[in] node Handle to the compound configuration node to iterate over. - * - * Use this macro like a \c for statement, e.g.: - * \code - * snd_config_iterator_t pos, next; - * snd_config_for_each(pos, next, node) { - * snd_config_t *entry = snd_config_iterator_entry(pos); - * ... - * } - * \endcode - * - * This macro allows deleting or removing the current node. - */ -#define snd_config_for_each(pos, next, node) \ - for (pos = snd_config_iterator_first(node), next = snd_config_iterator_next(pos); pos != snd_config_iterator_end(node); pos = next, next = snd_config_iterator_next(pos)) - -/* Misc functions */ - -int snd_config_get_bool_ascii(const char *ascii); -int snd_config_get_bool(const snd_config_t *conf); -int snd_config_get_ctl_iface_ascii(const char *ascii); -int snd_config_get_ctl_iface(const snd_config_t *conf); - -/* Names functions */ - -/** - * Device-name list element - */ -typedef struct snd_devname snd_devname_t; - -/** - * Device-name list element (definition) - */ -struct snd_devname { - char *name; /**< Device name string */ - char *comment; /**< Comments */ - snd_devname_t *next; /**< Next pointer */ -}; - -int snd_names_list(const char *iface, snd_devname_t **list); -void snd_names_list_free(snd_devname_t *list); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_CONF_H */ diff --git a/raylib/external/alsa/control.h b/raylib/external/alsa/control.h deleted file mode 100644 index 1d4bfcb..0000000 --- a/raylib/external/alsa/control.h +++ /dev/null @@ -1,622 +0,0 @@ -/** - * \file include/control.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_CONTROL_H -#define __ALSA_CONTROL_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Control Control Interface - * The control interface. - * See \ref control page for more details. - * \{ - */ - -/** dlsym version for interface entry callback */ -#define SND_CONTROL_DLSYM_VERSION _dlsym_control_001 - -/** IEC958 structure */ -typedef struct snd_aes_iec958 { - unsigned char status[24]; /**< AES/IEC958 channel status bits */ - unsigned char subcode[147]; /**< AES/IEC958 subcode bits */ - unsigned char pad; /**< nothing */ - unsigned char dig_subframe[4]; /**< AES/IEC958 subframe bits */ -} snd_aes_iec958_t; - -/** CTL card info container */ -typedef struct _snd_ctl_card_info snd_ctl_card_info_t; - -/** CTL element identifier container */ -typedef struct _snd_ctl_elem_id snd_ctl_elem_id_t; - -/** CTL element identifier list container */ -typedef struct _snd_ctl_elem_list snd_ctl_elem_list_t; - -/** CTL element info container */ -typedef struct _snd_ctl_elem_info snd_ctl_elem_info_t; - -/** CTL element value container */ -typedef struct _snd_ctl_elem_value snd_ctl_elem_value_t; - -/** CTL event container */ -typedef struct _snd_ctl_event snd_ctl_event_t; - -/** CTL element type */ -typedef enum _snd_ctl_elem_type { - /** Invalid type */ - SND_CTL_ELEM_TYPE_NONE = 0, - /** Boolean contents */ - SND_CTL_ELEM_TYPE_BOOLEAN, - /** Integer contents */ - SND_CTL_ELEM_TYPE_INTEGER, - /** Enumerated contents */ - SND_CTL_ELEM_TYPE_ENUMERATED, - /** Bytes contents */ - SND_CTL_ELEM_TYPE_BYTES, - /** IEC958 (S/PDIF) setting content */ - SND_CTL_ELEM_TYPE_IEC958, - /** 64-bit integer contents */ - SND_CTL_ELEM_TYPE_INTEGER64, - SND_CTL_ELEM_TYPE_LAST = SND_CTL_ELEM_TYPE_INTEGER64 -} snd_ctl_elem_type_t; - -/** CTL related interface */ -typedef enum _snd_ctl_elem_iface { - /** Card level */ - SND_CTL_ELEM_IFACE_CARD = 0, - /** Hardware dependent device */ - SND_CTL_ELEM_IFACE_HWDEP, - /** Mixer */ - SND_CTL_ELEM_IFACE_MIXER, - /** PCM */ - SND_CTL_ELEM_IFACE_PCM, - /** RawMidi */ - SND_CTL_ELEM_IFACE_RAWMIDI, - /** Timer */ - SND_CTL_ELEM_IFACE_TIMER, - /** Sequencer */ - SND_CTL_ELEM_IFACE_SEQUENCER, - SND_CTL_ELEM_IFACE_LAST = SND_CTL_ELEM_IFACE_SEQUENCER -} snd_ctl_elem_iface_t; - -/** Event class */ -typedef enum _snd_ctl_event_type { - /** Elements related event */ - SND_CTL_EVENT_ELEM = 0, - SND_CTL_EVENT_LAST = SND_CTL_EVENT_ELEM -}snd_ctl_event_type_t; - -/** Element has been removed (Warning: test this first and if set don't - * test the other masks) \hideinitializer */ -#define SND_CTL_EVENT_MASK_REMOVE (~0U) -/** Element value has been changed \hideinitializer */ -#define SND_CTL_EVENT_MASK_VALUE (1<<0) -/** Element info has been changed \hideinitializer */ -#define SND_CTL_EVENT_MASK_INFO (1<<1) -/** Element has been added \hideinitializer */ -#define SND_CTL_EVENT_MASK_ADD (1<<2) -/** Element's TLV value has been changed \hideinitializer */ -#define SND_CTL_EVENT_MASK_TLV (1<<3) - -/** CTL name helper */ -#define SND_CTL_NAME_NONE "" -/** CTL name helper */ -#define SND_CTL_NAME_PLAYBACK "Playback " -/** CTL name helper */ -#define SND_CTL_NAME_CAPTURE "Capture " - -/** CTL name helper */ -#define SND_CTL_NAME_IEC958_NONE "" -/** CTL name helper */ -#define SND_CTL_NAME_IEC958_SWITCH "Switch" -/** CTL name helper */ -#define SND_CTL_NAME_IEC958_VOLUME "Volume" -/** CTL name helper */ -#define SND_CTL_NAME_IEC958_DEFAULT "Default" -/** CTL name helper */ -#define SND_CTL_NAME_IEC958_MASK "Mask" -/** CTL name helper */ -#define SND_CTL_NAME_IEC958_CON_MASK "Con Mask" -/** CTL name helper */ -#define SND_CTL_NAME_IEC958_PRO_MASK "Pro Mask" -/** CTL name helper */ -#define SND_CTL_NAME_IEC958_PCM_STREAM "PCM Stream" -/** Element name for IEC958 (S/PDIF) */ -#define SND_CTL_NAME_IEC958(expl,direction,what) "IEC958 " expl SND_CTL_NAME_##direction SND_CTL_NAME_IEC958_##what - -/** Mask for the major Power State identifier */ -#define SND_CTL_POWER_MASK 0xff00 -/** ACPI/PCI Power State D0 */ -#define SND_CTL_POWER_D0 0x0000 -/** ACPI/PCI Power State D1 */ -#define SND_CTL_POWER_D1 0x0100 -/** ACPI/PCI Power State D2 */ -#define SND_CTL_POWER_D2 0x0200 -/** ACPI/PCI Power State D3 */ -#define SND_CTL_POWER_D3 0x0300 -/** ACPI/PCI Power State D3hot */ -#define SND_CTL_POWER_D3hot (SND_CTL_POWER_D3|0x0000) -/** ACPI/PCI Power State D3cold */ -#define SND_CTL_POWER_D3cold (SND_CTL_POWER_D3|0x0001) - -/** TLV type - Container */ -#define SND_CTL_TLVT_CONTAINER 0x0000 -/** TLV type - basic dB scale */ -#define SND_CTL_TLVT_DB_SCALE 0x0001 -/** TLV type - linear volume */ -#define SND_CTL_TLVT_DB_LINEAR 0x0002 -/** TLV type - dB range container */ -#define SND_CTL_TLVT_DB_RANGE 0x0003 -/** TLV type - dB scale specified by min/max values */ -#define SND_CTL_TLVT_DB_MINMAX 0x0004 -/** TLV type - dB scale specified by min/max values (with mute) */ -#define SND_CTL_TLVT_DB_MINMAX_MUTE 0x0005 - -/** Mute state */ -#define SND_CTL_TLV_DB_GAIN_MUTE -9999999 - -/** TLV type - fixed channel map positions */ -#define SND_CTL_TLVT_CHMAP_FIXED 0x00101 -/** TLV type - freely swappable channel map positions */ -#define SND_CTL_TLVT_CHMAP_VAR 0x00102 -/** TLV type - pair-wise swappable channel map positions */ -#define SND_CTL_TLVT_CHMAP_PAIRED 0x00103 - -/** CTL type */ -typedef enum _snd_ctl_type { - /** Kernel level CTL */ - SND_CTL_TYPE_HW, - /** Shared memory client CTL */ - SND_CTL_TYPE_SHM, - /** INET client CTL (not yet implemented) */ - SND_CTL_TYPE_INET, - /** External control plugin */ - SND_CTL_TYPE_EXT -} snd_ctl_type_t; - -/** Non blocking mode (flag for open mode) \hideinitializer */ -#define SND_CTL_NONBLOCK 0x0001 - -/** Async notification (flag for open mode) \hideinitializer */ -#define SND_CTL_ASYNC 0x0002 - -/** Read only (flag for open mode) \hideinitializer */ -#define SND_CTL_READONLY 0x0004 - -/** CTL handle */ -typedef struct _snd_ctl snd_ctl_t; - -/** Don't destroy the ctl handle when close */ -#define SND_SCTL_NOFREE 0x0001 - -/** SCTL type */ -typedef struct _snd_sctl snd_sctl_t; - -int snd_card_load(int card); -int snd_card_next(int *card); -int snd_card_get_index(const char *name); -int snd_card_get_name(int card, char **name); -int snd_card_get_longname(int card, char **name); - -int snd_device_name_hint(int card, const char *iface, void ***hints); -int snd_device_name_free_hint(void **hints); -char *snd_device_name_get_hint(const void *hint, const char *id); - -int snd_ctl_open(snd_ctl_t **ctl, const char *name, int mode); -int snd_ctl_open_lconf(snd_ctl_t **ctl, const char *name, int mode, snd_config_t *lconf); -int snd_ctl_open_fallback(snd_ctl_t **ctl, snd_config_t *root, const char *name, const char *orig_name, int mode); -int snd_ctl_close(snd_ctl_t *ctl); -int snd_ctl_nonblock(snd_ctl_t *ctl, int nonblock); -static __inline__ int snd_ctl_abort(snd_ctl_t *ctl) { return snd_ctl_nonblock(ctl, 2); } -int snd_async_add_ctl_handler(snd_async_handler_t **handler, snd_ctl_t *ctl, - snd_async_callback_t callback, void *private_data); -snd_ctl_t *snd_async_handler_get_ctl(snd_async_handler_t *handler); -int snd_ctl_poll_descriptors_count(snd_ctl_t *ctl); -int snd_ctl_poll_descriptors(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int space); -int snd_ctl_poll_descriptors_revents(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -int snd_ctl_subscribe_events(snd_ctl_t *ctl, int subscribe); -int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info); -int snd_ctl_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list); -int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info); -int snd_ctl_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *data); -int snd_ctl_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *data); -int snd_ctl_elem_lock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id); -int snd_ctl_elem_unlock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id); -int snd_ctl_elem_tlv_read(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, - unsigned int *tlv, unsigned int tlv_size); -int snd_ctl_elem_tlv_write(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, - const unsigned int *tlv); -int snd_ctl_elem_tlv_command(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, - const unsigned int *tlv); -#ifdef __ALSA_HWDEP_H -int snd_ctl_hwdep_next_device(snd_ctl_t *ctl, int * device); -int snd_ctl_hwdep_info(snd_ctl_t *ctl, snd_hwdep_info_t * info); -#endif -#ifdef __ALSA_PCM_H -int snd_ctl_pcm_next_device(snd_ctl_t *ctl, int *device); -int snd_ctl_pcm_info(snd_ctl_t *ctl, snd_pcm_info_t * info); -int snd_ctl_pcm_prefer_subdevice(snd_ctl_t *ctl, int subdev); -#endif -#ifdef __ALSA_RAWMIDI_H -int snd_ctl_rawmidi_next_device(snd_ctl_t *ctl, int * device); -int snd_ctl_rawmidi_info(snd_ctl_t *ctl, snd_rawmidi_info_t * info); -int snd_ctl_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev); -#endif -int snd_ctl_set_power_state(snd_ctl_t *ctl, unsigned int state); -int snd_ctl_get_power_state(snd_ctl_t *ctl, unsigned int *state); - -int snd_ctl_read(snd_ctl_t *ctl, snd_ctl_event_t *event); -int snd_ctl_wait(snd_ctl_t *ctl, int timeout); -const char *snd_ctl_name(snd_ctl_t *ctl); -snd_ctl_type_t snd_ctl_type(snd_ctl_t *ctl); - -const char *snd_ctl_elem_type_name(snd_ctl_elem_type_t type); -const char *snd_ctl_elem_iface_name(snd_ctl_elem_iface_t iface); -const char *snd_ctl_event_type_name(snd_ctl_event_type_t type); - -unsigned int snd_ctl_event_elem_get_mask(const snd_ctl_event_t *obj); -unsigned int snd_ctl_event_elem_get_numid(const snd_ctl_event_t *obj); -void snd_ctl_event_elem_get_id(const snd_ctl_event_t *obj, snd_ctl_elem_id_t *ptr); -snd_ctl_elem_iface_t snd_ctl_event_elem_get_interface(const snd_ctl_event_t *obj); -unsigned int snd_ctl_event_elem_get_device(const snd_ctl_event_t *obj); -unsigned int snd_ctl_event_elem_get_subdevice(const snd_ctl_event_t *obj); -const char *snd_ctl_event_elem_get_name(const snd_ctl_event_t *obj); -unsigned int snd_ctl_event_elem_get_index(const snd_ctl_event_t *obj); - -int snd_ctl_elem_list_alloc_space(snd_ctl_elem_list_t *obj, unsigned int entries); -void snd_ctl_elem_list_free_space(snd_ctl_elem_list_t *obj); - -char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id); -int snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str); -int snd_ctl_ascii_value_parse(snd_ctl_t *handle, - snd_ctl_elem_value_t *dst, - snd_ctl_elem_info_t *info, - const char *value); - -size_t snd_ctl_elem_id_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_ctl_elem_id_t using standard alloca - * \param ptr returned pointer - */ -#define snd_ctl_elem_id_alloca(ptr) __snd_alloca(ptr, snd_ctl_elem_id) -int snd_ctl_elem_id_malloc(snd_ctl_elem_id_t **ptr); -void snd_ctl_elem_id_free(snd_ctl_elem_id_t *obj); -void snd_ctl_elem_id_clear(snd_ctl_elem_id_t *obj); -void snd_ctl_elem_id_copy(snd_ctl_elem_id_t *dst, const snd_ctl_elem_id_t *src); -unsigned int snd_ctl_elem_id_get_numid(const snd_ctl_elem_id_t *obj); -snd_ctl_elem_iface_t snd_ctl_elem_id_get_interface(const snd_ctl_elem_id_t *obj); -unsigned int snd_ctl_elem_id_get_device(const snd_ctl_elem_id_t *obj); -unsigned int snd_ctl_elem_id_get_subdevice(const snd_ctl_elem_id_t *obj); -const char *snd_ctl_elem_id_get_name(const snd_ctl_elem_id_t *obj); -unsigned int snd_ctl_elem_id_get_index(const snd_ctl_elem_id_t *obj); -void snd_ctl_elem_id_set_numid(snd_ctl_elem_id_t *obj, unsigned int val); -void snd_ctl_elem_id_set_interface(snd_ctl_elem_id_t *obj, snd_ctl_elem_iface_t val); -void snd_ctl_elem_id_set_device(snd_ctl_elem_id_t *obj, unsigned int val); -void snd_ctl_elem_id_set_subdevice(snd_ctl_elem_id_t *obj, unsigned int val); -void snd_ctl_elem_id_set_name(snd_ctl_elem_id_t *obj, const char *val); -void snd_ctl_elem_id_set_index(snd_ctl_elem_id_t *obj, unsigned int val); - -size_t snd_ctl_card_info_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_ctl_card_info_t using standard alloca - * \param ptr returned pointer - */ -#define snd_ctl_card_info_alloca(ptr) __snd_alloca(ptr, snd_ctl_card_info) -int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr); -void snd_ctl_card_info_free(snd_ctl_card_info_t *obj); -void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj); -void snd_ctl_card_info_copy(snd_ctl_card_info_t *dst, const snd_ctl_card_info_t *src); -int snd_ctl_card_info_get_card(const snd_ctl_card_info_t *obj); -const char *snd_ctl_card_info_get_id(const snd_ctl_card_info_t *obj); -const char *snd_ctl_card_info_get_driver(const snd_ctl_card_info_t *obj); -const char *snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj); -const char *snd_ctl_card_info_get_longname(const snd_ctl_card_info_t *obj); -const char *snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj); -const char *snd_ctl_card_info_get_components(const snd_ctl_card_info_t *obj); - -size_t snd_ctl_event_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_ctl_event_t using standard alloca - * \param ptr returned pointer - */ -#define snd_ctl_event_alloca(ptr) __snd_alloca(ptr, snd_ctl_event) -int snd_ctl_event_malloc(snd_ctl_event_t **ptr); -void snd_ctl_event_free(snd_ctl_event_t *obj); -void snd_ctl_event_clear(snd_ctl_event_t *obj); -void snd_ctl_event_copy(snd_ctl_event_t *dst, const snd_ctl_event_t *src); -snd_ctl_event_type_t snd_ctl_event_get_type(const snd_ctl_event_t *obj); - -size_t snd_ctl_elem_list_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_ctl_elem_list_t using standard alloca - * \param ptr returned pointer - */ -#define snd_ctl_elem_list_alloca(ptr) __snd_alloca(ptr, snd_ctl_elem_list) -int snd_ctl_elem_list_malloc(snd_ctl_elem_list_t **ptr); -void snd_ctl_elem_list_free(snd_ctl_elem_list_t *obj); -void snd_ctl_elem_list_clear(snd_ctl_elem_list_t *obj); -void snd_ctl_elem_list_copy(snd_ctl_elem_list_t *dst, const snd_ctl_elem_list_t *src); -void snd_ctl_elem_list_set_offset(snd_ctl_elem_list_t *obj, unsigned int val); -unsigned int snd_ctl_elem_list_get_used(const snd_ctl_elem_list_t *obj); -unsigned int snd_ctl_elem_list_get_count(const snd_ctl_elem_list_t *obj); -void snd_ctl_elem_list_get_id(const snd_ctl_elem_list_t *obj, unsigned int idx, snd_ctl_elem_id_t *ptr); -unsigned int snd_ctl_elem_list_get_numid(const snd_ctl_elem_list_t *obj, unsigned int idx); -snd_ctl_elem_iface_t snd_ctl_elem_list_get_interface(const snd_ctl_elem_list_t *obj, unsigned int idx); -unsigned int snd_ctl_elem_list_get_device(const snd_ctl_elem_list_t *obj, unsigned int idx); -unsigned int snd_ctl_elem_list_get_subdevice(const snd_ctl_elem_list_t *obj, unsigned int idx); -const char *snd_ctl_elem_list_get_name(const snd_ctl_elem_list_t *obj, unsigned int idx); -unsigned int snd_ctl_elem_list_get_index(const snd_ctl_elem_list_t *obj, unsigned int idx); - -size_t snd_ctl_elem_info_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_ctl_elem_info_t using standard alloca - * \param ptr returned pointer - */ -#define snd_ctl_elem_info_alloca(ptr) __snd_alloca(ptr, snd_ctl_elem_info) -int snd_ctl_elem_info_malloc(snd_ctl_elem_info_t **ptr); -void snd_ctl_elem_info_free(snd_ctl_elem_info_t *obj); -void snd_ctl_elem_info_clear(snd_ctl_elem_info_t *obj); -void snd_ctl_elem_info_copy(snd_ctl_elem_info_t *dst, const snd_ctl_elem_info_t *src); -snd_ctl_elem_type_t snd_ctl_elem_info_get_type(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_readable(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_writable(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_volatile(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_inactive(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_locked(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_tlv_readable(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_tlv_writable(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_tlv_commandable(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_owner(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_is_user(const snd_ctl_elem_info_t *obj); -pid_t snd_ctl_elem_info_get_owner(const snd_ctl_elem_info_t *obj); -unsigned int snd_ctl_elem_info_get_count(const snd_ctl_elem_info_t *obj); -long snd_ctl_elem_info_get_min(const snd_ctl_elem_info_t *obj); -long snd_ctl_elem_info_get_max(const snd_ctl_elem_info_t *obj); -long snd_ctl_elem_info_get_step(const snd_ctl_elem_info_t *obj); -long long snd_ctl_elem_info_get_min64(const snd_ctl_elem_info_t *obj); -long long snd_ctl_elem_info_get_max64(const snd_ctl_elem_info_t *obj); -long long snd_ctl_elem_info_get_step64(const snd_ctl_elem_info_t *obj); -unsigned int snd_ctl_elem_info_get_items(const snd_ctl_elem_info_t *obj); -void snd_ctl_elem_info_set_item(snd_ctl_elem_info_t *obj, unsigned int val); -const char *snd_ctl_elem_info_get_item_name(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_get_dimensions(const snd_ctl_elem_info_t *obj); -int snd_ctl_elem_info_get_dimension(const snd_ctl_elem_info_t *obj, unsigned int idx); -int snd_ctl_elem_info_set_dimension(snd_ctl_elem_info_t *info, - const int dimension[4]); -void snd_ctl_elem_info_get_id(const snd_ctl_elem_info_t *obj, snd_ctl_elem_id_t *ptr); -unsigned int snd_ctl_elem_info_get_numid(const snd_ctl_elem_info_t *obj); -snd_ctl_elem_iface_t snd_ctl_elem_info_get_interface(const snd_ctl_elem_info_t *obj); -unsigned int snd_ctl_elem_info_get_device(const snd_ctl_elem_info_t *obj); -unsigned int snd_ctl_elem_info_get_subdevice(const snd_ctl_elem_info_t *obj); -const char *snd_ctl_elem_info_get_name(const snd_ctl_elem_info_t *obj); -unsigned int snd_ctl_elem_info_get_index(const snd_ctl_elem_info_t *obj); -void snd_ctl_elem_info_set_id(snd_ctl_elem_info_t *obj, const snd_ctl_elem_id_t *ptr); -void snd_ctl_elem_info_set_numid(snd_ctl_elem_info_t *obj, unsigned int val); -void snd_ctl_elem_info_set_interface(snd_ctl_elem_info_t *obj, snd_ctl_elem_iface_t val); -void snd_ctl_elem_info_set_device(snd_ctl_elem_info_t *obj, unsigned int val); -void snd_ctl_elem_info_set_subdevice(snd_ctl_elem_info_t *obj, unsigned int val); -void snd_ctl_elem_info_set_name(snd_ctl_elem_info_t *obj, const char *val); -void snd_ctl_elem_info_set_index(snd_ctl_elem_info_t *obj, unsigned int val); - -int snd_ctl_add_integer_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, - unsigned int element_count, - unsigned int member_count, - long min, long max, long step); -int snd_ctl_add_integer64_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, - unsigned int element_count, - unsigned int member_count, - long long min, long long max, - long long step); -int snd_ctl_add_boolean_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, - unsigned int element_count, - unsigned int member_count); -int snd_ctl_add_enumerated_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, - unsigned int element_count, - unsigned int member_count, - unsigned int items, - const char *const labels[]); -int snd_ctl_add_bytes_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, - unsigned int element_count, - unsigned int member_count); - -int snd_ctl_elem_add_integer(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, unsigned int count, long imin, long imax, long istep); -int snd_ctl_elem_add_integer64(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, unsigned int count, long long imin, long long imax, long long istep); -int snd_ctl_elem_add_boolean(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, unsigned int count); -int snd_ctl_elem_add_enumerated(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, unsigned int count, unsigned int items, const char *const names[]); -int snd_ctl_elem_add_iec958(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id); -int snd_ctl_elem_remove(snd_ctl_t *ctl, snd_ctl_elem_id_t *id); - -size_t snd_ctl_elem_value_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_ctl_elem_value_t using standard alloca - * \param ptr returned pointer - */ -#define snd_ctl_elem_value_alloca(ptr) __snd_alloca(ptr, snd_ctl_elem_value) -int snd_ctl_elem_value_malloc(snd_ctl_elem_value_t **ptr); -void snd_ctl_elem_value_free(snd_ctl_elem_value_t *obj); -void snd_ctl_elem_value_clear(snd_ctl_elem_value_t *obj); -void snd_ctl_elem_value_copy(snd_ctl_elem_value_t *dst, const snd_ctl_elem_value_t *src); -int snd_ctl_elem_value_compare(snd_ctl_elem_value_t *left, const snd_ctl_elem_value_t *right); -void snd_ctl_elem_value_get_id(const snd_ctl_elem_value_t *obj, snd_ctl_elem_id_t *ptr); -unsigned int snd_ctl_elem_value_get_numid(const snd_ctl_elem_value_t *obj); -snd_ctl_elem_iface_t snd_ctl_elem_value_get_interface(const snd_ctl_elem_value_t *obj); -unsigned int snd_ctl_elem_value_get_device(const snd_ctl_elem_value_t *obj); -unsigned int snd_ctl_elem_value_get_subdevice(const snd_ctl_elem_value_t *obj); -const char *snd_ctl_elem_value_get_name(const snd_ctl_elem_value_t *obj); -unsigned int snd_ctl_elem_value_get_index(const snd_ctl_elem_value_t *obj); -void snd_ctl_elem_value_set_id(snd_ctl_elem_value_t *obj, const snd_ctl_elem_id_t *ptr); -void snd_ctl_elem_value_set_numid(snd_ctl_elem_value_t *obj, unsigned int val); -void snd_ctl_elem_value_set_interface(snd_ctl_elem_value_t *obj, snd_ctl_elem_iface_t val); -void snd_ctl_elem_value_set_device(snd_ctl_elem_value_t *obj, unsigned int val); -void snd_ctl_elem_value_set_subdevice(snd_ctl_elem_value_t *obj, unsigned int val); -void snd_ctl_elem_value_set_name(snd_ctl_elem_value_t *obj, const char *val); -void snd_ctl_elem_value_set_index(snd_ctl_elem_value_t *obj, unsigned int val); -int snd_ctl_elem_value_get_boolean(const snd_ctl_elem_value_t *obj, unsigned int idx); -long snd_ctl_elem_value_get_integer(const snd_ctl_elem_value_t *obj, unsigned int idx); -long long snd_ctl_elem_value_get_integer64(const snd_ctl_elem_value_t *obj, unsigned int idx); -unsigned int snd_ctl_elem_value_get_enumerated(const snd_ctl_elem_value_t *obj, unsigned int idx); -unsigned char snd_ctl_elem_value_get_byte(const snd_ctl_elem_value_t *obj, unsigned int idx); -void snd_ctl_elem_value_set_boolean(snd_ctl_elem_value_t *obj, unsigned int idx, long val); -void snd_ctl_elem_value_set_integer(snd_ctl_elem_value_t *obj, unsigned int idx, long val); -void snd_ctl_elem_value_set_integer64(snd_ctl_elem_value_t *obj, unsigned int idx, long long val); -void snd_ctl_elem_value_set_enumerated(snd_ctl_elem_value_t *obj, unsigned int idx, unsigned int val); -void snd_ctl_elem_value_set_byte(snd_ctl_elem_value_t *obj, unsigned int idx, unsigned char val); -void snd_ctl_elem_set_bytes(snd_ctl_elem_value_t *obj, void *data, size_t size); -const void * snd_ctl_elem_value_get_bytes(const snd_ctl_elem_value_t *obj); -void snd_ctl_elem_value_get_iec958(const snd_ctl_elem_value_t *obj, snd_aes_iec958_t *ptr); -void snd_ctl_elem_value_set_iec958(snd_ctl_elem_value_t *obj, const snd_aes_iec958_t *ptr); - -int snd_tlv_parse_dB_info(unsigned int *tlv, unsigned int tlv_size, - unsigned int **db_tlvp); -int snd_tlv_get_dB_range(unsigned int *tlv, long rangemin, long rangemax, - long *min, long *max); -int snd_tlv_convert_to_dB(unsigned int *tlv, long rangemin, long rangemax, - long volume, long *db_gain); -int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax, - long db_gain, long *value, int xdir); -int snd_ctl_get_dB_range(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, - long *min, long *max); -int snd_ctl_convert_to_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, - long volume, long *db_gain); -int snd_ctl_convert_from_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, - long db_gain, long *value, int xdir); - -/** - * \defgroup HControl High level Control Interface - * \ingroup Control - * The high level control interface. - * See \ref hcontrol page for more details. - * \{ - */ - -/** HCTL element handle */ -typedef struct _snd_hctl_elem snd_hctl_elem_t; - -/** HCTL handle */ -typedef struct _snd_hctl snd_hctl_t; - -/** - * \brief Compare function for sorting HCTL elements - * \param e1 First element - * \param e2 Second element - * \return -1 if e1 < e2, 0 if e1 == e2, 1 if e1 > e2 - */ -typedef int (*snd_hctl_compare_t)(const snd_hctl_elem_t *e1, - const snd_hctl_elem_t *e2); -int snd_hctl_compare_fast(const snd_hctl_elem_t *c1, - const snd_hctl_elem_t *c2); -/** - * \brief HCTL callback function - * \param hctl HCTL handle - * \param mask event mask - * \param elem related HCTL element (if any) - * \return 0 on success otherwise a negative error code - */ -typedef int (*snd_hctl_callback_t)(snd_hctl_t *hctl, - unsigned int mask, - snd_hctl_elem_t *elem); -/** - * \brief HCTL element callback function - * \param elem HCTL element - * \param mask event mask - * \return 0 on success otherwise a negative error code - */ -typedef int (*snd_hctl_elem_callback_t)(snd_hctl_elem_t *elem, - unsigned int mask); - -int snd_hctl_open(snd_hctl_t **hctl, const char *name, int mode); -int snd_hctl_open_ctl(snd_hctl_t **hctlp, snd_ctl_t *ctl); -int snd_hctl_close(snd_hctl_t *hctl); -int snd_hctl_nonblock(snd_hctl_t *hctl, int nonblock); -static __inline__ int snd_hctl_abort(snd_hctl_t *hctl) { return snd_hctl_nonblock(hctl, 2); } -int snd_hctl_poll_descriptors_count(snd_hctl_t *hctl); -int snd_hctl_poll_descriptors(snd_hctl_t *hctl, struct pollfd *pfds, unsigned int space); -int snd_hctl_poll_descriptors_revents(snd_hctl_t *ctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -unsigned int snd_hctl_get_count(snd_hctl_t *hctl); -int snd_hctl_set_compare(snd_hctl_t *hctl, snd_hctl_compare_t hsort); -snd_hctl_elem_t *snd_hctl_first_elem(snd_hctl_t *hctl); -snd_hctl_elem_t *snd_hctl_last_elem(snd_hctl_t *hctl); -snd_hctl_elem_t *snd_hctl_find_elem(snd_hctl_t *hctl, const snd_ctl_elem_id_t *id); -void snd_hctl_set_callback(snd_hctl_t *hctl, snd_hctl_callback_t callback); -void snd_hctl_set_callback_private(snd_hctl_t *hctl, void *data); -void *snd_hctl_get_callback_private(snd_hctl_t *hctl); -int snd_hctl_load(snd_hctl_t *hctl); -int snd_hctl_free(snd_hctl_t *hctl); -int snd_hctl_handle_events(snd_hctl_t *hctl); -const char *snd_hctl_name(snd_hctl_t *hctl); -int snd_hctl_wait(snd_hctl_t *hctl, int timeout); -snd_ctl_t *snd_hctl_ctl(snd_hctl_t *hctl); - -snd_hctl_elem_t *snd_hctl_elem_next(snd_hctl_elem_t *elem); -snd_hctl_elem_t *snd_hctl_elem_prev(snd_hctl_elem_t *elem); -int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t * info); -int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value); -int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value); -int snd_hctl_elem_tlv_read(snd_hctl_elem_t *elem, unsigned int *tlv, unsigned int tlv_size); -int snd_hctl_elem_tlv_write(snd_hctl_elem_t *elem, const unsigned int *tlv); -int snd_hctl_elem_tlv_command(snd_hctl_elem_t *elem, const unsigned int *tlv); - -snd_hctl_t *snd_hctl_elem_get_hctl(snd_hctl_elem_t *elem); - -void snd_hctl_elem_get_id(const snd_hctl_elem_t *obj, snd_ctl_elem_id_t *ptr); -unsigned int snd_hctl_elem_get_numid(const snd_hctl_elem_t *obj); -snd_ctl_elem_iface_t snd_hctl_elem_get_interface(const snd_hctl_elem_t *obj); -unsigned int snd_hctl_elem_get_device(const snd_hctl_elem_t *obj); -unsigned int snd_hctl_elem_get_subdevice(const snd_hctl_elem_t *obj); -const char *snd_hctl_elem_get_name(const snd_hctl_elem_t *obj); -unsigned int snd_hctl_elem_get_index(const snd_hctl_elem_t *obj); -void snd_hctl_elem_set_callback(snd_hctl_elem_t *obj, snd_hctl_elem_callback_t val); -void * snd_hctl_elem_get_callback_private(const snd_hctl_elem_t *obj); -void snd_hctl_elem_set_callback_private(snd_hctl_elem_t *obj, void * val); - -/** \} */ - -/** \} */ - -/** - * \defgroup SControl Setup Control Interface - * \ingroup Control - * The setup control interface - set or modify control elements from a configuration file. - * \{ - */ - -int snd_sctl_build(snd_sctl_t **ctl, snd_ctl_t *handle, snd_config_t *config, - snd_config_t *private_data, int mode); -int snd_sctl_free(snd_sctl_t *handle); -int snd_sctl_install(snd_sctl_t *handle); -int snd_sctl_remove(snd_sctl_t *handle); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_CONTROL_H */ diff --git a/raylib/external/alsa/control_external.h b/raylib/external/alsa/control_external.h deleted file mode 100644 index 12958e7..0000000 --- a/raylib/external/alsa/control_external.h +++ /dev/null @@ -1,286 +0,0 @@ -/** - * \file include/control_external.h - * \brief External control plugin SDK - * \author Takashi Iwai - * \date 2005 - * - * External control plugin SDK. - */ - -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __ALSA_CONTROL_EXTERNAL_H -#define __ALSA_CONTROL_EXTERNAL_H - -#include "control.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup CtlPlugin_SDK External Control Plugin SDK - * \{ - */ - -/** - * Define the object entry for external control plugins - */ -#define SND_CTL_PLUGIN_ENTRY(name) _snd_ctl_##name##_open - -/** - * Define the symbols of the given control plugin with versions - */ -#define SND_CTL_PLUGIN_SYMBOL(name) SND_DLSYM_BUILD_VERSION(SND_CTL_PLUGIN_ENTRY(name), SND_CONTROL_DLSYM_VERSION); - -/** - * Define the control plugin - */ -#define SND_CTL_PLUGIN_DEFINE_FUNC(plugin) \ -int SND_CTL_PLUGIN_ENTRY(plugin) (snd_ctl_t **handlep, const char *name,\ - snd_config_t *root, snd_config_t *conf, int mode) - -/** External control plugin handle */ -typedef struct snd_ctl_ext snd_ctl_ext_t; -/** Callback table of control ext */ -typedef struct snd_ctl_ext_callback snd_ctl_ext_callback_t; -/** Key to access a control pointer */ -typedef unsigned long snd_ctl_ext_key_t; -#ifdef DOC_HIDDEN -/* redefine typedef's for stupid doxygen */ -typedef snd_ctl_ext snd_ctl_ext_t; -typedef snd_ctl_ext_callback snd_ctl_ext_callback_t; -#endif -/** Callback to handle TLV commands. */ -typedef int (snd_ctl_ext_tlv_rw_t)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int op_flag, unsigned int numid, - unsigned int *tlv, unsigned int tlv_size); - -/* - * Protocol version - */ -#define SND_CTL_EXT_VERSION_MAJOR 1 /**< Protocol major version */ -#define SND_CTL_EXT_VERSION_MINOR 0 /**< Protocol minor version */ -#define SND_CTL_EXT_VERSION_TINY 1 /**< Protocol tiny version */ -/** - * external plugin protocol version - */ -#define SND_CTL_EXT_VERSION ((SND_CTL_EXT_VERSION_MAJOR<<16) |\ - (SND_CTL_EXT_VERSION_MINOR<<8) |\ - (SND_CTL_EXT_VERSION_TINY)) - -/** Handle of control ext */ -struct snd_ctl_ext { - /** - * protocol version; #SND_CTL_EXT_VERSION must be filled here - * before calling #snd_ctl_ext_create() - */ - unsigned int version; - /** - * Index of this card; must be filled before calling #snd_ctl_ext_create() - */ - int card_idx; - /** - * ID string of this card; must be filled before calling #snd_ctl_ext_create() - */ - char id[16]; - /** - * Driver name of this card; must be filled before calling #snd_ctl_ext_create() - */ - char driver[16]; - /** - * short name of this card; must be filled before calling #snd_ctl_ext_create() - */ - char name[32]; - /** - * Long name of this card; must be filled before calling #snd_ctl_ext_create() - */ - char longname[80]; - /** - * Mixer name of this card; must be filled before calling #snd_ctl_ext_create() - */ - char mixername[80]; - /** - * poll descriptor - */ - int poll_fd; - - /** - * callbacks of this plugin; must be filled before calling #snd_pcm_ioplug_create() - */ - const snd_ctl_ext_callback_t *callback; - /** - * private data, which can be used freely in the driver callbacks - */ - void *private_data; - /** - * control handle filled by #snd_ctl_ext_create() - */ - snd_ctl_t *handle; - - int nonblock; /**< non-block mode; read-only */ - int subscribed; /**< events subscribed; read-only */ - - /** - * optional TLV data for the control (since protocol 1.0.1) - */ - union { - snd_ctl_ext_tlv_rw_t *c; - const unsigned int *p; - } tlv; -}; - -/** Callback table of ext. */ -struct snd_ctl_ext_callback { - /** - * close the control handle; optional - */ - void (*close)(snd_ctl_ext_t *ext); - /** - * return the total number of elements; required - */ - int (*elem_count)(snd_ctl_ext_t *ext); - /** - * return the element id of the given offset (array index); required - */ - int (*elem_list)(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id); - /** - * convert the element id to a search key; required - */ - snd_ctl_ext_key_t (*find_elem)(snd_ctl_ext_t *ext, const snd_ctl_elem_id_t *id); - /** - * the destructor of the key; optional - */ - void (*free_key)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key); - /** - * get the attribute of the element; required - */ - int (*get_attribute)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, - int *type, unsigned int *acc, unsigned int *count); - /** - * get the element information of integer type - */ - int (*get_integer_info)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, - long *imin, long *imax, long *istep); - /** - * get the element information of integer64 type - */ - int (*get_integer64_info)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, - int64_t *imin, int64_t *imax, int64_t *istep); - /** - * get the element information of enumerated type - */ - int (*get_enumerated_info)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items); - /** - * get the name of the enumerated item - */ - int (*get_enumerated_name)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int item, - char *name, size_t name_max_len); - /** - * read the current values of integer type - */ - int (*read_integer)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value); - /** - * read the current values of integer64 type - */ - int (*read_integer64)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int64_t *value); - /** - * read the current values of enumerated type - */ - int (*read_enumerated)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items); - /** - * read the current values of bytes type - */ - int (*read_bytes)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned char *data, - size_t max_bytes); - /** - * read the current values of iec958 type - */ - int (*read_iec958)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, snd_aes_iec958_t *iec958); - /** - * update the current values of integer type with the given values - */ - int (*write_integer)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value); - /** - * update the current values of integer64 type with the given values - */ - int (*write_integer64)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int64_t *value); - /** - * update the current values of enumerated type with the given values - */ - int (*write_enumerated)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items); - /** - * update the current values of bytes type with the given values - */ - int (*write_bytes)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned char *data, - size_t max_bytes); - /** - * update the current values of iec958 type with the given values - */ - int (*write_iec958)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, snd_aes_iec958_t *iec958); - /** - * subscribe/unsubscribe the event notification; optional - */ - void (*subscribe_events)(snd_ctl_ext_t *ext, int subscribe); - /** - * read a pending notification event; optional - */ - int (*read_event)(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, unsigned int *event_mask); - /** - * return the number of poll descriptors; optional - */ - int (*poll_descriptors_count)(snd_ctl_ext_t *ext); - /** - * fill the poll descriptors; optional - */ - int (*poll_descriptors)(snd_ctl_ext_t *ext, struct pollfd *pfds, unsigned int space); - /** - * mangle the revents of poll descriptors - */ - int (*poll_revents)(snd_ctl_ext_t *ext, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -}; - -/** - * The access type bits stored in get_attribute callback - */ -typedef enum snd_ctl_ext_access { - SND_CTL_EXT_ACCESS_READ = (1<<0), - SND_CTL_EXT_ACCESS_WRITE = (1<<1), - SND_CTL_EXT_ACCESS_READWRITE = (3<<0), - SND_CTL_EXT_ACCESS_VOLATILE = (1<<2), - SND_CTL_EXT_ACCESS_TLV_READ = (1<<4), - SND_CTL_EXT_ACCESS_TLV_WRITE = (1<<5), - SND_CTL_EXT_ACCESS_TLV_READWRITE = (3<<4), - SND_CTL_EXT_ACCESS_TLV_COMMAND = (1<<6), - SND_CTL_EXT_ACCESS_INACTIVE = (1<<8), - SND_CTL_EXT_ACCESS_TLV_CALLBACK = (1<<28), -} snd_ctl_ext_access_t; - -/** - * find_elem callback returns this if no matching control element is found - */ -#define SND_CTL_EXT_KEY_NOT_FOUND (snd_ctl_ext_key_t)(-1) - -int snd_ctl_ext_create(snd_ctl_ext_t *ext, const char *name, int mode); -int snd_ctl_ext_delete(snd_ctl_ext_t *ext); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_CONTROL_EXTERNAL_H */ diff --git a/raylib/external/alsa/error.h b/raylib/external/alsa/error.h deleted file mode 100644 index 38ee070..0000000 --- a/raylib/external/alsa/error.h +++ /dev/null @@ -1,85 +0,0 @@ -/** - * \file include/error.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_ERROR_H -#define __ALSA_ERROR_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Error Error handling - * Error handling macros and functions. - * \{ - */ - -#define SND_ERROR_BEGIN 500000 /**< Lower boundary of sound error codes. */ -#define SND_ERROR_INCOMPATIBLE_VERSION (SND_ERROR_BEGIN+0) /**< Kernel/library protocols are not compatible. */ -#define SND_ERROR_ALISP_NIL (SND_ERROR_BEGIN+1) /**< Lisp encountered an error during acall. */ - -const char *snd_strerror(int errnum); - -/** - * \brief Error handler callback. - * \param file Source file name. - * \param line Line number. - * \param function Function name. - * \param err Value of \c errno, or 0 if not relevant. - * \param fmt \c printf(3) format. - * \param ... \c printf(3) arguments. - * - * A function of this type is called by the ALSA library when an error occurs. - * This function usually shows the message on the screen, and/or logs it. - */ -typedef void (*snd_lib_error_handler_t)(const char *file, int line, const char *function, int err, const char *fmt, ...) /* __attribute__ ((format (printf, 5, 6))) */; -extern snd_lib_error_handler_t snd_lib_error; -extern int snd_lib_error_set_handler(snd_lib_error_handler_t handler); - -#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 95) -#define SNDERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__) /**< Shows a sound error message. */ -#define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__) /**< Shows a system error message (related to \c errno). */ -#else -#define SNDERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args) /**< Shows a sound error message. */ -#define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args) /**< Shows a system error message (related to \c errno). */ -#endif - -/** \} */ - -#ifdef __cplusplus -} -#endif - -/** Local error handler function type */ -typedef void (*snd_local_error_handler_t)(const char *file, int line, - const char *func, int err, - const char *fmt, va_list arg); - -snd_local_error_handler_t snd_lib_error_set_local(snd_local_error_handler_t func); - -#endif /* __ALSA_ERROR_H */ - diff --git a/raylib/external/alsa/global.h b/raylib/external/alsa/global.h deleted file mode 100644 index 16a26dc..0000000 --- a/raylib/external/alsa/global.h +++ /dev/null @@ -1,161 +0,0 @@ -/** - * \file include/global.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_GLOBAL_H_ -#define __ALSA_GLOBAL_H_ - -/* for timeval and timespec */ -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Global Global defines and functions - * Global defines and functions. - * \par - * The ALSA library implementation uses these macros and functions. - * Most applications probably do not need them. - * \{ - */ - -const char *snd_asoundlib_version(void); - -#ifndef ATTRIBUTE_UNUSED -/** do not print warning (gcc) when function parameter is not used */ -#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -#endif - -#ifdef PIC /* dynamic build */ - -/** \hideinitializer \brief Helper macro for #SND_DLSYM_BUILD_VERSION. */ -#define __SND_DLSYM_VERSION(name, version) _ ## name ## version -/** - * \hideinitializer - * \brief Appends the build version to the name of a versioned dynamic symbol. - */ -#define SND_DLSYM_BUILD_VERSION(name, version) char __SND_DLSYM_VERSION(name, version); - -#else /* static build */ - -struct snd_dlsym_link { - struct snd_dlsym_link *next; - const char *dlsym_name; - const void *dlsym_ptr; -}; - -extern struct snd_dlsym_link *snd_dlsym_start; - -/** \hideinitializer \brief Helper macro for #SND_DLSYM_BUILD_VERSION. */ -#define __SND_DLSYM_VERSION(prefix, name, version) _ ## prefix ## name ## version -/** - * \hideinitializer - * \brief Appends the build version to the name of a versioned dynamic symbol. - */ -#define SND_DLSYM_BUILD_VERSION(name, version) \ - static struct snd_dlsym_link __SND_DLSYM_VERSION(snd_dlsym_, name, version); \ - void __SND_DLSYM_VERSION(snd_dlsym_constructor_, name, version) (void) __attribute__ ((constructor)); \ - void __SND_DLSYM_VERSION(snd_dlsym_constructor_, name, version) (void) { \ - __SND_DLSYM_VERSION(snd_dlsym_, name, version).next = snd_dlsym_start; \ - __SND_DLSYM_VERSION(snd_dlsym_, name, version).dlsym_name = # name; \ - __SND_DLSYM_VERSION(snd_dlsym_, name, version).dlsym_ptr = (void *)&name; \ - snd_dlsym_start = &__SND_DLSYM_VERSION(snd_dlsym_, name, version); \ - } - -#endif - -#ifndef __STRING -/** \brief Return 'x' argument as string */ -#define __STRING(x) #x -#endif - -/** \brief Returns the version of a dynamic symbol as a string. */ -#define SND_DLSYM_VERSION(version) __STRING(version) - -void *snd_dlopen(const char *file, int mode); -void *snd_dlsym(void *handle, const char *name, const char *version); -int snd_dlclose(void *handle); - - -/** \brief alloca helper macro. */ -#define __snd_alloca(ptr,type) do { *ptr = (type##_t *) alloca(type##_sizeof()); memset(*ptr, 0, type##_sizeof()); } while (0) - -/** - * \brief Internal structure for an async notification client handler. - * - * The ALSA library uses a pointer to this structure as a handle to an async - * notification object. Applications don't access its contents directly. - */ -typedef struct _snd_async_handler snd_async_handler_t; - -/** - * \brief Async notification callback. - * - * See the #snd_async_add_handler function for details. - */ -typedef void (*snd_async_callback_t)(snd_async_handler_t *handler); - -int snd_async_add_handler(snd_async_handler_t **handler, int fd, - snd_async_callback_t callback, void *private_data); -int snd_async_del_handler(snd_async_handler_t *handler); -int snd_async_handler_get_fd(snd_async_handler_t *handler); -int snd_async_handler_get_signo(snd_async_handler_t *handler); -void *snd_async_handler_get_callback_private(snd_async_handler_t *handler); - -struct snd_shm_area *snd_shm_area_create(int shmid, void *ptr); -struct snd_shm_area *snd_shm_area_share(struct snd_shm_area *area); -int snd_shm_area_destroy(struct snd_shm_area *area); - -int snd_user_file(const char *file, char **result); - -#ifdef __GLIBC__ -#if !defined(_POSIX_C_SOURCE) && !defined(_POSIX_SOURCE) -struct timeval { - time_t tv_sec; /* seconds */ - long tv_usec; /* microseconds */ -}; - -struct timespec { - time_t tv_sec; /* seconds */ - long tv_nsec; /* nanoseconds */ -}; -#endif -#endif - -/** Timestamp */ -typedef struct timeval snd_timestamp_t; -/** Hi-res timestamp */ -typedef struct timespec snd_htimestamp_t; - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_GLOBAL_H */ diff --git a/raylib/external/alsa/hwdep.h b/raylib/external/alsa/hwdep.h deleted file mode 100644 index ac71368..0000000 --- a/raylib/external/alsa/hwdep.h +++ /dev/null @@ -1,172 +0,0 @@ -/** - * \file include/hwdep.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_HWDEP_H -#define __ALSA_HWDEP_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup HwDep Hardware Dependant Interface - * The Hardware Dependant Interface. - * \{ - */ - -/** dlsym version for interface entry callback */ -#define SND_HWDEP_DLSYM_VERSION _dlsym_hwdep_001 - -/** HwDep information container */ -typedef struct _snd_hwdep_info snd_hwdep_info_t; - -/** HwDep DSP status container */ -typedef struct _snd_hwdep_dsp_status snd_hwdep_dsp_status_t; - -/** HwDep DSP image container */ -typedef struct _snd_hwdep_dsp_image snd_hwdep_dsp_image_t; - -/** HwDep interface */ -typedef enum _snd_hwdep_iface { - SND_HWDEP_IFACE_OPL2 = 0, /**< OPL2 raw driver */ - SND_HWDEP_IFACE_OPL3, /**< OPL3 raw driver */ - SND_HWDEP_IFACE_OPL4, /**< OPL4 raw driver */ - SND_HWDEP_IFACE_SB16CSP, /**< SB16CSP driver */ - SND_HWDEP_IFACE_EMU10K1, /**< EMU10K1 driver */ - SND_HWDEP_IFACE_YSS225, /**< YSS225 driver */ - SND_HWDEP_IFACE_ICS2115, /**< ICS2115 driver */ - SND_HWDEP_IFACE_SSCAPE, /**< Ensoniq SoundScape ISA card (MC68EC000) */ - SND_HWDEP_IFACE_VX, /**< Digigram VX cards */ - SND_HWDEP_IFACE_MIXART, /**< Digigram miXart cards */ - SND_HWDEP_IFACE_USX2Y, /**< Tascam US122, US224 & US428 usb */ - SND_HWDEP_IFACE_EMUX_WAVETABLE, /**< EmuX wavetable */ - SND_HWDEP_IFACE_BLUETOOTH, /**< Bluetooth audio */ - SND_HWDEP_IFACE_USX2Y_PCM, /**< Tascam US122, US224 & US428 raw USB PCM */ - SND_HWDEP_IFACE_PCXHR, /**< Digigram PCXHR */ - SND_HWDEP_IFACE_SB_RC, /**< SB Extigy/Audigy2NX remote control */ - SND_HWDEP_IFACE_HDA, /**< HD-audio */ - SND_HWDEP_IFACE_USB_STREAM, /**< direct access to usb stream */ - SND_HWDEP_IFACE_FW_DICE, /**< TC DICE FireWire device */ - SND_HWDEP_IFACE_FW_FIREWORKS, /**< Echo Audio Fireworks based device */ - SND_HWDEP_IFACE_FW_BEBOB, /**< BridgeCo BeBoB based device */ - SND_HWDEP_IFACE_FW_OXFW, /**< Oxford OXFW970/971 based device */ - SND_HWDEP_IFACE_FW_DIGI00X, /* Digidesign Digi 002/003 family */ - SND_HWDEP_IFACE_FW_TASCAM, /* TASCAM FireWire series */ - SND_HWDEP_IFACE_LINE6, /* Line6 USB processors */ - SND_HWDEP_IFACE_FW_MOTU, /* MOTU FireWire series */ - SND_HWDEP_IFACE_FW_FIREFACE, /* RME Fireface series */ - - SND_HWDEP_IFACE_LAST = SND_HWDEP_IFACE_FW_FIREFACE, /**< last known hwdep interface */ -} snd_hwdep_iface_t; - -/** open for reading */ -#define SND_HWDEP_OPEN_READ (O_RDONLY) -/** open for writing */ -#define SND_HWDEP_OPEN_WRITE (O_WRONLY) -/** open for reading and writing */ -#define SND_HWDEP_OPEN_DUPLEX (O_RDWR) -/** open mode flag: open in nonblock mode */ -#define SND_HWDEP_OPEN_NONBLOCK (O_NONBLOCK) - -/** HwDep handle type */ -typedef enum _snd_hwdep_type { - /** Kernel level HwDep */ - SND_HWDEP_TYPE_HW, - /** Shared memory client HwDep (not yet implemented) */ - SND_HWDEP_TYPE_SHM, - /** INET client HwDep (not yet implemented) */ - SND_HWDEP_TYPE_INET -} snd_hwdep_type_t; - -/** HwDep handle */ -typedef struct _snd_hwdep snd_hwdep_t; - -int snd_hwdep_open(snd_hwdep_t **hwdep, const char *name, int mode); -int snd_hwdep_close(snd_hwdep_t *hwdep); -int snd_hwdep_poll_descriptors(snd_hwdep_t *hwdep, struct pollfd *pfds, unsigned int space); -int snd_hwdep_poll_descriptors_count(snd_hwdep_t *hwdep); -int snd_hwdep_poll_descriptors_revents(snd_hwdep_t *hwdep, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -int snd_hwdep_nonblock(snd_hwdep_t *hwdep, int nonblock); -int snd_hwdep_info(snd_hwdep_t *hwdep, snd_hwdep_info_t * info); -int snd_hwdep_dsp_status(snd_hwdep_t *hwdep, snd_hwdep_dsp_status_t *status); -int snd_hwdep_dsp_load(snd_hwdep_t *hwdep, snd_hwdep_dsp_image_t *block); -int snd_hwdep_ioctl(snd_hwdep_t *hwdep, unsigned int request, void * arg); -ssize_t snd_hwdep_write(snd_hwdep_t *hwdep, const void *buffer, size_t size); -ssize_t snd_hwdep_read(snd_hwdep_t *hwdep, void *buffer, size_t size); - -size_t snd_hwdep_info_sizeof(void); -/** allocate #snd_hwdep_info_t container on stack */ -#define snd_hwdep_info_alloca(ptr) __snd_alloca(ptr, snd_hwdep_info) -int snd_hwdep_info_malloc(snd_hwdep_info_t **ptr); -void snd_hwdep_info_free(snd_hwdep_info_t *obj); -void snd_hwdep_info_copy(snd_hwdep_info_t *dst, const snd_hwdep_info_t *src); - -unsigned int snd_hwdep_info_get_device(const snd_hwdep_info_t *obj); -int snd_hwdep_info_get_card(const snd_hwdep_info_t *obj); -const char *snd_hwdep_info_get_id(const snd_hwdep_info_t *obj); -const char *snd_hwdep_info_get_name(const snd_hwdep_info_t *obj); -snd_hwdep_iface_t snd_hwdep_info_get_iface(const snd_hwdep_info_t *obj); -void snd_hwdep_info_set_device(snd_hwdep_info_t *obj, unsigned int val); - -size_t snd_hwdep_dsp_status_sizeof(void); -/** allocate #snd_hwdep_dsp_status_t container on stack */ -#define snd_hwdep_dsp_status_alloca(ptr) __snd_alloca(ptr, snd_hwdep_dsp_status) -int snd_hwdep_dsp_status_malloc(snd_hwdep_dsp_status_t **ptr); -void snd_hwdep_dsp_status_free(snd_hwdep_dsp_status_t *obj); -void snd_hwdep_dsp_status_copy(snd_hwdep_dsp_status_t *dst, const snd_hwdep_dsp_status_t *src); - -unsigned int snd_hwdep_dsp_status_get_version(const snd_hwdep_dsp_status_t *obj); -const char *snd_hwdep_dsp_status_get_id(const snd_hwdep_dsp_status_t *obj); -unsigned int snd_hwdep_dsp_status_get_num_dsps(const snd_hwdep_dsp_status_t *obj); -unsigned int snd_hwdep_dsp_status_get_dsp_loaded(const snd_hwdep_dsp_status_t *obj); -unsigned int snd_hwdep_dsp_status_get_chip_ready(const snd_hwdep_dsp_status_t *obj); - -size_t snd_hwdep_dsp_image_sizeof(void); -/** allocate #snd_hwdep_dsp_image_t container on stack */ -#define snd_hwdep_dsp_image_alloca(ptr) __snd_alloca(ptr, snd_hwdep_dsp_image) -int snd_hwdep_dsp_image_malloc(snd_hwdep_dsp_image_t **ptr); -void snd_hwdep_dsp_image_free(snd_hwdep_dsp_image_t *obj); -void snd_hwdep_dsp_image_copy(snd_hwdep_dsp_image_t *dst, const snd_hwdep_dsp_image_t *src); - -unsigned int snd_hwdep_dsp_image_get_index(const snd_hwdep_dsp_image_t *obj); -const char *snd_hwdep_dsp_image_get_name(const snd_hwdep_dsp_image_t *obj); -const void *snd_hwdep_dsp_image_get_image(const snd_hwdep_dsp_image_t *obj); -size_t snd_hwdep_dsp_image_get_length(const snd_hwdep_dsp_image_t *obj); - -void snd_hwdep_dsp_image_set_index(snd_hwdep_dsp_image_t *obj, unsigned int _index); -void snd_hwdep_dsp_image_set_name(snd_hwdep_dsp_image_t *obj, const char *name); -void snd_hwdep_dsp_image_set_image(snd_hwdep_dsp_image_t *obj, void *buffer); -void snd_hwdep_dsp_image_set_length(snd_hwdep_dsp_image_t *obj, size_t length); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_HWDEP_H */ - diff --git a/raylib/external/alsa/input.h b/raylib/external/alsa/input.h deleted file mode 100644 index fc5d0e6..0000000 --- a/raylib/external/alsa/input.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * \file include/input.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_INPUT_H -#define __ALSA_INPUT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Input Input Interface - * - * The input functions present an interface similar to the stdio functions - * on top of different underlying input sources. - * - * The #snd_config_load function uses such an input handle to be able to - * load configurations not only from standard files but also from other - * sources, e.g. from memory buffers. - * - * \{ - */ - -/** - * \brief Internal structure for an input object. - * - * The ALSA library uses a pointer to this structure as a handle to an - * input object. Applications don't access its contents directly. - */ -typedef struct _snd_input snd_input_t; - -/** Input type. */ -typedef enum _snd_input_type { - /** Input from a stdio stream. */ - SND_INPUT_STDIO, - /** Input from a memory buffer. */ - SND_INPUT_BUFFER -} snd_input_type_t; - -int snd_input_stdio_open(snd_input_t **inputp, const char *file, const char *mode); -int snd_input_stdio_attach(snd_input_t **inputp, FILE *fp, int _close); -int snd_input_buffer_open(snd_input_t **inputp, const char *buffer, ssize_t size); -int snd_input_close(snd_input_t *input); -int snd_input_scanf(snd_input_t *input, const char *format, ...) -#ifndef DOC_HIDDEN - __attribute__ ((format (scanf, 2, 3))) -#endif - ; -char *snd_input_gets(snd_input_t *input, char *str, size_t size); -int snd_input_getc(snd_input_t *input); -int snd_input_ungetc(snd_input_t *input, int c); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_INPUT_H */ diff --git a/raylib/external/alsa/mixer.h b/raylib/external/alsa/mixer.h deleted file mode 100644 index 066d978..0000000 --- a/raylib/external/alsa/mixer.h +++ /dev/null @@ -1,317 +0,0 @@ -/** - * \file include/mixer.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_MIXER_H -#define __ALSA_MIXER_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Mixer Mixer Interface - * The mixer interface. - * \{ - */ - -/** Mixer handle */ -typedef struct _snd_mixer snd_mixer_t; -/** Mixer elements class handle */ -typedef struct _snd_mixer_class snd_mixer_class_t; -/** Mixer element handle */ -typedef struct _snd_mixer_elem snd_mixer_elem_t; - -/** - * \brief Mixer callback function - * \param mixer Mixer handle - * \param mask event mask - * \param elem related mixer element (if any) - * \return 0 on success otherwise a negative error code - */ -typedef int (*snd_mixer_callback_t)(snd_mixer_t *ctl, - unsigned int mask, - snd_mixer_elem_t *elem); - -/** - * \brief Mixer element callback function - * \param elem Mixer element - * \param mask event mask - * \return 0 on success otherwise a negative error code - */ -typedef int (*snd_mixer_elem_callback_t)(snd_mixer_elem_t *elem, - unsigned int mask); - -/** - * \brief Compare function for sorting mixer elements - * \param e1 First element - * \param e2 Second element - * \return -1 if e1 < e2, 0 if e1 == e2, 1 if e1 > e2 - */ -typedef int (*snd_mixer_compare_t)(const snd_mixer_elem_t *e1, - const snd_mixer_elem_t *e2); - -/** - * \brief Event callback for the mixer class - * \param class_ Mixer class - * \param mask Event mask (SND_CTL_EVENT_*) - * \param helem HCTL element which invoked the event - * \param melem Mixer element associated to HCTL element - * \return zero if success, otherwise a negative error value - */ -typedef int (*snd_mixer_event_t)(snd_mixer_class_t *class_, unsigned int mask, - snd_hctl_elem_t *helem, snd_mixer_elem_t *melem); - - -/** Mixer element type */ -typedef enum _snd_mixer_elem_type { - /* Simple mixer elements */ - SND_MIXER_ELEM_SIMPLE, - SND_MIXER_ELEM_LAST = SND_MIXER_ELEM_SIMPLE -} snd_mixer_elem_type_t; - -int snd_mixer_open(snd_mixer_t **mixer, int mode); -int snd_mixer_close(snd_mixer_t *mixer); -snd_mixer_elem_t *snd_mixer_first_elem(snd_mixer_t *mixer); -snd_mixer_elem_t *snd_mixer_last_elem(snd_mixer_t *mixer); -int snd_mixer_handle_events(snd_mixer_t *mixer); -int snd_mixer_attach(snd_mixer_t *mixer, const char *name); -int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl); -int snd_mixer_detach(snd_mixer_t *mixer, const char *name); -int snd_mixer_detach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl); -int snd_mixer_get_hctl(snd_mixer_t *mixer, const char *name, snd_hctl_t **hctl); -int snd_mixer_poll_descriptors_count(snd_mixer_t *mixer); -int snd_mixer_poll_descriptors(snd_mixer_t *mixer, struct pollfd *pfds, unsigned int space); -int snd_mixer_poll_descriptors_revents(snd_mixer_t *mixer, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -int snd_mixer_load(snd_mixer_t *mixer); -void snd_mixer_free(snd_mixer_t *mixer); -int snd_mixer_wait(snd_mixer_t *mixer, int timeout); -int snd_mixer_set_compare(snd_mixer_t *mixer, snd_mixer_compare_t msort); -void snd_mixer_set_callback(snd_mixer_t *obj, snd_mixer_callback_t val); -void * snd_mixer_get_callback_private(const snd_mixer_t *obj); -void snd_mixer_set_callback_private(snd_mixer_t *obj, void * val); -unsigned int snd_mixer_get_count(const snd_mixer_t *obj); -int snd_mixer_class_unregister(snd_mixer_class_t *clss); - -snd_mixer_elem_t *snd_mixer_elem_next(snd_mixer_elem_t *elem); -snd_mixer_elem_t *snd_mixer_elem_prev(snd_mixer_elem_t *elem); -void snd_mixer_elem_set_callback(snd_mixer_elem_t *obj, snd_mixer_elem_callback_t val); -void * snd_mixer_elem_get_callback_private(const snd_mixer_elem_t *obj); -void snd_mixer_elem_set_callback_private(snd_mixer_elem_t *obj, void * val); -snd_mixer_elem_type_t snd_mixer_elem_get_type(const snd_mixer_elem_t *obj); - -int snd_mixer_class_register(snd_mixer_class_t *class_, snd_mixer_t *mixer); -int snd_mixer_elem_new(snd_mixer_elem_t **elem, - snd_mixer_elem_type_t type, - int compare_weight, - void *private_data, - void (*private_free)(snd_mixer_elem_t *elem)); -int snd_mixer_elem_add(snd_mixer_elem_t *elem, snd_mixer_class_t *class_); -int snd_mixer_elem_remove(snd_mixer_elem_t *elem); -void snd_mixer_elem_free(snd_mixer_elem_t *elem); -int snd_mixer_elem_info(snd_mixer_elem_t *elem); -int snd_mixer_elem_value(snd_mixer_elem_t *elem); -int snd_mixer_elem_attach(snd_mixer_elem_t *melem, snd_hctl_elem_t *helem); -int snd_mixer_elem_detach(snd_mixer_elem_t *melem, snd_hctl_elem_t *helem); -int snd_mixer_elem_empty(snd_mixer_elem_t *melem); -void *snd_mixer_elem_get_private(const snd_mixer_elem_t *melem); - -size_t snd_mixer_class_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_mixer_class_t using standard alloca - * \param ptr returned pointer - */ -#define snd_mixer_class_alloca(ptr) __snd_alloca(ptr, snd_mixer_class) -int snd_mixer_class_malloc(snd_mixer_class_t **ptr); -void snd_mixer_class_free(snd_mixer_class_t *obj); -void snd_mixer_class_copy(snd_mixer_class_t *dst, const snd_mixer_class_t *src); -snd_mixer_t *snd_mixer_class_get_mixer(const snd_mixer_class_t *class_); -snd_mixer_event_t snd_mixer_class_get_event(const snd_mixer_class_t *class_); -void *snd_mixer_class_get_private(const snd_mixer_class_t *class_); -snd_mixer_compare_t snd_mixer_class_get_compare(const snd_mixer_class_t *class_); -int snd_mixer_class_set_event(snd_mixer_class_t *class_, snd_mixer_event_t event); -int snd_mixer_class_set_private(snd_mixer_class_t *class_, void *private_data); -int snd_mixer_class_set_private_free(snd_mixer_class_t *class_, void (*private_free)(snd_mixer_class_t *)); -int snd_mixer_class_set_compare(snd_mixer_class_t *class_, snd_mixer_compare_t compare); - -/** - * \defgroup SimpleMixer Simple Mixer Interface - * \ingroup Mixer - * The simple mixer interface. - * \{ - */ - -/* Simple mixer elements API */ - -/** Mixer simple element channel identifier */ -typedef enum _snd_mixer_selem_channel_id { - /** Unknown */ - SND_MIXER_SCHN_UNKNOWN = -1, - /** Front left */ - SND_MIXER_SCHN_FRONT_LEFT = 0, - /** Front right */ - SND_MIXER_SCHN_FRONT_RIGHT, - /** Rear left */ - SND_MIXER_SCHN_REAR_LEFT, - /** Rear right */ - SND_MIXER_SCHN_REAR_RIGHT, - /** Front center */ - SND_MIXER_SCHN_FRONT_CENTER, - /** Woofer */ - SND_MIXER_SCHN_WOOFER, - /** Side Left */ - SND_MIXER_SCHN_SIDE_LEFT, - /** Side Right */ - SND_MIXER_SCHN_SIDE_RIGHT, - /** Rear Center */ - SND_MIXER_SCHN_REAR_CENTER, - SND_MIXER_SCHN_LAST = 31, - /** Mono (Front left alias) */ - SND_MIXER_SCHN_MONO = SND_MIXER_SCHN_FRONT_LEFT -} snd_mixer_selem_channel_id_t; - -/** Mixer simple element - register options - abstraction level */ -enum snd_mixer_selem_regopt_abstract { - /** no abstraction - try use all universal controls from driver */ - SND_MIXER_SABSTRACT_NONE = 0, - /** basic abstraction - Master,PCM,CD,Aux,Record-Gain etc. */ - SND_MIXER_SABSTRACT_BASIC, -}; - -/** Mixer simple element - register options */ -struct snd_mixer_selem_regopt { - /** structure version */ - int ver; - /** v1: abstract layer selection */ - enum snd_mixer_selem_regopt_abstract abstract; - /** v1: device name (must be NULL when playback_pcm or capture_pcm != NULL) */ - const char *device; - /** v1: playback PCM connected to mixer device (NULL == none) */ - snd_pcm_t *playback_pcm; - /** v1: capture PCM connected to mixer device (NULL == none) */ - snd_pcm_t *capture_pcm; -}; - -/** Mixer simple element identifier */ -typedef struct _snd_mixer_selem_id snd_mixer_selem_id_t; - -const char *snd_mixer_selem_channel_name(snd_mixer_selem_channel_id_t channel); - -int snd_mixer_selem_register(snd_mixer_t *mixer, - struct snd_mixer_selem_regopt *options, - snd_mixer_class_t **classp); -void snd_mixer_selem_get_id(snd_mixer_elem_t *element, - snd_mixer_selem_id_t *id); -const char *snd_mixer_selem_get_name(snd_mixer_elem_t *elem); -unsigned int snd_mixer_selem_get_index(snd_mixer_elem_t *elem); -snd_mixer_elem_t *snd_mixer_find_selem(snd_mixer_t *mixer, - const snd_mixer_selem_id_t *id); - -int snd_mixer_selem_is_active(snd_mixer_elem_t *elem); -int snd_mixer_selem_is_playback_mono(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_playback_channel(snd_mixer_elem_t *obj, snd_mixer_selem_channel_id_t channel); -int snd_mixer_selem_is_capture_mono(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_capture_channel(snd_mixer_elem_t *obj, snd_mixer_selem_channel_id_t channel); -int snd_mixer_selem_get_capture_group(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_common_volume(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_playback_volume(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_playback_volume_joined(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_capture_volume(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_capture_volume_joined(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_common_switch(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_playback_switch(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_playback_switch_joined(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_capture_switch(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_capture_switch_joined(snd_mixer_elem_t *elem); -int snd_mixer_selem_has_capture_switch_exclusive(snd_mixer_elem_t *elem); - -int snd_mixer_selem_ask_playback_vol_dB(snd_mixer_elem_t *elem, long value, long *dBvalue); -int snd_mixer_selem_ask_capture_vol_dB(snd_mixer_elem_t *elem, long value, long *dBvalue); -int snd_mixer_selem_ask_playback_dB_vol(snd_mixer_elem_t *elem, long dBvalue, int dir, long *value); -int snd_mixer_selem_ask_capture_dB_vol(snd_mixer_elem_t *elem, long dBvalue, int dir, long *value); -int snd_mixer_selem_get_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value); -int snd_mixer_selem_get_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value); -int snd_mixer_selem_get_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value); -int snd_mixer_selem_get_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value); -int snd_mixer_selem_get_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int *value); -int snd_mixer_selem_get_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int *value); -int snd_mixer_selem_set_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value); -int snd_mixer_selem_set_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value); -int snd_mixer_selem_set_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir); -int snd_mixer_selem_set_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir); -int snd_mixer_selem_set_playback_volume_all(snd_mixer_elem_t *elem, long value); -int snd_mixer_selem_set_capture_volume_all(snd_mixer_elem_t *elem, long value); -int snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *elem, long value, int dir); -int snd_mixer_selem_set_capture_dB_all(snd_mixer_elem_t *elem, long value, int dir); -int snd_mixer_selem_set_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value); -int snd_mixer_selem_set_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value); -int snd_mixer_selem_set_playback_switch_all(snd_mixer_elem_t *elem, int value); -int snd_mixer_selem_set_capture_switch_all(snd_mixer_elem_t *elem, int value); -int snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem, - long *min, long *max); -int snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem, - long *min, long *max); -int snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem, - long min, long max); -int snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem, - long *min, long *max); -int snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem, - long *min, long *max); -int snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem, - long min, long max); - -int snd_mixer_selem_is_enumerated(snd_mixer_elem_t *elem); -int snd_mixer_selem_is_enum_playback(snd_mixer_elem_t *elem); -int snd_mixer_selem_is_enum_capture(snd_mixer_elem_t *elem); -int snd_mixer_selem_get_enum_items(snd_mixer_elem_t *elem); -int snd_mixer_selem_get_enum_item_name(snd_mixer_elem_t *elem, unsigned int idx, size_t maxlen, char *str); -int snd_mixer_selem_get_enum_item(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int *idxp); -int snd_mixer_selem_set_enum_item(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int idx); - -size_t snd_mixer_selem_id_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_mixer_selem_id_t using standard alloca - * \param ptr returned pointer - */ -#define snd_mixer_selem_id_alloca(ptr) __snd_alloca(ptr, snd_mixer_selem_id) -int snd_mixer_selem_id_malloc(snd_mixer_selem_id_t **ptr); -void snd_mixer_selem_id_free(snd_mixer_selem_id_t *obj); -void snd_mixer_selem_id_copy(snd_mixer_selem_id_t *dst, const snd_mixer_selem_id_t *src); -const char *snd_mixer_selem_id_get_name(const snd_mixer_selem_id_t *obj); -unsigned int snd_mixer_selem_id_get_index(const snd_mixer_selem_id_t *obj); -void snd_mixer_selem_id_set_name(snd_mixer_selem_id_t *obj, const char *val); -void snd_mixer_selem_id_set_index(snd_mixer_selem_id_t *obj, unsigned int val); - -/** \} */ - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_MIXER_H */ - diff --git a/raylib/external/alsa/mixer_abst.h b/raylib/external/alsa/mixer_abst.h deleted file mode 100644 index 7844b19..0000000 --- a/raylib/external/alsa/mixer_abst.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * \file include/mixer_abst.h - * \brief Mixer abstract implementation interface library for the ALSA library - * \author Jaroslav Kysela - * \date 2005 - * - * Mixer abstact implementation interface library for the ALSA library - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_MIXER_ABST_H -#define __ALSA_MIXER_ABST_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Mixer_Abstract Mixer Abstact Module Interface - * The mixer abstact module interface. - * \{ - */ - -#define SM_PLAY 0 -#define SM_CAPT 1 - -#define SM_CAP_GVOLUME (1<<1) -#define SM_CAP_GSWITCH (1<<2) -#define SM_CAP_PVOLUME (1<<3) -#define SM_CAP_PVOLUME_JOIN (1<<4) -#define SM_CAP_PSWITCH (1<<5) -#define SM_CAP_PSWITCH_JOIN (1<<6) -#define SM_CAP_CVOLUME (1<<7) -#define SM_CAP_CVOLUME_JOIN (1<<8) -#define SM_CAP_CSWITCH (1<<9) -#define SM_CAP_CSWITCH_JOIN (1<<10) -#define SM_CAP_CSWITCH_EXCL (1<<11) -#define SM_CAP_PENUM (1<<12) -#define SM_CAP_CENUM (1<<13) -/* SM_CAP_* 24-31 => private for module use */ - -#define SM_OPS_IS_ACTIVE 0 -#define SM_OPS_IS_MONO 1 -#define SM_OPS_IS_CHANNEL 2 -#define SM_OPS_IS_ENUMERATED 3 -#define SM_OPS_IS_ENUMCNT 4 - -#define sm_selem(x) ((sm_selem_t *)((x)->private_data)) -#define sm_selem_ops(x) ((sm_selem_t *)((x)->private_data))->ops - -typedef struct _sm_selem { - snd_mixer_selem_id_t *id; - struct sm_elem_ops *ops; - unsigned int caps; - unsigned int capture_group; -} sm_selem_t; - -typedef struct _sm_class_basic { - char *device; - snd_ctl_t *ctl; - snd_hctl_t *hctl; - snd_ctl_card_info_t *info; -} sm_class_basic_t; - -struct sm_elem_ops { - int (*is)(snd_mixer_elem_t *elem, int dir, int cmd, int val); - int (*get_range)(snd_mixer_elem_t *elem, int dir, long *min, long *max); - int (*set_range)(snd_mixer_elem_t *elem, int dir, long min, long max); - int (*get_dB_range)(snd_mixer_elem_t *elem, int dir, long *min, long *max); - int (*ask_vol_dB)(snd_mixer_elem_t *elem, int dir, long value, long *dbValue); - int (*ask_dB_vol)(snd_mixer_elem_t *elem, int dir, long dbValue, long *value, int xdir); - int (*get_volume)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long *value); - int (*get_dB)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long *value); - int (*set_volume)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value); - int (*set_dB)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value, int xdir); - int (*get_switch)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int *value); - int (*set_switch)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value); - int (*enum_item_name)(snd_mixer_elem_t *elem, unsigned int item, size_t maxlen, char *buf); - int (*get_enum_item)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int *itemp); - int (*set_enum_item)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int item); -}; - -int snd_mixer_selem_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2); - -int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info); -void *snd_mixer_sbasic_get_private(const snd_mixer_class_t *class); -void snd_mixer_sbasic_set_private(const snd_mixer_class_t *class, void *private_data); -void snd_mixer_sbasic_set_private_free(const snd_mixer_class_t *class, void (*private_free)(snd_mixer_class_t *class)); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_MIXER_ABST_H */ - diff --git a/raylib/external/alsa/output.h b/raylib/external/alsa/output.h deleted file mode 100644 index 5279aa2..0000000 --- a/raylib/external/alsa/output.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * \file include/output.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_OUTPUT_H -#define __ALSA_OUTPUT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Output Output Interface - * - * The output functions present an interface similar to the stdio functions - * on top of different underlying output destinations. - * - * Many PCM debugging functions (\c snd_pcm_xxx_dump_xxx) use such an output - * handle to be able to write not only to the screen but also to other - * destinations, e.g. to files or to memory buffers. - * - * \{ - */ - -/** - * \brief Internal structure for an output object. - * - * The ALSA library uses a pointer to this structure as a handle to an - * output object. Applications don't access its contents directly. - */ -typedef struct _snd_output snd_output_t; - -/** Output type. */ -typedef enum _snd_output_type { - /** Output to a stdio stream. */ - SND_OUTPUT_STDIO, - /** Output to a memory buffer. */ - SND_OUTPUT_BUFFER -} snd_output_type_t; - -int snd_output_stdio_open(snd_output_t **outputp, const char *file, const char *mode); -int snd_output_stdio_attach(snd_output_t **outputp, FILE *fp, int _close); -int snd_output_buffer_open(snd_output_t **outputp); -size_t snd_output_buffer_string(snd_output_t *output, char **buf); -int snd_output_close(snd_output_t *output); -int snd_output_printf(snd_output_t *output, const char *format, ...) -#ifndef DOC_HIDDEN - __attribute__ ((format (printf, 2, 3))) -#endif - ; -int snd_output_vprintf(snd_output_t *output, const char *format, va_list args); -int snd_output_puts(snd_output_t *output, const char *str); -int snd_output_putc(snd_output_t *output, int c); -int snd_output_flush(snd_output_t *output); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_OUTPUT_H */ - diff --git a/raylib/external/alsa/pcm.h b/raylib/external/alsa/pcm.h deleted file mode 100644 index d44de54..0000000 --- a/raylib/external/alsa/pcm.h +++ /dev/null @@ -1,1329 +0,0 @@ -/** - * \file include/pcm.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver. - * See the \ref pcm page for more details. - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_PCM_H -#define __ALSA_PCM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/** - * \defgroup PCM PCM Interface - * See the \ref pcm page for more details. - * \{ - */ - -/** dlsym version for interface entry callback */ -#define SND_PCM_DLSYM_VERSION _dlsym_pcm_001 - -/** PCM generic info container */ -typedef struct _snd_pcm_info snd_pcm_info_t; - -/** PCM hardware configuration space container - * - * snd_pcm_hw_params_t is an opaque structure which contains a set of possible - * PCM hardware configurations. For example, a given instance might include a - * range of buffer sizes, a range of period sizes, and a set of several sample - * formats. Some subset of all possible combinations these sets may be valid, - * but not necessarily any combination will be valid. - * - * When a parameter is set or restricted using a snd_pcm_hw_params_set* - * function, all of the other ranges will be updated to exclude as many - * impossible configurations as possible. Attempting to set a parameter - * outside of its acceptable range will result in the function failing - * and an error code being returned. - */ -typedef struct _snd_pcm_hw_params snd_pcm_hw_params_t; - -/** PCM software configuration container */ -typedef struct _snd_pcm_sw_params snd_pcm_sw_params_t; -/** PCM status container */ - typedef struct _snd_pcm_status snd_pcm_status_t; -/** PCM access types mask */ -typedef struct _snd_pcm_access_mask snd_pcm_access_mask_t; -/** PCM formats mask */ -typedef struct _snd_pcm_format_mask snd_pcm_format_mask_t; -/** PCM subformats mask */ -typedef struct _snd_pcm_subformat_mask snd_pcm_subformat_mask_t; - -/** PCM class */ -typedef enum _snd_pcm_class { - /** standard device */ - - SND_PCM_CLASS_GENERIC = 0, - /** multichannel device */ - SND_PCM_CLASS_MULTI, - /** software modem device */ - SND_PCM_CLASS_MODEM, - /** digitizer device */ - SND_PCM_CLASS_DIGITIZER, - SND_PCM_CLASS_LAST = SND_PCM_CLASS_DIGITIZER -} snd_pcm_class_t; - -/** PCM subclass */ -typedef enum _snd_pcm_subclass { - /** subdevices are mixed together */ - SND_PCM_SUBCLASS_GENERIC_MIX = 0, - /** multichannel subdevices are mixed together */ - SND_PCM_SUBCLASS_MULTI_MIX, - SND_PCM_SUBCLASS_LAST = SND_PCM_SUBCLASS_MULTI_MIX -} snd_pcm_subclass_t; - -/** PCM stream (direction) */ -typedef enum _snd_pcm_stream { - /** Playback stream */ - SND_PCM_STREAM_PLAYBACK = 0, - /** Capture stream */ - SND_PCM_STREAM_CAPTURE, - SND_PCM_STREAM_LAST = SND_PCM_STREAM_CAPTURE -} snd_pcm_stream_t; - -/** PCM access type */ -typedef enum _snd_pcm_access { - /** mmap access with simple interleaved channels */ - SND_PCM_ACCESS_MMAP_INTERLEAVED = 0, - /** mmap access with simple non interleaved channels */ - SND_PCM_ACCESS_MMAP_NONINTERLEAVED, - /** mmap access with complex placement */ - SND_PCM_ACCESS_MMAP_COMPLEX, - /** snd_pcm_readi/snd_pcm_writei access */ - SND_PCM_ACCESS_RW_INTERLEAVED, - /** snd_pcm_readn/snd_pcm_writen access */ - SND_PCM_ACCESS_RW_NONINTERLEAVED, - SND_PCM_ACCESS_LAST = SND_PCM_ACCESS_RW_NONINTERLEAVED -} snd_pcm_access_t; - -/** PCM sample format */ -typedef enum _snd_pcm_format { - /** Unknown */ - SND_PCM_FORMAT_UNKNOWN = -1, - /** Signed 8 bit */ - SND_PCM_FORMAT_S8 = 0, - /** Unsigned 8 bit */ - SND_PCM_FORMAT_U8, - /** Signed 16 bit Little Endian */ - SND_PCM_FORMAT_S16_LE, - /** Signed 16 bit Big Endian */ - SND_PCM_FORMAT_S16_BE, - /** Unsigned 16 bit Little Endian */ - SND_PCM_FORMAT_U16_LE, - /** Unsigned 16 bit Big Endian */ - SND_PCM_FORMAT_U16_BE, - /** Signed 24 bit Little Endian using low three bytes in 32-bit word */ - SND_PCM_FORMAT_S24_LE, - /** Signed 24 bit Big Endian using low three bytes in 32-bit word */ - SND_PCM_FORMAT_S24_BE, - /** Unsigned 24 bit Little Endian using low three bytes in 32-bit word */ - SND_PCM_FORMAT_U24_LE, - /** Unsigned 24 bit Big Endian using low three bytes in 32-bit word */ - SND_PCM_FORMAT_U24_BE, - /** Signed 32 bit Little Endian */ - SND_PCM_FORMAT_S32_LE, - /** Signed 32 bit Big Endian */ - SND_PCM_FORMAT_S32_BE, - /** Unsigned 32 bit Little Endian */ - SND_PCM_FORMAT_U32_LE, - /** Unsigned 32 bit Big Endian */ - SND_PCM_FORMAT_U32_BE, - /** Float 32 bit Little Endian, Range -1.0 to 1.0 */ - SND_PCM_FORMAT_FLOAT_LE, - /** Float 32 bit Big Endian, Range -1.0 to 1.0 */ - SND_PCM_FORMAT_FLOAT_BE, - /** Float 64 bit Little Endian, Range -1.0 to 1.0 */ - SND_PCM_FORMAT_FLOAT64_LE, - /** Float 64 bit Big Endian, Range -1.0 to 1.0 */ - SND_PCM_FORMAT_FLOAT64_BE, - /** IEC-958 Little Endian */ - SND_PCM_FORMAT_IEC958_SUBFRAME_LE, - /** IEC-958 Big Endian */ - SND_PCM_FORMAT_IEC958_SUBFRAME_BE, - /** Mu-Law */ - SND_PCM_FORMAT_MU_LAW, - /** A-Law */ - SND_PCM_FORMAT_A_LAW, - /** Ima-ADPCM */ - SND_PCM_FORMAT_IMA_ADPCM, - /** MPEG */ - SND_PCM_FORMAT_MPEG, - /** GSM */ - SND_PCM_FORMAT_GSM, - /** Special */ - SND_PCM_FORMAT_SPECIAL = 31, - /** Signed 24bit Little Endian in 3bytes format */ - SND_PCM_FORMAT_S24_3LE = 32, - /** Signed 24bit Big Endian in 3bytes format */ - SND_PCM_FORMAT_S24_3BE, - /** Unsigned 24bit Little Endian in 3bytes format */ - SND_PCM_FORMAT_U24_3LE, - /** Unsigned 24bit Big Endian in 3bytes format */ - SND_PCM_FORMAT_U24_3BE, - /** Signed 20bit Little Endian in 3bytes format */ - SND_PCM_FORMAT_S20_3LE, - /** Signed 20bit Big Endian in 3bytes format */ - SND_PCM_FORMAT_S20_3BE, - /** Unsigned 20bit Little Endian in 3bytes format */ - SND_PCM_FORMAT_U20_3LE, - /** Unsigned 20bit Big Endian in 3bytes format */ - SND_PCM_FORMAT_U20_3BE, - /** Signed 18bit Little Endian in 3bytes format */ - SND_PCM_FORMAT_S18_3LE, - /** Signed 18bit Big Endian in 3bytes format */ - SND_PCM_FORMAT_S18_3BE, - /** Unsigned 18bit Little Endian in 3bytes format */ - SND_PCM_FORMAT_U18_3LE, - /** Unsigned 18bit Big Endian in 3bytes format */ - SND_PCM_FORMAT_U18_3BE, - /* G.723 (ADPCM) 24 kbit/s, 8 samples in 3 bytes */ - SND_PCM_FORMAT_G723_24, - /* G.723 (ADPCM) 24 kbit/s, 1 sample in 1 byte */ - SND_PCM_FORMAT_G723_24_1B, - /* G.723 (ADPCM) 40 kbit/s, 8 samples in 3 bytes */ - SND_PCM_FORMAT_G723_40, - /* G.723 (ADPCM) 40 kbit/s, 1 sample in 1 byte */ - SND_PCM_FORMAT_G723_40_1B, - /* Direct Stream Digital (DSD) in 1-byte samples (x8) */ - SND_PCM_FORMAT_DSD_U8, - /* Direct Stream Digital (DSD) in 2-byte samples (x16) */ - SND_PCM_FORMAT_DSD_U16_LE, - /* Direct Stream Digital (DSD) in 4-byte samples (x32) */ - SND_PCM_FORMAT_DSD_U32_LE, - /* Direct Stream Digital (DSD) in 2-byte samples (x16) */ - SND_PCM_FORMAT_DSD_U16_BE, - /* Direct Stream Digital (DSD) in 4-byte samples (x32) */ - SND_PCM_FORMAT_DSD_U32_BE, - SND_PCM_FORMAT_LAST = SND_PCM_FORMAT_DSD_U32_BE, - -#if __BYTE_ORDER == __LITTLE_ENDIAN - /** Signed 16 bit CPU endian */ - SND_PCM_FORMAT_S16 = SND_PCM_FORMAT_S16_LE, - /** Unsigned 16 bit CPU endian */ - SND_PCM_FORMAT_U16 = SND_PCM_FORMAT_U16_LE, - /** Signed 24 bit CPU endian */ - SND_PCM_FORMAT_S24 = SND_PCM_FORMAT_S24_LE, - /** Unsigned 24 bit CPU endian */ - SND_PCM_FORMAT_U24 = SND_PCM_FORMAT_U24_LE, - /** Signed 32 bit CPU endian */ - SND_PCM_FORMAT_S32 = SND_PCM_FORMAT_S32_LE, - /** Unsigned 32 bit CPU endian */ - SND_PCM_FORMAT_U32 = SND_PCM_FORMAT_U32_LE, - /** Float 32 bit CPU endian */ - SND_PCM_FORMAT_FLOAT = SND_PCM_FORMAT_FLOAT_LE, - /** Float 64 bit CPU endian */ - SND_PCM_FORMAT_FLOAT64 = SND_PCM_FORMAT_FLOAT64_LE, - /** IEC-958 CPU Endian */ - SND_PCM_FORMAT_IEC958_SUBFRAME = SND_PCM_FORMAT_IEC958_SUBFRAME_LE -#elif __BYTE_ORDER == __BIG_ENDIAN - /** Signed 16 bit CPU endian */ - SND_PCM_FORMAT_S16 = SND_PCM_FORMAT_S16_BE, - /** Unsigned 16 bit CPU endian */ - SND_PCM_FORMAT_U16 = SND_PCM_FORMAT_U16_BE, - /** Signed 24 bit CPU endian */ - SND_PCM_FORMAT_S24 = SND_PCM_FORMAT_S24_BE, - /** Unsigned 24 bit CPU endian */ - SND_PCM_FORMAT_U24 = SND_PCM_FORMAT_U24_BE, - /** Signed 32 bit CPU endian */ - SND_PCM_FORMAT_S32 = SND_PCM_FORMAT_S32_BE, - /** Unsigned 32 bit CPU endian */ - SND_PCM_FORMAT_U32 = SND_PCM_FORMAT_U32_BE, - /** Float 32 bit CPU endian */ - SND_PCM_FORMAT_FLOAT = SND_PCM_FORMAT_FLOAT_BE, - /** Float 64 bit CPU endian */ - SND_PCM_FORMAT_FLOAT64 = SND_PCM_FORMAT_FLOAT64_BE, - /** IEC-958 CPU Endian */ - SND_PCM_FORMAT_IEC958_SUBFRAME = SND_PCM_FORMAT_IEC958_SUBFRAME_BE -#else -#error "Unknown endian" -#endif -} snd_pcm_format_t; - -/** PCM sample subformat */ -typedef enum _snd_pcm_subformat { - /** Standard */ - SND_PCM_SUBFORMAT_STD = 0, - SND_PCM_SUBFORMAT_LAST = SND_PCM_SUBFORMAT_STD -} snd_pcm_subformat_t; - -/** PCM state */ -typedef enum _snd_pcm_state { - /** Open */ - SND_PCM_STATE_OPEN = 0, - /** Setup installed */ - SND_PCM_STATE_SETUP, - /** Ready to start */ - SND_PCM_STATE_PREPARED, - /** Running */ - SND_PCM_STATE_RUNNING, - /** Stopped: underrun (playback) or overrun (capture) detected */ - SND_PCM_STATE_XRUN, - /** Draining: running (playback) or stopped (capture) */ - SND_PCM_STATE_DRAINING, - /** Paused */ - SND_PCM_STATE_PAUSED, - /** Hardware is suspended */ - SND_PCM_STATE_SUSPENDED, - /** Hardware is disconnected */ - SND_PCM_STATE_DISCONNECTED, - SND_PCM_STATE_LAST = SND_PCM_STATE_DISCONNECTED -} snd_pcm_state_t; - -/** PCM start mode */ -typedef enum _snd_pcm_start { - /** Automatic start on data read/write */ - SND_PCM_START_DATA = 0, - /** Explicit start */ - SND_PCM_START_EXPLICIT, - SND_PCM_START_LAST = SND_PCM_START_EXPLICIT -} snd_pcm_start_t; - -/** PCM xrun mode */ -typedef enum _snd_pcm_xrun { - /** Xrun detection disabled */ - SND_PCM_XRUN_NONE = 0, - /** Stop on xrun detection */ - SND_PCM_XRUN_STOP, - SND_PCM_XRUN_LAST = SND_PCM_XRUN_STOP -} snd_pcm_xrun_t; - -/** PCM timestamp mode */ -typedef enum _snd_pcm_tstamp { - /** No timestamp */ - SND_PCM_TSTAMP_NONE = 0, - /** Update timestamp at every hardware position update */ - SND_PCM_TSTAMP_ENABLE, - /** Equivalent with #SND_PCM_TSTAMP_ENABLE, - * just for compatibility with older versions - */ - SND_PCM_TSTAMP_MMAP = SND_PCM_TSTAMP_ENABLE, - SND_PCM_TSTAMP_LAST = SND_PCM_TSTAMP_ENABLE -} snd_pcm_tstamp_t; - -typedef enum _snd_pcm_tstamp_type { - SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, /**< gettimeofday equivalent */ - SND_PCM_TSTAMP_TYPE_MONOTONIC, /**< posix_clock_monotonic equivalent */ - SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW, /**< monotonic_raw (no NTP) */ - SND_PCM_TSTAMP_TYPE_LAST = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW, -} snd_pcm_tstamp_type_t; - -typedef struct _snd_pcm_audio_tstamp_config { - /* 5 of max 16 bits used */ - unsigned int type_requested:4; - unsigned int report_delay:1; /* add total delay to A/D or D/A */ -} snd_pcm_audio_tstamp_config_t; - -typedef struct _snd_pcm_audio_tstamp_report { - /* 6 of max 16 bits used for bit-fields */ - - /* for backwards compatibility */ - unsigned int valid:1; - - /* actual type if hardware could not support requested timestamp */ - unsigned int actual_type:4; - - /* accuracy represented in ns units */ - unsigned int accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */ - unsigned int accuracy; /* up to 4.29s, will be packed in separate field */ -} snd_pcm_audio_tstamp_report_t; - -/** Unsigned frames quantity */ -typedef unsigned long snd_pcm_uframes_t; -/** Signed frames quantity */ -typedef long snd_pcm_sframes_t; - -/** Non blocking mode (flag for open mode) \hideinitializer */ -#define SND_PCM_NONBLOCK 0x00000001 -/** Async notification (flag for open mode) \hideinitializer */ -#define SND_PCM_ASYNC 0x00000002 -/** In an abort state (internal, not allowed for open) */ -#define SND_PCM_ABORT 0x00008000 -/** Disable automatic (but not forced!) rate resamplinig */ -#define SND_PCM_NO_AUTO_RESAMPLE 0x00010000 -/** Disable automatic (but not forced!) channel conversion */ -#define SND_PCM_NO_AUTO_CHANNELS 0x00020000 -/** Disable automatic (but not forced!) format conversion */ -#define SND_PCM_NO_AUTO_FORMAT 0x00040000 -/** Disable soft volume control */ -#define SND_PCM_NO_SOFTVOL 0x00080000 - -/** PCM handle */ -typedef struct _snd_pcm snd_pcm_t; - -/** PCM type */ -enum _snd_pcm_type { - /** Kernel level PCM */ - SND_PCM_TYPE_HW = 0, - /** Hooked PCM */ - SND_PCM_TYPE_HOOKS, - /** One or more linked PCM with exclusive access to selected - channels */ - SND_PCM_TYPE_MULTI, - /** File writing plugin */ - SND_PCM_TYPE_FILE, - /** Null endpoint PCM */ - SND_PCM_TYPE_NULL, - /** Shared memory client PCM */ - SND_PCM_TYPE_SHM, - /** INET client PCM (not yet implemented) */ - SND_PCM_TYPE_INET, - /** Copying plugin */ - SND_PCM_TYPE_COPY, - /** Linear format conversion PCM */ - SND_PCM_TYPE_LINEAR, - /** A-Law format conversion PCM */ - SND_PCM_TYPE_ALAW, - /** Mu-Law format conversion PCM */ - SND_PCM_TYPE_MULAW, - /** IMA-ADPCM format conversion PCM */ - SND_PCM_TYPE_ADPCM, - /** Rate conversion PCM */ - SND_PCM_TYPE_RATE, - /** Attenuated static route PCM */ - SND_PCM_TYPE_ROUTE, - /** Format adjusted PCM */ - SND_PCM_TYPE_PLUG, - /** Sharing PCM */ - SND_PCM_TYPE_SHARE, - /** Meter plugin */ - SND_PCM_TYPE_METER, - /** Mixing PCM */ - SND_PCM_TYPE_MIX, - /** Attenuated dynamic route PCM (not yet implemented) */ - SND_PCM_TYPE_DROUTE, - /** Loopback server plugin (not yet implemented) */ - SND_PCM_TYPE_LBSERVER, - /** Linear Integer <-> Linear Float format conversion PCM */ - SND_PCM_TYPE_LINEAR_FLOAT, - /** LADSPA integration plugin */ - SND_PCM_TYPE_LADSPA, - /** Direct Mixing plugin */ - SND_PCM_TYPE_DMIX, - /** Jack Audio Connection Kit plugin */ - SND_PCM_TYPE_JACK, - /** Direct Snooping plugin */ - SND_PCM_TYPE_DSNOOP, - /** Direct Sharing plugin */ - SND_PCM_TYPE_DSHARE, - /** IEC958 subframe plugin */ - SND_PCM_TYPE_IEC958, - /** Soft volume plugin */ - SND_PCM_TYPE_SOFTVOL, - /** External I/O plugin */ - SND_PCM_TYPE_IOPLUG, - /** External filter plugin */ - SND_PCM_TYPE_EXTPLUG, - /** Mmap-emulation plugin */ - SND_PCM_TYPE_MMAP_EMUL, - SND_PCM_TYPE_LAST = SND_PCM_TYPE_MMAP_EMUL -}; - -/** PCM type */ -typedef enum _snd_pcm_type snd_pcm_type_t; - -/** PCM area specification */ -typedef struct _snd_pcm_channel_area { - /** base address of channel samples */ - void *addr; - /** offset to first sample in bits */ - unsigned int first; - /** samples distance in bits */ - unsigned int step; -} snd_pcm_channel_area_t; - -/** PCM synchronization ID */ -typedef union _snd_pcm_sync_id { - /** 8-bit ID */ - unsigned char id[16]; - /** 16-bit ID */ - unsigned short id16[8]; - /** 32-bit ID */ - unsigned int id32[4]; -} snd_pcm_sync_id_t; - -/** #SND_PCM_TYPE_METER scope handle */ -typedef struct _snd_pcm_scope snd_pcm_scope_t; - -int snd_pcm_open(snd_pcm_t **pcm, const char *name, - snd_pcm_stream_t stream, int mode); -int snd_pcm_open_lconf(snd_pcm_t **pcm, const char *name, - snd_pcm_stream_t stream, int mode, - snd_config_t *lconf); -int snd_pcm_open_fallback(snd_pcm_t **pcm, snd_config_t *root, - const char *name, const char *orig_name, - snd_pcm_stream_t stream, int mode); - -int snd_pcm_close(snd_pcm_t *pcm); -const char *snd_pcm_name(snd_pcm_t *pcm); -snd_pcm_type_t snd_pcm_type(snd_pcm_t *pcm); -snd_pcm_stream_t snd_pcm_stream(snd_pcm_t *pcm); -int snd_pcm_poll_descriptors_count(snd_pcm_t *pcm); -int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); -int snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock); -static __inline__ int snd_pcm_abort(snd_pcm_t *pcm) { return snd_pcm_nonblock(pcm, 2); } -int snd_async_add_pcm_handler(snd_async_handler_t **handler, snd_pcm_t *pcm, - snd_async_callback_t callback, void *private_data); -snd_pcm_t *snd_async_handler_get_pcm(snd_async_handler_t *handler); -int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info); -int snd_pcm_hw_params_current(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -int snd_pcm_hw_free(snd_pcm_t *pcm); -int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); -int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); -int snd_pcm_prepare(snd_pcm_t *pcm); -int snd_pcm_reset(snd_pcm_t *pcm); -int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status); -int snd_pcm_start(snd_pcm_t *pcm); -int snd_pcm_drop(snd_pcm_t *pcm); -int snd_pcm_drain(snd_pcm_t *pcm); -int snd_pcm_pause(snd_pcm_t *pcm, int enable); -snd_pcm_state_t snd_pcm_state(snd_pcm_t *pcm); -int snd_pcm_hwsync(snd_pcm_t *pcm); -int snd_pcm_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp); -int snd_pcm_resume(snd_pcm_t *pcm); -int snd_pcm_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail, snd_htimestamp_t *tstamp); -snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm); -snd_pcm_sframes_t snd_pcm_avail_update(snd_pcm_t *pcm); -int snd_pcm_avail_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *availp, snd_pcm_sframes_t *delayp); -snd_pcm_sframes_t snd_pcm_rewindable(snd_pcm_t *pcm); -snd_pcm_sframes_t snd_pcm_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_forwardable(snd_pcm_t *pcm); -snd_pcm_sframes_t snd_pcm_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); -snd_pcm_sframes_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size); -snd_pcm_sframes_t snd_pcm_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size); -snd_pcm_sframes_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size); -int snd_pcm_wait(snd_pcm_t *pcm, int timeout); - -int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2); -int snd_pcm_unlink(snd_pcm_t *pcm); - -/** channel mapping API version number */ -#define SND_CHMAP_API_VERSION ((1 << 16) | (0 << 8) | 1) - -/** channel map list type */ -enum snd_pcm_chmap_type { - SND_CHMAP_TYPE_NONE = 0,/**< unspecified channel position */ - SND_CHMAP_TYPE_FIXED, /**< fixed channel position */ - SND_CHMAP_TYPE_VAR, /**< freely swappable channel position */ - SND_CHMAP_TYPE_PAIRED, /**< pair-wise swappable channel position */ - SND_CHMAP_TYPE_LAST = SND_CHMAP_TYPE_PAIRED, /**< last entry */ -}; - -/** channel positions */ -enum snd_pcm_chmap_position { - SND_CHMAP_UNKNOWN = 0, /**< unspecified */ - SND_CHMAP_NA, /**< N/A, silent */ - SND_CHMAP_MONO, /**< mono stream */ - SND_CHMAP_FL, /**< front left */ - SND_CHMAP_FR, /**< front right */ - SND_CHMAP_RL, /**< rear left */ - SND_CHMAP_RR, /**< rear right */ - SND_CHMAP_FC, /**< front center */ - SND_CHMAP_LFE, /**< LFE */ - SND_CHMAP_SL, /**< side left */ - SND_CHMAP_SR, /**< side right */ - SND_CHMAP_RC, /**< rear center */ - SND_CHMAP_FLC, /**< front left center */ - SND_CHMAP_FRC, /**< front right center */ - SND_CHMAP_RLC, /**< rear left center */ - SND_CHMAP_RRC, /**< rear right center */ - SND_CHMAP_FLW, /**< front left wide */ - SND_CHMAP_FRW, /**< front right wide */ - SND_CHMAP_FLH, /**< front left high */ - SND_CHMAP_FCH, /**< front center high */ - SND_CHMAP_FRH, /**< front right high */ - SND_CHMAP_TC, /**< top center */ - SND_CHMAP_TFL, /**< top front left */ - SND_CHMAP_TFR, /**< top front right */ - SND_CHMAP_TFC, /**< top front center */ - SND_CHMAP_TRL, /**< top rear left */ - SND_CHMAP_TRR, /**< top rear right */ - SND_CHMAP_TRC, /**< top rear center */ - SND_CHMAP_TFLC, /**< top front left center */ - SND_CHMAP_TFRC, /**< top front right center */ - SND_CHMAP_TSL, /**< top side left */ - SND_CHMAP_TSR, /**< top side right */ - SND_CHMAP_LLFE, /**< left LFE */ - SND_CHMAP_RLFE, /**< right LFE */ - SND_CHMAP_BC, /**< bottom center */ - SND_CHMAP_BLC, /**< bottom left center */ - SND_CHMAP_BRC, /**< bottom right center */ - SND_CHMAP_LAST = SND_CHMAP_BRC, -}; - -/** bitmask for channel position */ -#define SND_CHMAP_POSITION_MASK 0xffff - -/** bit flag indicating the channel is phase inverted */ -#define SND_CHMAP_PHASE_INVERSE (0x01 << 16) -/** bit flag indicating the non-standard channel value */ -#define SND_CHMAP_DRIVER_SPEC (0x02 << 16) - -/** the channel map header */ -typedef struct snd_pcm_chmap { - unsigned int channels; /**< number of channels */ - unsigned int pos[0]; /**< channel position array */ -} snd_pcm_chmap_t; - -/** the header of array items returned from snd_pcm_query_chmaps() */ -typedef struct snd_pcm_chmap_query { - enum snd_pcm_chmap_type type; /**< channel map type */ - snd_pcm_chmap_t map; /**< available channel map */ -} snd_pcm_chmap_query_t; - - -snd_pcm_chmap_query_t **snd_pcm_query_chmaps(snd_pcm_t *pcm); -snd_pcm_chmap_query_t **snd_pcm_query_chmaps_from_hw(int card, int dev, - int subdev, - snd_pcm_stream_t stream); -void snd_pcm_free_chmaps(snd_pcm_chmap_query_t **maps); -snd_pcm_chmap_t *snd_pcm_get_chmap(snd_pcm_t *pcm); -int snd_pcm_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map); - -const char *snd_pcm_chmap_type_name(enum snd_pcm_chmap_type val); -const char *snd_pcm_chmap_name(enum snd_pcm_chmap_position val); -const char *snd_pcm_chmap_long_name(enum snd_pcm_chmap_position val); -int snd_pcm_chmap_print(const snd_pcm_chmap_t *map, size_t maxlen, char *buf); -unsigned int snd_pcm_chmap_from_string(const char *str); -snd_pcm_chmap_t *snd_pcm_chmap_parse_string(const char *str); - -//int snd_pcm_mixer_element(snd_pcm_t *pcm, snd_mixer_t *mixer, snd_mixer_elem_t **elem); - -/* - * application helpers - these functions are implemented on top - * of the basic API - */ - -int snd_pcm_recover(snd_pcm_t *pcm, int err, int silent); -int snd_pcm_set_params(snd_pcm_t *pcm, - snd_pcm_format_t format, - snd_pcm_access_t access, - unsigned int channels, - unsigned int rate, - int soft_resample, - unsigned int latency); -int snd_pcm_get_params(snd_pcm_t *pcm, - snd_pcm_uframes_t *buffer_size, - snd_pcm_uframes_t *period_size); - -/** \} */ - -/** - * \defgroup PCM_Info Stream Information - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -size_t snd_pcm_info_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_pcm_info_t using standard alloca - * \param ptr returned pointer - */ -#define snd_pcm_info_alloca(ptr) __snd_alloca(ptr, snd_pcm_info) -int snd_pcm_info_malloc(snd_pcm_info_t **ptr); -void snd_pcm_info_free(snd_pcm_info_t *obj); -void snd_pcm_info_copy(snd_pcm_info_t *dst, const snd_pcm_info_t *src); -unsigned int snd_pcm_info_get_device(const snd_pcm_info_t *obj); -unsigned int snd_pcm_info_get_subdevice(const snd_pcm_info_t *obj); -snd_pcm_stream_t snd_pcm_info_get_stream(const snd_pcm_info_t *obj); -int snd_pcm_info_get_card(const snd_pcm_info_t *obj); -const char *snd_pcm_info_get_id(const snd_pcm_info_t *obj); -const char *snd_pcm_info_get_name(const snd_pcm_info_t *obj); -const char *snd_pcm_info_get_subdevice_name(const snd_pcm_info_t *obj); -snd_pcm_class_t snd_pcm_info_get_class(const snd_pcm_info_t *obj); -snd_pcm_subclass_t snd_pcm_info_get_subclass(const snd_pcm_info_t *obj); -unsigned int snd_pcm_info_get_subdevices_count(const snd_pcm_info_t *obj); -unsigned int snd_pcm_info_get_subdevices_avail(const snd_pcm_info_t *obj); -snd_pcm_sync_id_t snd_pcm_info_get_sync(const snd_pcm_info_t *obj); -void snd_pcm_info_set_device(snd_pcm_info_t *obj, unsigned int val); -void snd_pcm_info_set_subdevice(snd_pcm_info_t *obj, unsigned int val); -void snd_pcm_info_set_stream(snd_pcm_info_t *obj, snd_pcm_stream_t val); - -/** \} */ - -/** - * \defgroup PCM_HW_Params Hardware Parameters - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); - -int snd_pcm_hw_params_can_mmap_sample_resolution(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_is_double(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_is_batch(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_is_block_transfer(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_is_monotonic(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_can_overrange(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_can_pause(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_can_resume(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_is_half_duplex(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_is_joint_duplex(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_can_sync_start(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params); /* deprecated, use audio_ts_type */ -int snd_pcm_hw_params_supports_audio_ts_type(const snd_pcm_hw_params_t *params, int type); -int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, - unsigned int *rate_num, - unsigned int *rate_den); -int snd_pcm_hw_params_get_sbits(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_get_fifo_size(const snd_pcm_hw_params_t *params); - -#if 0 -typedef struct _snd_pcm_hw_strategy snd_pcm_hw_strategy_t; - -/* choices need to be sorted on ascending badness */ -typedef struct _snd_pcm_hw_strategy_simple_choices_list { - unsigned int value; - unsigned int badness; -} snd_pcm_hw_strategy_simple_choices_list_t; - -int snd_pcm_hw_params_strategy(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, - const snd_pcm_hw_strategy_t *strategy, - unsigned int badness_min, - unsigned int badness_max); - -void snd_pcm_hw_strategy_free(snd_pcm_hw_strategy_t *strategy); -int snd_pcm_hw_strategy_simple(snd_pcm_hw_strategy_t **strategyp, - unsigned int badness_min, - unsigned int badness_max); -int snd_pcm_hw_params_try_explain_failure(snd_pcm_t *pcm, - snd_pcm_hw_params_t *fail, - snd_pcm_hw_params_t *success, - unsigned int depth, - snd_output_t *out); - -#endif - -size_t snd_pcm_hw_params_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_pcm_hw_params_t using standard alloca - * \param ptr returned pointer - */ -#define snd_pcm_hw_params_alloca(ptr) __snd_alloca(ptr, snd_pcm_hw_params) -int snd_pcm_hw_params_malloc(snd_pcm_hw_params_t **ptr); -void snd_pcm_hw_params_free(snd_pcm_hw_params_t *obj); -void snd_pcm_hw_params_copy(snd_pcm_hw_params_t *dst, const snd_pcm_hw_params_t *src); - -#if !defined(ALSA_LIBRARY_BUILD) && !defined(ALSA_PCM_OLD_HW_PARAMS_API) - -int snd_pcm_hw_params_get_access(const snd_pcm_hw_params_t *params, snd_pcm_access_t *_access); -int snd_pcm_hw_params_test_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access); -int snd_pcm_hw_params_set_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access); -int snd_pcm_hw_params_set_access_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t *_access); -int snd_pcm_hw_params_set_access_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t *_access); -int snd_pcm_hw_params_set_access_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_mask_t *mask); -int snd_pcm_hw_params_get_access_mask(snd_pcm_hw_params_t *params, snd_pcm_access_mask_t *mask); - -int snd_pcm_hw_params_get_format(const snd_pcm_hw_params_t *params, snd_pcm_format_t *val); -int snd_pcm_hw_params_test_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); -int snd_pcm_hw_params_set_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); -int snd_pcm_hw_params_set_format_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t *format); -int snd_pcm_hw_params_set_format_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t *format); -int snd_pcm_hw_params_set_format_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); -void snd_pcm_hw_params_get_format_mask(snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); - -int snd_pcm_hw_params_get_subformat(const snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat); -int snd_pcm_hw_params_test_subformat(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t subformat); -int snd_pcm_hw_params_set_subformat(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t subformat); -int snd_pcm_hw_params_set_subformat_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat); -int snd_pcm_hw_params_set_subformat_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat); -int snd_pcm_hw_params_set_subformat_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_mask_t *mask); -void snd_pcm_hw_params_get_subformat_mask(snd_pcm_hw_params_t *params, snd_pcm_subformat_mask_t *mask); - -int snd_pcm_hw_params_get_channels(const snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_get_channels_min(const snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_get_channels_max(const snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_test_channels(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -int snd_pcm_hw_params_set_channels(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -int snd_pcm_hw_params_set_channels_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_set_channels_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_set_channels_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, unsigned int *max); -int snd_pcm_hw_params_set_channels_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_set_channels_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_set_channels_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); - -int snd_pcm_hw_params_get_rate(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_get_rate_min(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_get_rate_max(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_test_rate(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_rate(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_rate_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_rate_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_rate_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); -int snd_pcm_hw_params_set_rate_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_rate_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_rate_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_rate_resample(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -int snd_pcm_hw_params_get_rate_resample(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_set_export_buffer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -int snd_pcm_hw_params_get_export_buffer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_set_period_wakeup(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -int snd_pcm_hw_params_get_period_wakeup(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); - -int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_get_period_time_min(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_get_period_time_max(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_test_period_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_period_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_period_time_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_period_time_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_period_time_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); -int snd_pcm_hw_params_set_period_time_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_period_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_period_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); - -int snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir); -int snd_pcm_hw_params_get_period_size_min(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir); -int snd_pcm_hw_params_get_period_size_max(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir); -int snd_pcm_hw_params_test_period_size(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int dir); -int snd_pcm_hw_params_set_period_size(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int dir); -int snd_pcm_hw_params_set_period_size_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); -int snd_pcm_hw_params_set_period_size_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); -int snd_pcm_hw_params_set_period_size_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *min, int *mindir, snd_pcm_uframes_t *max, int *maxdir); -int snd_pcm_hw_params_set_period_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); -int snd_pcm_hw_params_set_period_size_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); -int snd_pcm_hw_params_set_period_size_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); -int snd_pcm_hw_params_set_period_size_integer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); - -int snd_pcm_hw_params_get_periods(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_get_periods_min(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_get_periods_max(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_test_periods(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_periods(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_periods_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_periods_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_periods_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); -int snd_pcm_hw_params_set_periods_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_periods_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_periods_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_periods_integer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); - -int snd_pcm_hw_params_get_buffer_time(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_get_buffer_time_min(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_get_buffer_time_max(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_test_buffer_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_buffer_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_buffer_time_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_buffer_time_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_buffer_time_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); -int snd_pcm_hw_params_set_buffer_time_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_buffer_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_buffer_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); - -int snd_pcm_hw_params_get_buffer_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_hw_params_get_buffer_size_min(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_hw_params_get_buffer_size_max(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_hw_params_test_buffer_size(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val); -int snd_pcm_hw_params_set_buffer_size(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val); -int snd_pcm_hw_params_set_buffer_size_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_hw_params_set_buffer_size_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_hw_params_set_buffer_size_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *min, snd_pcm_uframes_t *max); -int snd_pcm_hw_params_set_buffer_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_hw_params_set_buffer_size_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_hw_params_set_buffer_size_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); - -#endif /* !ALSA_LIBRARY_BUILD && !ALSA_PCM_OLD_HW_PARAMS_API */ - -int snd_pcm_hw_params_get_min_align(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); - -/** \} */ - -/** - * \defgroup PCM_SW_Params Software Parameters - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -size_t snd_pcm_sw_params_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_pcm_sw_params_t using standard alloca - * \param ptr returned pointer - */ -#define snd_pcm_sw_params_alloca(ptr) __snd_alloca(ptr, snd_pcm_sw_params) -int snd_pcm_sw_params_malloc(snd_pcm_sw_params_t **ptr); -void snd_pcm_sw_params_free(snd_pcm_sw_params_t *obj); -void snd_pcm_sw_params_copy(snd_pcm_sw_params_t *dst, const snd_pcm_sw_params_t *src); -int snd_pcm_sw_params_get_boundary(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val); - -#if !defined(ALSA_LIBRARY_BUILD) && !defined(ALSA_PCM_OLD_SW_PARAMS_API) - -int snd_pcm_sw_params_set_tstamp_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_tstamp_t val); -int snd_pcm_sw_params_get_tstamp_mode(const snd_pcm_sw_params_t *params, snd_pcm_tstamp_t *val); -int snd_pcm_sw_params_set_tstamp_type(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_tstamp_type_t val); -int snd_pcm_sw_params_get_tstamp_type(const snd_pcm_sw_params_t *params, snd_pcm_tstamp_type_t *val); -int snd_pcm_sw_params_set_avail_min(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -int snd_pcm_sw_params_get_avail_min(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_sw_params_set_period_event(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, int val); -int snd_pcm_sw_params_get_period_event(const snd_pcm_sw_params_t *params, int *val); -int snd_pcm_sw_params_set_start_threshold(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -int snd_pcm_sw_params_get_start_threshold(const snd_pcm_sw_params_t *paramsm, snd_pcm_uframes_t *val); -int snd_pcm_sw_params_set_stop_threshold(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -int snd_pcm_sw_params_get_stop_threshold(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_sw_params_set_silence_threshold(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -int snd_pcm_sw_params_get_silence_threshold(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_sw_params_set_silence_size(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -int snd_pcm_sw_params_get_silence_size(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val); - -#endif /* !ALSA_LIBRARY_BUILD && !ALSA_PCM_OLD_SW_PARAMS_API */ - -/** \} */ - -/* include old API */ -#ifndef ALSA_LIBRARY_BUILD -#if defined(ALSA_PCM_OLD_HW_PARAMS_API) || defined(ALSA_PCM_OLD_SW_PARAMS_API) -#include "pcm_old.h" -#endif -#endif - -/** - * \defgroup PCM_Access Access Mask Functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -size_t snd_pcm_access_mask_sizeof(void); -/** \hideinitializer - * \brief allocate an empty #snd_pcm_access_mask_t using standard alloca - * \param ptr returned pointer - */ -#define snd_pcm_access_mask_alloca(ptr) __snd_alloca(ptr, snd_pcm_access_mask) -int snd_pcm_access_mask_malloc(snd_pcm_access_mask_t **ptr); -void snd_pcm_access_mask_free(snd_pcm_access_mask_t *obj); -void snd_pcm_access_mask_copy(snd_pcm_access_mask_t *dst, const snd_pcm_access_mask_t *src); -void snd_pcm_access_mask_none(snd_pcm_access_mask_t *mask); -void snd_pcm_access_mask_any(snd_pcm_access_mask_t *mask); -int snd_pcm_access_mask_test(const snd_pcm_access_mask_t *mask, snd_pcm_access_t val); -int snd_pcm_access_mask_empty(const snd_pcm_access_mask_t *mask); -void snd_pcm_access_mask_set(snd_pcm_access_mask_t *mask, snd_pcm_access_t val); -void snd_pcm_access_mask_reset(snd_pcm_access_mask_t *mask, snd_pcm_access_t val); - -/** \} */ - -/** - * \defgroup PCM_Format Format Mask Functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -size_t snd_pcm_format_mask_sizeof(void); -/** \hideinitializer - * \brief allocate an empty #snd_pcm_format_mask_t using standard alloca - * \param ptr returned pointer - */ -#define snd_pcm_format_mask_alloca(ptr) __snd_alloca(ptr, snd_pcm_format_mask) -int snd_pcm_format_mask_malloc(snd_pcm_format_mask_t **ptr); -void snd_pcm_format_mask_free(snd_pcm_format_mask_t *obj); -void snd_pcm_format_mask_copy(snd_pcm_format_mask_t *dst, const snd_pcm_format_mask_t *src); -void snd_pcm_format_mask_none(snd_pcm_format_mask_t *mask); -void snd_pcm_format_mask_any(snd_pcm_format_mask_t *mask); -int snd_pcm_format_mask_test(const snd_pcm_format_mask_t *mask, snd_pcm_format_t val); -int snd_pcm_format_mask_empty(const snd_pcm_format_mask_t *mask); -void snd_pcm_format_mask_set(snd_pcm_format_mask_t *mask, snd_pcm_format_t val); -void snd_pcm_format_mask_reset(snd_pcm_format_mask_t *mask, snd_pcm_format_t val); - -/** \} */ - -/** - * \defgroup PCM_SubFormat Subformat Mask Functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -size_t snd_pcm_subformat_mask_sizeof(void); -/** \hideinitializer - * \brief allocate an empty #snd_pcm_subformat_mask_t using standard alloca - * \param ptr returned pointer - */ -#define snd_pcm_subformat_mask_alloca(ptr) __snd_alloca(ptr, snd_pcm_subformat_mask) -int snd_pcm_subformat_mask_malloc(snd_pcm_subformat_mask_t **ptr); -void snd_pcm_subformat_mask_free(snd_pcm_subformat_mask_t *obj); -void snd_pcm_subformat_mask_copy(snd_pcm_subformat_mask_t *dst, const snd_pcm_subformat_mask_t *src); -void snd_pcm_subformat_mask_none(snd_pcm_subformat_mask_t *mask); -void snd_pcm_subformat_mask_any(snd_pcm_subformat_mask_t *mask); -int snd_pcm_subformat_mask_test(const snd_pcm_subformat_mask_t *mask, snd_pcm_subformat_t val); -int snd_pcm_subformat_mask_empty(const snd_pcm_subformat_mask_t *mask); -void snd_pcm_subformat_mask_set(snd_pcm_subformat_mask_t *mask, snd_pcm_subformat_t val); -void snd_pcm_subformat_mask_reset(snd_pcm_subformat_mask_t *mask, snd_pcm_subformat_t val); - -/** \} */ - -/** - * \defgroup PCM_Status Status Functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -size_t snd_pcm_status_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_pcm_status_t using standard alloca - * \param ptr returned pointer - */ -#define snd_pcm_status_alloca(ptr) __snd_alloca(ptr, snd_pcm_status) -int snd_pcm_status_malloc(snd_pcm_status_t **ptr); -void snd_pcm_status_free(snd_pcm_status_t *obj); -void snd_pcm_status_copy(snd_pcm_status_t *dst, const snd_pcm_status_t *src); -snd_pcm_state_t snd_pcm_status_get_state(const snd_pcm_status_t *obj); -void snd_pcm_status_get_trigger_tstamp(const snd_pcm_status_t *obj, snd_timestamp_t *ptr); -void snd_pcm_status_get_trigger_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); -void snd_pcm_status_get_tstamp(const snd_pcm_status_t *obj, snd_timestamp_t *ptr); -void snd_pcm_status_get_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); -void snd_pcm_status_get_audio_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); -void snd_pcm_status_get_driver_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); -void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj, - snd_pcm_audio_tstamp_report_t *audio_tstamp_report); -void snd_pcm_status_set_audio_htstamp_config(snd_pcm_status_t *obj, - snd_pcm_audio_tstamp_config_t *audio_tstamp_config); - -static inline void snd_pcm_pack_audio_tstamp_config(unsigned int *data, - snd_pcm_audio_tstamp_config_t *config) -{ - *data = config->report_delay; - *data <<= 4; - *data |= config->type_requested; -} - -static inline void snd_pcm_unpack_audio_tstamp_report(unsigned int data, unsigned int accuracy, - snd_pcm_audio_tstamp_report_t *report) -{ - data >>= 16; - report->valid = data & 1; - report->actual_type = (data >> 1) & 0xF; - report->accuracy_report = (data >> 5) & 1; - report->accuracy = accuracy; -} - -snd_pcm_sframes_t snd_pcm_status_get_delay(const snd_pcm_status_t *obj); -snd_pcm_uframes_t snd_pcm_status_get_avail(const snd_pcm_status_t *obj); -snd_pcm_uframes_t snd_pcm_status_get_avail_max(const snd_pcm_status_t *obj); -snd_pcm_uframes_t snd_pcm_status_get_overrange(const snd_pcm_status_t *obj); - -/** \} */ - -/** - * \defgroup PCM_Description Description Functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -const char *snd_pcm_type_name(snd_pcm_type_t type); -const char *snd_pcm_stream_name(const snd_pcm_stream_t stream); -const char *snd_pcm_access_name(const snd_pcm_access_t _access); -const char *snd_pcm_format_name(const snd_pcm_format_t format); -const char *snd_pcm_format_description(const snd_pcm_format_t format); -const char *snd_pcm_subformat_name(const snd_pcm_subformat_t subformat); -const char *snd_pcm_subformat_description(const snd_pcm_subformat_t subformat); -snd_pcm_format_t snd_pcm_format_value(const char* name); -const char *snd_pcm_tstamp_mode_name(const snd_pcm_tstamp_t mode); -const char *snd_pcm_state_name(const snd_pcm_state_t state); - -/** \} */ - -/** - * \defgroup PCM_Dump Debug Functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -int snd_pcm_dump(snd_pcm_t *pcm, snd_output_t *out); -int snd_pcm_dump_hw_setup(snd_pcm_t *pcm, snd_output_t *out); -int snd_pcm_dump_sw_setup(snd_pcm_t *pcm, snd_output_t *out); -int snd_pcm_dump_setup(snd_pcm_t *pcm, snd_output_t *out); -int snd_pcm_hw_params_dump(snd_pcm_hw_params_t *params, snd_output_t *out); -int snd_pcm_sw_params_dump(snd_pcm_sw_params_t *params, snd_output_t *out); -int snd_pcm_status_dump(snd_pcm_status_t *status, snd_output_t *out); - -/** \} */ - -/** - * \defgroup PCM_Direct Direct Access (MMAP) Functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -int snd_pcm_mmap_begin(snd_pcm_t *pcm, - const snd_pcm_channel_area_t **areas, - snd_pcm_uframes_t *offset, - snd_pcm_uframes_t *frames); -snd_pcm_sframes_t snd_pcm_mmap_commit(snd_pcm_t *pcm, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); -snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size); -snd_pcm_sframes_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size); -snd_pcm_sframes_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size); - -/** \} */ - -/** - * \defgroup PCM_Helpers Helper Functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -int snd_pcm_format_signed(snd_pcm_format_t format); -int snd_pcm_format_unsigned(snd_pcm_format_t format); -int snd_pcm_format_linear(snd_pcm_format_t format); -int snd_pcm_format_float(snd_pcm_format_t format); -int snd_pcm_format_little_endian(snd_pcm_format_t format); -int snd_pcm_format_big_endian(snd_pcm_format_t format); -int snd_pcm_format_cpu_endian(snd_pcm_format_t format); -int snd_pcm_format_width(snd_pcm_format_t format); /* in bits */ -int snd_pcm_format_physical_width(snd_pcm_format_t format); /* in bits */ -snd_pcm_format_t snd_pcm_build_linear_format(int width, int pwidth, int unsignd, int big_endian); -ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples); -uint8_t snd_pcm_format_silence(snd_pcm_format_t format); -uint16_t snd_pcm_format_silence_16(snd_pcm_format_t format); -uint32_t snd_pcm_format_silence_32(snd_pcm_format_t format); -uint64_t snd_pcm_format_silence_64(snd_pcm_format_t format); -int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int samples); - -snd_pcm_sframes_t snd_pcm_bytes_to_frames(snd_pcm_t *pcm, ssize_t bytes); -ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *pcm, snd_pcm_sframes_t frames); -long snd_pcm_bytes_to_samples(snd_pcm_t *pcm, ssize_t bytes); -ssize_t snd_pcm_samples_to_bytes(snd_pcm_t *pcm, long samples); - -int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, snd_pcm_uframes_t dst_offset, - unsigned int samples, snd_pcm_format_t format); -int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset, - unsigned int channels, snd_pcm_uframes_t frames, snd_pcm_format_t format); -int snd_pcm_area_copy(const snd_pcm_channel_area_t *dst_channel, snd_pcm_uframes_t dst_offset, - const snd_pcm_channel_area_t *src_channel, snd_pcm_uframes_t src_offset, - unsigned int samples, snd_pcm_format_t format); -int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset, - const snd_pcm_channel_area_t *src_channels, snd_pcm_uframes_t src_offset, - unsigned int channels, snd_pcm_uframes_t frames, snd_pcm_format_t format); - -/** \} */ - -/** - * \defgroup PCM_Hook Hook Extension - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -/** type of pcm hook */ -typedef enum _snd_pcm_hook_type { - SND_PCM_HOOK_TYPE_HW_PARAMS = 0, - SND_PCM_HOOK_TYPE_HW_FREE, - SND_PCM_HOOK_TYPE_CLOSE, - SND_PCM_HOOK_TYPE_LAST = SND_PCM_HOOK_TYPE_CLOSE -} snd_pcm_hook_type_t; - -/** PCM hook container */ -typedef struct _snd_pcm_hook snd_pcm_hook_t; -/** PCM hook callback function */ -typedef int (*snd_pcm_hook_func_t)(snd_pcm_hook_t *hook); -snd_pcm_t *snd_pcm_hook_get_pcm(snd_pcm_hook_t *hook); -void *snd_pcm_hook_get_private(snd_pcm_hook_t *hook); -void snd_pcm_hook_set_private(snd_pcm_hook_t *hook, void *private_data); -int snd_pcm_hook_add(snd_pcm_hook_t **hookp, snd_pcm_t *pcm, - snd_pcm_hook_type_t type, - snd_pcm_hook_func_t func, void *private_data); -int snd_pcm_hook_remove(snd_pcm_hook_t *hook); - -/** \} */ - -/** - * \defgroup PCM_Scope Scope Plugin Extension - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -/** #SND_PCM_TYPE_METER scope functions */ -typedef struct _snd_pcm_scope_ops { - /** \brief Enable and prepare it using current params - * \param scope scope handle - */ - int (*enable)(snd_pcm_scope_t *scope); - /** \brief Disable - * \param scope scope handle - */ - void (*disable)(snd_pcm_scope_t *scope); - /** \brief PCM has been started - * \param scope scope handle - */ - void (*start)(snd_pcm_scope_t *scope); - /** \brief PCM has been stopped - * \param scope scope handle - */ - void (*stop)(snd_pcm_scope_t *scope); - /** \brief New frames are present - * \param scope scope handle - */ - void (*update)(snd_pcm_scope_t *scope); - /** \brief Reset status - * \param scope scope handle - */ - void (*reset)(snd_pcm_scope_t *scope); - /** \brief PCM is closing - * \param scope scope handle - */ - void (*close)(snd_pcm_scope_t *scope); -} snd_pcm_scope_ops_t; - -snd_pcm_uframes_t snd_pcm_meter_get_bufsize(snd_pcm_t *pcm); -unsigned int snd_pcm_meter_get_channels(snd_pcm_t *pcm); -unsigned int snd_pcm_meter_get_rate(snd_pcm_t *pcm); -snd_pcm_uframes_t snd_pcm_meter_get_now(snd_pcm_t *pcm); -snd_pcm_uframes_t snd_pcm_meter_get_boundary(snd_pcm_t *pcm); -int snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_scope_t *scope); -snd_pcm_scope_t *snd_pcm_meter_search_scope(snd_pcm_t *pcm, const char *name); -int snd_pcm_scope_malloc(snd_pcm_scope_t **ptr); -void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, - const snd_pcm_scope_ops_t *val); -void snd_pcm_scope_set_name(snd_pcm_scope_t *scope, const char *val); -const char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope); -void *snd_pcm_scope_get_callback_private(snd_pcm_scope_t *scope); -void snd_pcm_scope_set_callback_private(snd_pcm_scope_t *scope, void *val); -int snd_pcm_scope_s16_open(snd_pcm_t *pcm, const char *name, - snd_pcm_scope_t **scopep); -int16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope, - unsigned int channel); - -/** \} */ - -/** - * \defgroup PCM_Simple Simple setup functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -/** Simple PCM latency type */ -typedef enum _snd_spcm_latency { - /** standard latency - for standard playback or capture - (estimated latency in one direction 350ms) */ - SND_SPCM_LATENCY_STANDARD = 0, - /** medium latency - software phones etc. - (estimated latency in one direction maximally 25ms */ - SND_SPCM_LATENCY_MEDIUM, - /** realtime latency - realtime applications (effect processors etc.) - (estimated latency in one direction 5ms and better) */ - SND_SPCM_LATENCY_REALTIME -} snd_spcm_latency_t; - -/** Simple PCM xrun type */ -typedef enum _snd_spcm_xrun_type { - /** driver / library will ignore all xruns, the stream runs forever */ - SND_SPCM_XRUN_IGNORE = 0, - /** driver / library stops the stream when an xrun occurs */ - SND_SPCM_XRUN_STOP -} snd_spcm_xrun_type_t; - -/** Simple PCM duplex type */ -typedef enum _snd_spcm_duplex_type { - /** liberal duplex - the buffer and period sizes might not match */ - SND_SPCM_DUPLEX_LIBERAL = 0, - /** pedantic duplex - the buffer and period sizes MUST match */ - SND_SPCM_DUPLEX_PEDANTIC -} snd_spcm_duplex_type_t; - -int snd_spcm_init(snd_pcm_t *pcm, - unsigned int rate, - unsigned int channels, - snd_pcm_format_t format, - snd_pcm_subformat_t subformat, - snd_spcm_latency_t latency, - snd_pcm_access_t _access, - snd_spcm_xrun_type_t xrun_type); - -int snd_spcm_init_duplex(snd_pcm_t *playback_pcm, - snd_pcm_t *capture_pcm, - unsigned int rate, - unsigned int channels, - snd_pcm_format_t format, - snd_pcm_subformat_t subformat, - snd_spcm_latency_t latency, - snd_pcm_access_t _access, - snd_spcm_xrun_type_t xrun_type, - snd_spcm_duplex_type_t duplex_type); - -int snd_spcm_init_get_params(snd_pcm_t *pcm, - unsigned int *rate, - snd_pcm_uframes_t *buffer_size, - snd_pcm_uframes_t *period_size); - -/** \} */ - -/** - * \defgroup PCM_Deprecated Deprecated Functions - * \ingroup PCM - * See the \ref pcm page for more details. - * \{ - */ - -/* Deprecated functions, for compatibility */ -const char *snd_pcm_start_mode_name(snd_pcm_start_t mode) __attribute__((deprecated)); -const char *snd_pcm_xrun_mode_name(snd_pcm_xrun_t mode) __attribute__((deprecated)); -int snd_pcm_sw_params_set_start_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_start_t val) __attribute__((deprecated)); -snd_pcm_start_t snd_pcm_sw_params_get_start_mode(const snd_pcm_sw_params_t *params) __attribute__((deprecated)); -int snd_pcm_sw_params_set_xrun_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_xrun_t val) __attribute__((deprecated)); -snd_pcm_xrun_t snd_pcm_sw_params_get_xrun_mode(const snd_pcm_sw_params_t *params) __attribute__((deprecated)); -#if !defined(ALSA_LIBRARY_BUILD) && !defined(ALSA_PCM_OLD_SW_PARAMS_API) -int snd_pcm_sw_params_set_xfer_align(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val) __attribute__((deprecated)); -int snd_pcm_sw_params_get_xfer_align(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val) __attribute__((deprecated)); -int snd_pcm_sw_params_set_sleep_min(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, unsigned int val) __attribute__((deprecated)); -int snd_pcm_sw_params_get_sleep_min(const snd_pcm_sw_params_t *params, unsigned int *val) __attribute__((deprecated)); -#endif /* !ALSA_LIBRARY_BUILD && !ALSA_PCM_OLD_SW_PARAMS_API */ -#if !defined(ALSA_LIBRARY_BUILD) && !defined(ALSA_PCM_OLD_HW_PARAMS_API) -int snd_pcm_hw_params_get_tick_time(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) __attribute__((deprecated)); -int snd_pcm_hw_params_get_tick_time_min(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) __attribute__((deprecated)); -int snd_pcm_hw_params_get_tick_time_max(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) __attribute__((deprecated)); -int snd_pcm_hw_params_test_tick_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir) __attribute__((deprecated)); -int snd_pcm_hw_params_set_tick_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir) __attribute__((deprecated)); -int snd_pcm_hw_params_set_tick_time_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) __attribute__((deprecated)); -int snd_pcm_hw_params_set_tick_time_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) __attribute__((deprecated)); -int snd_pcm_hw_params_set_tick_time_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir) __attribute__((deprecated)); -int snd_pcm_hw_params_set_tick_time_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) __attribute__((deprecated)); -int snd_pcm_hw_params_set_tick_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) __attribute__((deprecated)); -int snd_pcm_hw_params_set_tick_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) __attribute__((deprecated)); -#endif /* !ALSA_LIBRARY_BUILD && !ALSA_PCM_OLD_HW_PARAMS_API */ - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_PCM_H */ diff --git a/raylib/external/alsa/pcm_external.h b/raylib/external/alsa/pcm_external.h deleted file mode 100644 index 5750418..0000000 --- a/raylib/external/alsa/pcm_external.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * \file include/pcm_external.h - * \brief External PCM plugin SDK - * \author Takashi Iwai - * \date 2005 - * - * Extern PCM plugin SDK. - */ - -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __ALSA_PCM_EXTERNAL_H -#define __ALSA_PCM_EXTERNAL_H - -#include "pcm.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Plugin_SDK External PCM plugin SDK - * \{ - */ - -/** - * Define the object entry for external PCM plugins - */ -#define SND_PCM_PLUGIN_ENTRY(name) _snd_pcm_##name##_open - -/** - * Define the symbols of the given plugin with versions - */ -#define SND_PCM_PLUGIN_SYMBOL(name) SND_DLSYM_BUILD_VERSION(SND_PCM_PLUGIN_ENTRY(name), SND_PCM_DLSYM_VERSION); - -/** - * Define the plugin - */ -#define SND_PCM_PLUGIN_DEFINE_FUNC(plugin) \ -int SND_PCM_PLUGIN_ENTRY(plugin) (snd_pcm_t **pcmp, const char *name,\ - snd_config_t *root, snd_config_t *conf, \ - snd_pcm_stream_t stream, int mode) - -#include "pcm_ioplug.h" -#include "pcm_extplug.h" - -int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp, - int *cchannelsp, int *hwctlp); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_PCM_EXTERNAL_H */ diff --git a/raylib/external/alsa/pcm_extplug.h b/raylib/external/alsa/pcm_extplug.h deleted file mode 100644 index e3b71bc..0000000 --- a/raylib/external/alsa/pcm_extplug.h +++ /dev/null @@ -1,206 +0,0 @@ -/** - * \file include/pcm_extplug.h - * \brief External Filter-Plugin SDK - * \author Takashi Iwai - * \date 2005 - * - * External Filter-Plugin SDK - */ - -/* - * ALSA external PCM plugin SDK (draft version) - * - * Copyright (c) 2005 Takashi Iwai - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_PCM_EXTPLUG_H -#define __ALSA_PCM_EXTPLUG_H - -/** - * \defgroup PCM_ExtPlug External Filter plugin SDK - * \ingroup Plugin_SDK - * See the \ref pcm page for more details. - * \{ - */ - -/** hw constraints for extplug */ -enum { - SND_PCM_EXTPLUG_HW_FORMAT, /**< format */ - SND_PCM_EXTPLUG_HW_CHANNELS, /**< channels */ - SND_PCM_EXTPLUG_HW_PARAMS /**< max number of hw constraints */ -}; - -/** Handle of external filter plugin */ -typedef struct snd_pcm_extplug snd_pcm_extplug_t; -/** Callback table of extplug */ -typedef struct snd_pcm_extplug_callback snd_pcm_extplug_callback_t; -#ifdef DOC_HIDDEN -/* redefine typedefs for stupid doxygen */ -typedef snd_pcm_extplug snd_pcm_extplug_t; -typedef snd_pcm_extplug_callback snd_pcm_extplug_callback_t; -#endif - -/* - * Protocol version - */ -#define SND_PCM_EXTPLUG_VERSION_MAJOR 1 /**< Protocol major version */ -#define SND_PCM_EXTPLUG_VERSION_MINOR 0 /**< Protocol minor version */ -#define SND_PCM_EXTPLUG_VERSION_TINY 2 /**< Protocol tiny version */ -/** - * Filter-plugin protocol version - */ -#define SND_PCM_EXTPLUG_VERSION ((SND_PCM_EXTPLUG_VERSION_MAJOR<<16) |\ - (SND_PCM_EXTPLUG_VERSION_MINOR<<8) |\ - (SND_PCM_EXTPLUG_VERSION_TINY)) - -/** Handle of extplug */ -struct snd_pcm_extplug { - /** - * protocol version; #SND_PCM_EXTPLUG_VERSION must be filled here - * before calling #snd_pcm_extplug_create() - */ - unsigned int version; - /** - * name of this plugin; must be filled before calling #snd_pcm_extplug_create() - */ - const char *name; - /** - * callbacks of this plugin; must be filled before calling #snd_pcm_extplug_create() - */ - const snd_pcm_extplug_callback_t *callback; - /** - * private data, which can be used freely in the driver callbacks - */ - void *private_data; - /** - * PCM handle filled by #snd_pcm_extplug_create() - */ - snd_pcm_t *pcm; - /** - * stream direction; read-only status - */ - snd_pcm_stream_t stream; - /** - * format hw parameter; filled after hw_params is caled - */ - snd_pcm_format_t format; - /** - * subformat hw parameter; filled after hw_params is caled - */ - snd_pcm_subformat_t subformat; - /** - * channels hw parameter; filled after hw_params is caled - */ - unsigned int channels; - /** - * rate hw parameter; filled after hw_params is caled - */ - unsigned int rate; - /** - * slave_format hw parameter; filled after hw_params is caled - */ - snd_pcm_format_t slave_format; - /** - * slave_subformat hw parameter; filled after hw_params is caled - */ - snd_pcm_subformat_t slave_subformat; - /** - * slave_channels hw parameter; filled after hw_params is caled - */ - unsigned int slave_channels; -}; - -/** Callback table of extplug */ -struct snd_pcm_extplug_callback { - /** - * transfer between source and destination; this is a required callback - */ - snd_pcm_sframes_t (*transfer)(snd_pcm_extplug_t *ext, - const snd_pcm_channel_area_t *dst_areas, - snd_pcm_uframes_t dst_offset, - const snd_pcm_channel_area_t *src_areas, - snd_pcm_uframes_t src_offset, - snd_pcm_uframes_t size); - /** - * close the PCM; optional - */ - int (*close)(snd_pcm_extplug_t *ext); - /** - * hw_params; optional - */ - int (*hw_params)(snd_pcm_extplug_t *ext, snd_pcm_hw_params_t *params); - /** - * hw_free; optional - */ - int (*hw_free)(snd_pcm_extplug_t *ext); - /** - * dump; optional - */ - void (*dump)(snd_pcm_extplug_t *ext, snd_output_t *out); - /** - * init; optional initialization called at prepare or reset - */ - int (*init)(snd_pcm_extplug_t *ext); - /** - * query the channel maps; optional; since v1.0.2 - */ - snd_pcm_chmap_query_t **(*query_chmaps)(snd_pcm_extplug_t *ext); - /** - * get the channel map; optional; since v1.0.2 - */ - snd_pcm_chmap_t *(*get_chmap)(snd_pcm_extplug_t *ext); - /** - * set the channel map; optional; since v1.0.2 - */ - int (*set_chmap)(snd_pcm_extplug_t *ext, const snd_pcm_chmap_t *map); -}; - - -int snd_pcm_extplug_create(snd_pcm_extplug_t *ext, const char *name, - snd_config_t *root, snd_config_t *slave_conf, - snd_pcm_stream_t stream, int mode); -int snd_pcm_extplug_delete(snd_pcm_extplug_t *ext); - -/* clear hw_parameter setting */ -void snd_pcm_extplug_params_reset(snd_pcm_extplug_t *ext); - -/* hw_parameter setting */ -int snd_pcm_extplug_set_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list); -int snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max); -int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list); -int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max); - -/** - * set the parameter constraint with a single value - */ -static __inline__ int snd_pcm_extplug_set_param(snd_pcm_extplug_t *extplug, int type, unsigned int val) -{ - return snd_pcm_extplug_set_param_list(extplug, type, 1, &val); -} - -/** - * set the parameter constraint for slave PCM with a single value - */ -static __inline__ int snd_pcm_extplug_set_slave_param(snd_pcm_extplug_t *extplug, int type, unsigned int val) -{ - return snd_pcm_extplug_set_slave_param_list(extplug, type, 1, &val); -} - -/** \} */ - -#endif /* __ALSA_PCM_EXTPLUG_H */ diff --git a/raylib/external/alsa/pcm_ioplug.h b/raylib/external/alsa/pcm_ioplug.h deleted file mode 100644 index 8c25e5e..0000000 --- a/raylib/external/alsa/pcm_ioplug.h +++ /dev/null @@ -1,234 +0,0 @@ -/** - * \file include/pcm_ioplug.h - * \brief External I/O-Plugin SDK - * \author Takashi Iwai - * \date 2005 - * - * External I/O-Plugin SDK - */ - -/* - * ALSA external PCM plugin SDK - * - * Copyright (c) 2005 Takashi Iwai - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_PCM_IOPLUG_H -#define __ALSA_PCM_IOPLUG_H - -/** - * \defgroup PCM_IOPlug External I/O plugin SDK - * \ingroup Plugin_SDK - * See the \ref pcm page for more details. - * \{ - */ - -/** hw constraints for ioplug */ -enum { - SND_PCM_IOPLUG_HW_ACCESS = 0, /**< access type */ - SND_PCM_IOPLUG_HW_FORMAT, /**< format */ - SND_PCM_IOPLUG_HW_CHANNELS, /**< channels */ - SND_PCM_IOPLUG_HW_RATE, /**< rate */ - SND_PCM_IOPLUG_HW_PERIOD_BYTES, /**< period bytes */ - SND_PCM_IOPLUG_HW_BUFFER_BYTES, /**< buffer bytes */ - SND_PCM_IOPLUG_HW_PERIODS, /**< number of periods */ - SND_PCM_IOPLUG_HW_PARAMS /**< max number of hw constraints */ -}; - -/** I/O plugin handle */ -typedef struct snd_pcm_ioplug snd_pcm_ioplug_t; -/** Callback table of ioplug */ -typedef struct snd_pcm_ioplug_callback snd_pcm_ioplug_callback_t; -#ifdef DOC_HIDDEN -/* redefine typedefs for stupid doxygen */ -typedef snd_pcm_ioplug snd_pcm_ioplug_t; -typedef snd_pcm_ioplug_callback snd_pcm_ioplug_callback_t; -#endif - -/* - * bit flags for additional conditions - */ -#define SND_PCM_IOPLUG_FLAG_LISTED (1<<0) /**< list up this PCM */ -#define SND_PCM_IOPLUG_FLAG_MONOTONIC (1<<1) /**< monotonic timestamps */ - -/* - * Protocol version - */ -#define SND_PCM_IOPLUG_VERSION_MAJOR 1 /**< Protocol major version */ -#define SND_PCM_IOPLUG_VERSION_MINOR 0 /**< Protocol minor version */ -#define SND_PCM_IOPLUG_VERSION_TINY 2 /**< Protocol tiny version */ -/** - * IO-plugin protocol version - */ -#define SND_PCM_IOPLUG_VERSION ((SND_PCM_IOPLUG_VERSION_MAJOR<<16) |\ - (SND_PCM_IOPLUG_VERSION_MINOR<<8) |\ - (SND_PCM_IOPLUG_VERSION_TINY)) - -/** Handle of ioplug */ -struct snd_pcm_ioplug { - /** - * protocol version; #SND_PCM_IOPLUG_VERSION must be filled here - * before calling #snd_pcm_ioplug_create() - */ - unsigned int version; - /** - * name of this plugin; must be filled before calling #snd_pcm_ioplug_create() - */ - const char *name; - unsigned int flags; /**< SND_PCM_IOPLUG_FLAG_XXX */ - int poll_fd; /**< poll file descriptor */ - unsigned int poll_events; /**< poll events */ - unsigned int mmap_rw; /**< pseudo mmap mode */ - /** - * callbacks of this plugin; must be filled before calling #snd_pcm_ioplug_create() - */ - const snd_pcm_ioplug_callback_t *callback; - /** - * private data, which can be used freely in the driver callbacks - */ - void *private_data; - /** - * PCM handle filled by #snd_pcm_extplug_create() - */ - snd_pcm_t *pcm; - - snd_pcm_stream_t stream; /**< stream direcion; read-only */ - snd_pcm_state_t state; /**< current PCM state; read-only */ - volatile snd_pcm_uframes_t appl_ptr; /**< application pointer; read-only */ - volatile snd_pcm_uframes_t hw_ptr; /**< hw pointer; read-only */ - int nonblock; /**< non-block mode; read-only */ - - snd_pcm_access_t access; /**< access type; filled after hw_params is called */ - snd_pcm_format_t format; /**< PCM format; filled after hw_params is called */ - unsigned int channels; /**< number of channels; filled after hw_params is called */ - unsigned int rate; /**< rate; filled after hw_params is called */ - snd_pcm_uframes_t period_size; /**< period size; filled after hw_params is called */ - snd_pcm_uframes_t buffer_size; /**< buffer size; filled after hw_params is called */ -}; - -/** Callback table of ioplug */ -struct snd_pcm_ioplug_callback { - /** - * start the PCM; required, called inside mutex lock - */ - int (*start)(snd_pcm_ioplug_t *io); - /** - * stop the PCM; required, called inside mutex lock - */ - int (*stop)(snd_pcm_ioplug_t *io); - /** - * get the current DMA position; required, called inside mutex lock - */ - snd_pcm_sframes_t (*pointer)(snd_pcm_ioplug_t *io); - /** - * transfer the data; optional, called inside mutex lock - */ - snd_pcm_sframes_t (*transfer)(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size); - /** - * close the PCM; optional - */ - int (*close)(snd_pcm_ioplug_t *io); - /** - * hw_params; optional - */ - int (*hw_params)(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params); - /** - * hw_free; optional - */ - int (*hw_free)(snd_pcm_ioplug_t *io); - /** - * sw_params; optional - */ - int (*sw_params)(snd_pcm_ioplug_t *io, snd_pcm_sw_params_t *params); - /** - * prepare; optional - */ - int (*prepare)(snd_pcm_ioplug_t *io); - /** - * drain; optional - */ - int (*drain)(snd_pcm_ioplug_t *io); - /** - * toggle pause; optional, called inside mutex lock - */ - int (*pause)(snd_pcm_ioplug_t *io, int enable); - /** - * resume; optional - */ - int (*resume)(snd_pcm_ioplug_t *io); - /** - * poll descriptors count; optional - */ - int (*poll_descriptors_count)(snd_pcm_ioplug_t *io); - /** - * poll descriptors; optional - */ - int (*poll_descriptors)(snd_pcm_ioplug_t *io, struct pollfd *pfd, unsigned int space); - /** - * mangle poll events; optional - */ - int (*poll_revents)(snd_pcm_ioplug_t *io, struct pollfd *pfd, unsigned int nfds, unsigned short *revents); - /** - * dump; optional - */ - void (*dump)(snd_pcm_ioplug_t *io, snd_output_t *out); - /** - * get the delay for the running PCM; optional; since v1.0.1 - */ - int (*delay)(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp); - /** - * query the channel maps; optional; since v1.0.2 - */ - snd_pcm_chmap_query_t **(*query_chmaps)(snd_pcm_ioplug_t *io); - /** - * get the channel map; optional; since v1.0.2 - */ - snd_pcm_chmap_t *(*get_chmap)(snd_pcm_ioplug_t *io); - /** - * set the channel map; optional; since v1.0.2 - */ - int (*set_chmap)(snd_pcm_ioplug_t *io, const snd_pcm_chmap_t *map); -}; - - -int snd_pcm_ioplug_create(snd_pcm_ioplug_t *io, const char *name, - snd_pcm_stream_t stream, int mode); -int snd_pcm_ioplug_delete(snd_pcm_ioplug_t *io); - -/* update poll_fd and mmap_rw */ -int snd_pcm_ioplug_reinit_status(snd_pcm_ioplug_t *ioplug); - -/* get a mmap area (for mmap_rw only) */ -const snd_pcm_channel_area_t *snd_pcm_ioplug_mmap_areas(snd_pcm_ioplug_t *ioplug); - -/* clear hw_parameter setting */ -void snd_pcm_ioplug_params_reset(snd_pcm_ioplug_t *io); - -/* hw_parameter setting */ -int snd_pcm_ioplug_set_param_minmax(snd_pcm_ioplug_t *io, int type, unsigned int min, unsigned int max); -int snd_pcm_ioplug_set_param_list(snd_pcm_ioplug_t *io, int type, unsigned int num_list, const unsigned int *list); - -/* change PCM status */ -int snd_pcm_ioplug_set_state(snd_pcm_ioplug_t *ioplug, snd_pcm_state_t state); - -/** \} */ - -#endif /* __ALSA_PCM_IOPLUG_H */ diff --git a/raylib/external/alsa/pcm_old.h b/raylib/external/alsa/pcm_old.h deleted file mode 100644 index e6e050f..0000000 --- a/raylib/external/alsa/pcm_old.h +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Old ALSA 0.9.x API - */ - -#ifdef ALSA_PCM_OLD_HW_PARAMS_API - -asm(".symver snd_pcm_hw_params_get_access,snd_pcm_hw_params_get_access@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_access_first,snd_pcm_hw_params_set_access_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_access_last,snd_pcm_hw_params_set_access_last@ALSA_0.9"); - -int snd_pcm_hw_params_get_access(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_test_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t val); -int snd_pcm_hw_params_set_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t val); -snd_pcm_access_t snd_pcm_hw_params_set_access_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -snd_pcm_access_t snd_pcm_hw_params_set_access_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_set_access_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_mask_t *mask); -void snd_pcm_hw_params_get_access_mask(snd_pcm_hw_params_t *params, snd_pcm_access_mask_t *mask); - -asm(".symver snd_pcm_hw_params_get_format,snd_pcm_hw_params_get_format@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_format_first,snd_pcm_hw_params_set_format_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_format_last,snd_pcm_hw_params_set_format_last@ALSA_0.9"); - -int snd_pcm_hw_params_get_format(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_test_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); -int snd_pcm_hw_params_set_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); -snd_pcm_format_t snd_pcm_hw_params_set_format_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -snd_pcm_format_t snd_pcm_hw_params_set_format_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_set_format_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); -void snd_pcm_hw_params_get_format_mask(snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); - -asm(".symver snd_pcm_hw_params_get_subformat,snd_pcm_hw_params_get_subformat@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_subformat_first,snd_pcm_hw_params_set_subformat_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_subformat_last,snd_pcm_hw_params_set_subformat_last@ALSA_0.9"); - -int snd_pcm_hw_params_test_subformat(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t val); -int snd_pcm_hw_params_get_subformat(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_set_subformat(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t val); -snd_pcm_subformat_t snd_pcm_hw_params_set_subformat_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -snd_pcm_subformat_t snd_pcm_hw_params_set_subformat_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_set_subformat_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_mask_t *mask); -void snd_pcm_hw_params_get_subformat_mask(snd_pcm_hw_params_t *params, snd_pcm_subformat_mask_t *mask); - -asm(".symver snd_pcm_hw_params_get_channels,snd_pcm_hw_params_get_channels@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_channels_min,snd_pcm_hw_params_get_channels_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_channels_max,snd_pcm_hw_params_get_channels_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_channels_near,snd_pcm_hw_params_set_channels_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_channels_first,snd_pcm_hw_params_set_channels_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_channels_last,snd_pcm_hw_params_set_channels_last@ALSA_0.9"); - -int snd_pcm_hw_params_get_channels(const snd_pcm_hw_params_t *params); -unsigned int snd_pcm_hw_params_get_channels_min(const snd_pcm_hw_params_t *params); -unsigned int snd_pcm_hw_params_get_channels_max(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_test_channels(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -int snd_pcm_hw_params_set_channels(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -int snd_pcm_hw_params_set_channels_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_set_channels_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -int snd_pcm_hw_params_set_channels_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, unsigned int *max); -unsigned int snd_pcm_hw_params_set_channels_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -unsigned int snd_pcm_hw_params_set_channels_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -unsigned int snd_pcm_hw_params_set_channels_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); - -asm(".symver snd_pcm_hw_params_get_rate,snd_pcm_hw_params_get_rate@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_rate_min,snd_pcm_hw_params_get_rate_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_rate_max,snd_pcm_hw_params_get_rate_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_rate_near,snd_pcm_hw_params_set_rate_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_rate_first,snd_pcm_hw_params_set_rate_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_rate_last,snd_pcm_hw_params_set_rate_last@ALSA_0.9"); - -int snd_pcm_hw_params_get_rate(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_rate_min(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_rate_max(const snd_pcm_hw_params_t *params, int *dir); -int snd_pcm_hw_params_test_rate(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_rate(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_rate_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_rate_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_rate_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); -unsigned int snd_pcm_hw_params_set_rate_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int *dir); -unsigned int snd_pcm_hw_params_set_rate_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_set_rate_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -int snd_pcm_hw_params_set_rate_resample(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -int snd_pcm_hw_params_get_rate_resample(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); - -asm(".symver snd_pcm_hw_params_get_period_time,snd_pcm_hw_params_get_period_time@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_time_min,snd_pcm_hw_params_get_period_time_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_time_max,snd_pcm_hw_params_get_period_time_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_time_near,snd_pcm_hw_params_set_period_time_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_time_first,snd_pcm_hw_params_set_period_time_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_time_last,snd_pcm_hw_params_set_period_time_last@ALSA_0.9"); - -int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_period_time_min(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_period_time_max(const snd_pcm_hw_params_t *params, int *dir); -int snd_pcm_hw_params_test_period_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_period_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_period_time_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_period_time_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_period_time_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); -unsigned int snd_pcm_hw_params_set_period_time_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int *dir); -unsigned int snd_pcm_hw_params_set_period_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_set_period_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); - -asm(".symver snd_pcm_hw_params_get_period_size,snd_pcm_hw_params_get_period_size@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_size_min,snd_pcm_hw_params_get_period_size_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_size_max,snd_pcm_hw_params_get_period_size_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_size_near,snd_pcm_hw_params_set_period_size_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_size_first,snd_pcm_hw_params_set_period_size_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_size_last,snd_pcm_hw_params_set_period_size_last@ALSA_0.9"); - -snd_pcm_sframes_t snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params, int *dir); -snd_pcm_uframes_t snd_pcm_hw_params_get_period_size_min(const snd_pcm_hw_params_t *params, int *dir); -snd_pcm_uframes_t snd_pcm_hw_params_get_period_size_max(const snd_pcm_hw_params_t *params, int *dir); -int snd_pcm_hw_params_test_period_size(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int dir); -int snd_pcm_hw_params_set_period_size(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int dir); -int snd_pcm_hw_params_set_period_size_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); -int snd_pcm_hw_params_set_period_size_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); -int snd_pcm_hw_params_set_period_size_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *min, int *mindir, snd_pcm_uframes_t *max, int *maxdir); -snd_pcm_uframes_t snd_pcm_hw_params_set_period_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int *dir); -snd_pcm_uframes_t snd_pcm_hw_params_set_period_size_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -snd_pcm_uframes_t snd_pcm_hw_params_set_period_size_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -int snd_pcm_hw_params_set_period_size_integer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); - -asm(".symver snd_pcm_hw_params_get_periods,snd_pcm_hw_params_get_periods@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_periods_min,snd_pcm_hw_params_get_periods_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_periods_max,snd_pcm_hw_params_get_periods_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_periods_near,snd_pcm_hw_params_set_periods_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_periods_first,snd_pcm_hw_params_set_periods_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_periods_last,snd_pcm_hw_params_set_periods_last@ALSA_0.9"); - -int snd_pcm_hw_params_get_periods(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_periods_min(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_periods_max(const snd_pcm_hw_params_t *params, int *dir); -int snd_pcm_hw_params_test_periods(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_periods(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_periods_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_periods_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_periods_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); -unsigned int snd_pcm_hw_params_set_periods_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int *dir); -unsigned int snd_pcm_hw_params_set_periods_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_set_periods_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -int snd_pcm_hw_params_set_periods_integer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); - -asm(".symver snd_pcm_hw_params_get_buffer_time,snd_pcm_hw_params_get_buffer_time@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_time_min,snd_pcm_hw_params_get_buffer_time_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_time_max,snd_pcm_hw_params_get_buffer_time_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_time_near,snd_pcm_hw_params_set_buffer_time_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_time_first,snd_pcm_hw_params_set_buffer_time_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_time_last,snd_pcm_hw_params_set_buffer_time_last@ALSA_0.9"); - -int snd_pcm_hw_params_get_buffer_time(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_buffer_time_min(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_buffer_time_max(const snd_pcm_hw_params_t *params, int *dir); -int snd_pcm_hw_params_test_buffer_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_buffer_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_buffer_time_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_buffer_time_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_buffer_time_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); -unsigned int snd_pcm_hw_params_set_buffer_time_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int *dir); -unsigned int snd_pcm_hw_params_set_buffer_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_set_buffer_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); - -asm(".symver snd_pcm_hw_params_get_buffer_size,snd_pcm_hw_params_get_buffer_size@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_size_min,snd_pcm_hw_params_get_buffer_size_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_size_max,snd_pcm_hw_params_get_buffer_size_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_size_near,snd_pcm_hw_params_set_buffer_size_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_size_first,snd_pcm_hw_params_set_buffer_size_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_size_last,snd_pcm_hw_params_set_buffer_size_last@ALSA_0.9"); - -snd_pcm_sframes_t snd_pcm_hw_params_get_buffer_size(const snd_pcm_hw_params_t *params); -snd_pcm_uframes_t snd_pcm_hw_params_get_buffer_size_min(const snd_pcm_hw_params_t *params); -snd_pcm_uframes_t snd_pcm_hw_params_get_buffer_size_max(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_test_buffer_size(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val); -int snd_pcm_hw_params_set_buffer_size(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val); -int snd_pcm_hw_params_set_buffer_size_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_hw_params_set_buffer_size_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -int snd_pcm_hw_params_set_buffer_size_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *min, snd_pcm_uframes_t *max); -snd_pcm_uframes_t snd_pcm_hw_params_set_buffer_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val); -snd_pcm_uframes_t snd_pcm_hw_params_set_buffer_size_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -snd_pcm_uframes_t snd_pcm_hw_params_set_buffer_size_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); - -asm(".symver snd_pcm_hw_params_get_tick_time,snd_pcm_hw_params_get_tick_time@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_tick_time_min,snd_pcm_hw_params_get_tick_time_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_tick_time_max,snd_pcm_hw_params_get_tick_time_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_tick_time_near,snd_pcm_hw_params_set_tick_time_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_tick_time_first,snd_pcm_hw_params_set_tick_time_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_tick_time_last,snd_pcm_hw_params_set_tick_time_last@ALSA_0.9"); - -int snd_pcm_hw_params_get_tick_time(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_tick_time_min(const snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_get_tick_time_max(const snd_pcm_hw_params_t *params, int *dir); -int snd_pcm_hw_params_test_tick_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_tick_time(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir); -int snd_pcm_hw_params_set_tick_time_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_tick_time_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -int snd_pcm_hw_params_set_tick_time_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); -unsigned int snd_pcm_hw_params_set_tick_time_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int *dir); -unsigned int snd_pcm_hw_params_set_tick_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -unsigned int snd_pcm_hw_params_set_tick_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); - -#endif /* ALSA_PCM_OLD_HW_PARAMS_API */ - - -#ifdef ALSA_PCM_OLD_SW_PARAMS_API - -asm(".symver snd_pcm_sw_params_get_tstamp_mode,snd_pcm_sw_params_get_tstamp_mode@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_sleep_min,snd_pcm_sw_params_get_sleep_min@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_avail_min,snd_pcm_sw_params_get_avail_min@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_xfer_align,snd_pcm_sw_params_get_xfer_align@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_start_threshold,snd_pcm_sw_params_get_start_threshold@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_stop_threshold,snd_pcm_sw_params_get_stop_threshold@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_silence_threshold,snd_pcm_sw_params_get_silence_threshold@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_silence_size,snd_pcm_sw_params_get_silence_size@ALSA_0.9"); - -int snd_pcm_sw_params_set_tstamp_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_tstamp_t val); -snd_pcm_tstamp_t snd_pcm_sw_params_get_tstamp_mode(const snd_pcm_sw_params_t *params); -int snd_pcm_sw_params_set_sleep_min(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, unsigned int val); -unsigned int snd_pcm_sw_params_get_sleep_min(const snd_pcm_sw_params_t *params); -int snd_pcm_sw_params_set_avail_min(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -snd_pcm_uframes_t snd_pcm_sw_params_get_avail_min(const snd_pcm_sw_params_t *params); -int snd_pcm_sw_params_set_xfer_align(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -snd_pcm_uframes_t snd_pcm_sw_params_get_xfer_align(const snd_pcm_sw_params_t *params); -int snd_pcm_sw_params_set_start_threshold(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -snd_pcm_uframes_t snd_pcm_sw_params_get_start_threshold(const snd_pcm_sw_params_t *params); -int snd_pcm_sw_params_set_stop_threshold(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -snd_pcm_uframes_t snd_pcm_sw_params_get_stop_threshold(const snd_pcm_sw_params_t *params); -int snd_pcm_sw_params_set_silence_threshold(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -snd_pcm_uframes_t snd_pcm_sw_params_get_silence_threshold(const snd_pcm_sw_params_t *params); -int snd_pcm_sw_params_set_silence_size(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -snd_pcm_uframes_t snd_pcm_sw_params_get_silence_size(const snd_pcm_sw_params_t *params); - -#endif /* ALSA_PCM_OLD_SW_PARAMS_API */ diff --git a/raylib/external/alsa/pcm_plugin.h b/raylib/external/alsa/pcm_plugin.h deleted file mode 100644 index eea1d82..0000000 --- a/raylib/external/alsa/pcm_plugin.h +++ /dev/null @@ -1,202 +0,0 @@ -/** - * \file include/pcm_plugin.h - * \brief Common PCM plugin code - * \author Abramo Bagnara - * \author Jaroslav Kysela - * \date 2000-2001 - * - * Application interface library for the ALSA driver. - * See the \ref pcm_plugins page for more details. - * - * \warning Using of contents of this header file might be dangerous - * in the sense of compatibility reasons. The contents might be - * freely changed in future. - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_PCM_PLUGIN_H - -/** - * \defgroup PCM_Plugins PCM Plugins - * \ingroup PCM - * See the \ref pcm_plugins page for more details. - * \{ - */ - -#define SND_PCM_PLUGIN_RATE_MIN 4000 /**< minimal rate for the rate plugin */ -#define SND_PCM_PLUGIN_RATE_MAX 192000 /**< maximal rate for the rate plugin */ - -/* ROUTE_FLOAT should be set to 0 for machines without FP unit - like iPAQ */ -#ifdef HAVE_SOFT_FLOAT -#define SND_PCM_PLUGIN_ROUTE_FLOAT 0 /**< use integers for route plugin */ -#else -#define SND_PCM_PLUGIN_ROUTE_FLOAT 1 /**< use floats for route plugin */ -#endif - -#define SND_PCM_PLUGIN_ROUTE_RESOLUTION 16 /**< integer resolution for route plugin */ - -#if SND_PCM_PLUGIN_ROUTE_FLOAT -/** route ttable entry type */ -typedef float snd_pcm_route_ttable_entry_t; -#define SND_PCM_PLUGIN_ROUTE_HALF 0.5 /**< half value */ -#define SND_PCM_PLUGIN_ROUTE_FULL 1.0 /**< full value */ -#else -/** route ttable entry type */ -typedef int snd_pcm_route_ttable_entry_t; -#define SND_PCM_PLUGIN_ROUTE_HALF (SND_PCM_PLUGIN_ROUTE_RESOLUTION / 2) /**< half value */ -#define SND_PCM_PLUGIN_ROUTE_FULL SND_PCM_PLUGIN_ROUTE_RESOLUTION /**< full value */ -#endif - -/* - * Hardware plugin - */ -int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, - int card, int device, int subdevice, - snd_pcm_stream_t stream, int mode, - int mmap_emulation, int sync_ptr_ioctl); -int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Copy plugin - */ -int snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_t *slave, int close_slave); -int _snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Linear conversion plugin - */ -int snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_format_t sformat, snd_pcm_t *slave, - int close_slave); -int _snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Linear<->Float conversion plugin - */ -int snd_pcm_lfloat_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_format_t sformat, snd_pcm_t *slave, - int close_slave); -int _snd_pcm_lfloat_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Linear<->mu-Law conversion plugin - */ -int snd_pcm_mulaw_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_format_t sformat, snd_pcm_t *slave, - int close_slave); -int _snd_pcm_mulaw_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Linear<->a-Law conversion plugin - */ -int snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_format_t sformat, snd_pcm_t *slave, - int close_slave); -int _snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Linear<->Ima-ADPCM conversion plugin - */ -int snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_format_t sformat, snd_pcm_t *slave, - int close_slave); -int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Route plugin for linear formats - */ -int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable, - unsigned int tt_csize, unsigned int tt_ssize, - unsigned int *tt_cused, unsigned int *tt_sused, - int schannels); -int snd_pcm_route_determine_ttable(snd_config_t *tt, - unsigned int *tt_csize, - unsigned int *tt_ssize); -int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_format_t sformat, int schannels, - snd_pcm_route_ttable_entry_t *ttable, - unsigned int tt_ssize, - unsigned int tt_cused, unsigned int tt_sused, - snd_pcm_t *slave, int close_slave); -int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Rate plugin for linear formats - */ -int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_format_t sformat, unsigned int srate, - const snd_config_t *converter, - snd_pcm_t *slave, int close_slave); -int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Hooks plugin - */ -int snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_t *slave, int close_slave); -int _snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * LADSPA plugin - */ -int snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name, - const char *ladspa_path, - unsigned int channels, - snd_config_t *ladspa_pplugins, - snd_config_t *ladspa_cplugins, - snd_pcm_t *slave, int close_slave); -int _snd_pcm_ladspa_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - -/* - * Jack plugin - */ -int snd_pcm_jack_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *playback_conf, - snd_config_t *capture_conf, - snd_pcm_stream_t stream, int mode); -int _snd_pcm_jack_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); - - -/** \} */ - -#endif /* __ALSA_PCM_PLUGIN_H */ diff --git a/raylib/external/alsa/pcm_rate.h b/raylib/external/alsa/pcm_rate.h deleted file mode 100644 index da278ac..0000000 --- a/raylib/external/alsa/pcm_rate.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * \file include/pcm_rate.h - * \brief External Rate-Converter-Plugin SDK - * \author Takashi Iwai - * \date 2006 - * - * External Rate-Converter-Plugin SDK - */ - -/* - * ALSA external PCM rate-converter plugin SDK (draft version) - * - * Copyright (c) 2006 Takashi Iwai - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_PCM_RATE_H -#define __ALSA_PCM_RATE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Protocol version - */ -#define SND_PCM_RATE_PLUGIN_VERSION 0x010002 - -/** hw_params information for a single side */ -typedef struct snd_pcm_rate_side_info { - snd_pcm_format_t format; - unsigned int rate; - snd_pcm_uframes_t buffer_size; - snd_pcm_uframes_t period_size; -} snd_pcm_rate_side_info_t; - -/** hw_params information */ -typedef struct snd_pcm_rate_info { - struct snd_pcm_rate_side_info in; - struct snd_pcm_rate_side_info out; - unsigned int channels; -} snd_pcm_rate_info_t; - -/** Callback table of rate-converter */ -typedef struct snd_pcm_rate_ops { - /** - * close the converter; optional - */ - void (*close)(void *obj); - /** - * initialize the converter, called at hw_params - */ - int (*init)(void *obj, snd_pcm_rate_info_t *info); - /** - * free the converter; optional - */ - void (*free)(void *obj); - /** - * reset the converter, called at prepare; optional - */ - void (*reset)(void *obj); - /** - * adjust the pitch, called at sw_params; optional - */ - int (*adjust_pitch)(void *obj, snd_pcm_rate_info_t *info); - /** - * convert the data - */ - void (*convert)(void *obj, - const snd_pcm_channel_area_t *dst_areas, - snd_pcm_uframes_t dst_offset, unsigned int dst_frames, - const snd_pcm_channel_area_t *src_areas, - snd_pcm_uframes_t src_offset, unsigned int src_frames); - /** - * convert an s16 interleaved-data array; exclusive with convert - */ - void (*convert_s16)(void *obj, int16_t *dst, unsigned int dst_frames, - const int16_t *src, unsigned int src_frames); - /** - * compute the frame size for input - */ - snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames); - /** - * compute the frame size for output - */ - snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames); - /** - * the protocol version the plugin supports; - * new field since version 0x010002 - */ - unsigned int version; - /** - * return the supported min / max sample rates; - * new ops since version 0x010002 - */ - int (*get_supported_rates)(void *obj, unsigned int *rate_min, - unsigned int *rate_max); - /** - * show some status messages for verbose mode; - * new ops since version 0x010002 - */ - void (*dump)(void *obj, snd_output_t *out); -} snd_pcm_rate_ops_t; - -/** open function type */ -typedef int (*snd_pcm_rate_open_func_t)(unsigned int version, void **objp, - snd_pcm_rate_ops_t *opsp); - -typedef int (*snd_pcm_rate_open_conf_func_t)(unsigned int version, void **objp, - snd_pcm_rate_ops_t *opsp, const snd_config_t *conf); - -/** - * Define the object entry for external PCM rate-converter plugins - */ -#define SND_PCM_RATE_PLUGIN_ENTRY(name) _snd_pcm_rate_##name##_open -#define SND_PCM_RATE_PLUGIN_CONF_ENTRY(name) _snd_pcm_rate_##name##_open_conf - -#ifndef DOC_HIDDEN -/* old rate_ops for protocol version 0x010001 */ -typedef struct snd_pcm_rate_old_ops { - void (*close)(void *obj); - int (*init)(void *obj, snd_pcm_rate_info_t *info); - void (*free)(void *obj); - void (*reset)(void *obj); - int (*adjust_pitch)(void *obj, snd_pcm_rate_info_t *info); - void (*convert)(void *obj, - const snd_pcm_channel_area_t *dst_areas, - snd_pcm_uframes_t dst_offset, unsigned int dst_frames, - const snd_pcm_channel_area_t *src_areas, - snd_pcm_uframes_t src_offset, unsigned int src_frames); - void (*convert_s16)(void *obj, int16_t *dst, unsigned int dst_frames, - const int16_t *src, unsigned int src_frames); - snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames); - snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames); -} snd_pcm_rate_old_ops_t; -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_PCM_RATE_H */ diff --git a/raylib/external/alsa/rawmidi.h b/raylib/external/alsa/rawmidi.h deleted file mode 100644 index 1d8fd56..0000000 --- a/raylib/external/alsa/rawmidi.h +++ /dev/null @@ -1,159 +0,0 @@ -/** - * \file include/rawmidi.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_RAWMIDI_H -#define __ALSA_RAWMIDI_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup RawMidi RawMidi Interface - * The RawMidi Interface. See \ref rawmidi page for more details. - * \{ - */ - -/** dlsym version for interface entry callback */ -#define SND_RAWMIDI_DLSYM_VERSION _dlsym_rawmidi_001 - -/** RawMidi information container */ -typedef struct _snd_rawmidi_info snd_rawmidi_info_t; -/** RawMidi settings container */ -typedef struct _snd_rawmidi_params snd_rawmidi_params_t; -/** RawMidi status container */ -typedef struct _snd_rawmidi_status snd_rawmidi_status_t; - -/** RawMidi stream (direction) */ -typedef enum _snd_rawmidi_stream { - /** Output stream */ - SND_RAWMIDI_STREAM_OUTPUT = 0, - /** Input stream */ - SND_RAWMIDI_STREAM_INPUT, - SND_RAWMIDI_STREAM_LAST = SND_RAWMIDI_STREAM_INPUT -} snd_rawmidi_stream_t; - -/** Append (flag to open mode) \hideinitializer */ -#define SND_RAWMIDI_APPEND 0x0001 -/** Non blocking mode (flag to open mode) \hideinitializer */ -#define SND_RAWMIDI_NONBLOCK 0x0002 -/** Write sync mode (Flag to open mode) \hideinitializer */ -#define SND_RAWMIDI_SYNC 0x0004 - -/** RawMidi handle */ -typedef struct _snd_rawmidi snd_rawmidi_t; - -/** RawMidi type */ -typedef enum _snd_rawmidi_type { - /** Kernel level RawMidi */ - SND_RAWMIDI_TYPE_HW, - /** Shared memory client RawMidi (not yet implemented) */ - SND_RAWMIDI_TYPE_SHM, - /** INET client RawMidi (not yet implemented) */ - SND_RAWMIDI_TYPE_INET, - /** Virtual (sequencer) RawMidi */ - SND_RAWMIDI_TYPE_VIRTUAL -} snd_rawmidi_type_t; - -int snd_rawmidi_open(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, - const char *name, int mode); -int snd_rawmidi_open_lconf(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, - const char *name, int mode, snd_config_t *lconf); -int snd_rawmidi_close(snd_rawmidi_t *rmidi); -int snd_rawmidi_poll_descriptors_count(snd_rawmidi_t *rmidi); -int snd_rawmidi_poll_descriptors(snd_rawmidi_t *rmidi, struct pollfd *pfds, unsigned int space); -int snd_rawmidi_poll_descriptors_revents(snd_rawmidi_t *rawmidi, struct pollfd *pfds, unsigned int nfds, unsigned short *revent); -int snd_rawmidi_nonblock(snd_rawmidi_t *rmidi, int nonblock); -size_t snd_rawmidi_info_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_rawmidi_info_t using standard alloca - * \param ptr returned pointer - */ -#define snd_rawmidi_info_alloca(ptr) __snd_alloca(ptr, snd_rawmidi_info) -int snd_rawmidi_info_malloc(snd_rawmidi_info_t **ptr); -void snd_rawmidi_info_free(snd_rawmidi_info_t *obj); -void snd_rawmidi_info_copy(snd_rawmidi_info_t *dst, const snd_rawmidi_info_t *src); -unsigned int snd_rawmidi_info_get_device(const snd_rawmidi_info_t *obj); -unsigned int snd_rawmidi_info_get_subdevice(const snd_rawmidi_info_t *obj); -snd_rawmidi_stream_t snd_rawmidi_info_get_stream(const snd_rawmidi_info_t *obj); -int snd_rawmidi_info_get_card(const snd_rawmidi_info_t *obj); -unsigned int snd_rawmidi_info_get_flags(const snd_rawmidi_info_t *obj); -const char *snd_rawmidi_info_get_id(const snd_rawmidi_info_t *obj); -const char *snd_rawmidi_info_get_name(const snd_rawmidi_info_t *obj); -const char *snd_rawmidi_info_get_subdevice_name(const snd_rawmidi_info_t *obj); -unsigned int snd_rawmidi_info_get_subdevices_count(const snd_rawmidi_info_t *obj); -unsigned int snd_rawmidi_info_get_subdevices_avail(const snd_rawmidi_info_t *obj); -void snd_rawmidi_info_set_device(snd_rawmidi_info_t *obj, unsigned int val); -void snd_rawmidi_info_set_subdevice(snd_rawmidi_info_t *obj, unsigned int val); -void snd_rawmidi_info_set_stream(snd_rawmidi_info_t *obj, snd_rawmidi_stream_t val); -int snd_rawmidi_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info); -size_t snd_rawmidi_params_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_rawmidi_params_t using standard alloca - * \param ptr returned pointer - */ -#define snd_rawmidi_params_alloca(ptr) __snd_alloca(ptr, snd_rawmidi_params) -int snd_rawmidi_params_malloc(snd_rawmidi_params_t **ptr); -void snd_rawmidi_params_free(snd_rawmidi_params_t *obj); -void snd_rawmidi_params_copy(snd_rawmidi_params_t *dst, const snd_rawmidi_params_t *src); -int snd_rawmidi_params_set_buffer_size(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params, size_t val); -size_t snd_rawmidi_params_get_buffer_size(const snd_rawmidi_params_t *params); -int snd_rawmidi_params_set_avail_min(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params, size_t val); -size_t snd_rawmidi_params_get_avail_min(const snd_rawmidi_params_t *params); -int snd_rawmidi_params_set_no_active_sensing(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params, int val); -int snd_rawmidi_params_get_no_active_sensing(const snd_rawmidi_params_t *params); -int snd_rawmidi_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params); -int snd_rawmidi_params_current(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params); -size_t snd_rawmidi_status_sizeof(void); -/** \hideinitializer - * \brief allocate an invalid #snd_rawmidi_status_t using standard alloca - * \param ptr returned pointer - */ -#define snd_rawmidi_status_alloca(ptr) __snd_alloca(ptr, snd_rawmidi_status) -int snd_rawmidi_status_malloc(snd_rawmidi_status_t **ptr); -void snd_rawmidi_status_free(snd_rawmidi_status_t *obj); -void snd_rawmidi_status_copy(snd_rawmidi_status_t *dst, const snd_rawmidi_status_t *src); -void snd_rawmidi_status_get_tstamp(const snd_rawmidi_status_t *obj, snd_htimestamp_t *ptr); -size_t snd_rawmidi_status_get_avail(const snd_rawmidi_status_t *obj); -size_t snd_rawmidi_status_get_xruns(const snd_rawmidi_status_t *obj); -int snd_rawmidi_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * status); -int snd_rawmidi_drain(snd_rawmidi_t *rmidi); -int snd_rawmidi_drop(snd_rawmidi_t *rmidi); -ssize_t snd_rawmidi_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size); -ssize_t snd_rawmidi_read(snd_rawmidi_t *rmidi, void *buffer, size_t size); -const char *snd_rawmidi_name(snd_rawmidi_t *rmidi); -snd_rawmidi_type_t snd_rawmidi_type(snd_rawmidi_t *rmidi); -snd_rawmidi_stream_t snd_rawmidi_stream(snd_rawmidi_t *rawmidi); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __RAWMIDI_H */ - diff --git a/raylib/external/alsa/seq.h b/raylib/external/alsa/seq.h deleted file mode 100644 index d05940e..0000000 --- a/raylib/external/alsa/seq.h +++ /dev/null @@ -1,739 +0,0 @@ -/** - * \file include/seq.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - */ -/* - * Application interface library for the ALSA driver - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_SEQ_H -#define __ALSA_SEQ_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Sequencer MIDI Sequencer - * MIDI Sequencer Interface. - * See \ref seq page for more details. - * \{ - */ - -/** dlsym version for interface entry callback */ -#define SND_SEQ_DLSYM_VERSION _dlsym_seq_001 - -/** Sequencer handle */ -typedef struct _snd_seq snd_seq_t; - -/** - * sequencer opening stream types - */ -#define SND_SEQ_OPEN_OUTPUT 1 /**< open for output (write) */ -#define SND_SEQ_OPEN_INPUT 2 /**< open for input (read) */ -#define SND_SEQ_OPEN_DUPLEX (SND_SEQ_OPEN_OUTPUT|SND_SEQ_OPEN_INPUT) /**< open for both input and output (read/write) */ - -/** - * sequencer opening mode - */ -#define SND_SEQ_NONBLOCK 0x0001 /**< non-blocking mode (flag to open mode) */ - -/** sequencer handle type */ -typedef enum _snd_seq_type { - SND_SEQ_TYPE_HW, /**< hardware */ - SND_SEQ_TYPE_SHM, /**< shared memory (NYI) */ - SND_SEQ_TYPE_INET /**< network (NYI) */ -} snd_seq_type_t; - -/** special client (port) ids */ -#define SND_SEQ_ADDRESS_UNKNOWN 253 /**< unknown source */ -#define SND_SEQ_ADDRESS_SUBSCRIBERS 254 /**< send event to all subscribed ports */ -#define SND_SEQ_ADDRESS_BROADCAST 255 /**< send event to all queues/clients/ports/channels */ - -/** known client numbers */ -#define SND_SEQ_CLIENT_SYSTEM 0 /**< system client */ - -/* - */ -int snd_seq_open(snd_seq_t **handle, const char *name, int streams, int mode); -int snd_seq_open_lconf(snd_seq_t **handle, const char *name, int streams, int mode, snd_config_t *lconf); -const char *snd_seq_name(snd_seq_t *seq); -snd_seq_type_t snd_seq_type(snd_seq_t *seq); -int snd_seq_close(snd_seq_t *handle); -int snd_seq_poll_descriptors_count(snd_seq_t *handle, short events); -int snd_seq_poll_descriptors(snd_seq_t *handle, struct pollfd *pfds, unsigned int space, short events); -int snd_seq_poll_descriptors_revents(snd_seq_t *seq, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -int snd_seq_nonblock(snd_seq_t *handle, int nonblock); -int snd_seq_client_id(snd_seq_t *handle); - -size_t snd_seq_get_output_buffer_size(snd_seq_t *handle); -size_t snd_seq_get_input_buffer_size(snd_seq_t *handle); -int snd_seq_set_output_buffer_size(snd_seq_t *handle, size_t size); -int snd_seq_set_input_buffer_size(snd_seq_t *handle, size_t size); - -/** system information container */ -typedef struct _snd_seq_system_info snd_seq_system_info_t; - -size_t snd_seq_system_info_sizeof(void); -/** allocate a #snd_seq_system_info_t container on stack */ -#define snd_seq_system_info_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_system_info) -int snd_seq_system_info_malloc(snd_seq_system_info_t **ptr); -void snd_seq_system_info_free(snd_seq_system_info_t *ptr); -void snd_seq_system_info_copy(snd_seq_system_info_t *dst, const snd_seq_system_info_t *src); - -int snd_seq_system_info_get_queues(const snd_seq_system_info_t *info); -int snd_seq_system_info_get_clients(const snd_seq_system_info_t *info); -int snd_seq_system_info_get_ports(const snd_seq_system_info_t *info); -int snd_seq_system_info_get_channels(const snd_seq_system_info_t *info); -int snd_seq_system_info_get_cur_clients(const snd_seq_system_info_t *info); -int snd_seq_system_info_get_cur_queues(const snd_seq_system_info_t *info); - -int snd_seq_system_info(snd_seq_t *handle, snd_seq_system_info_t *info); - -/** \} */ - - -/** - * \defgroup SeqClient Sequencer Client Interface - * Sequencer Client Interface - * \ingroup Sequencer - * \{ - */ - -/** client information container */ -typedef struct _snd_seq_client_info snd_seq_client_info_t; - -/** client types */ -typedef enum snd_seq_client_type { - SND_SEQ_USER_CLIENT = 1, /**< user client */ - SND_SEQ_KERNEL_CLIENT = 2 /**< kernel client */ -} snd_seq_client_type_t; - -size_t snd_seq_client_info_sizeof(void); -/** allocate a #snd_seq_client_info_t container on stack */ -#define snd_seq_client_info_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_client_info) -int snd_seq_client_info_malloc(snd_seq_client_info_t **ptr); -void snd_seq_client_info_free(snd_seq_client_info_t *ptr); -void snd_seq_client_info_copy(snd_seq_client_info_t *dst, const snd_seq_client_info_t *src); - -int snd_seq_client_info_get_client(const snd_seq_client_info_t *info); -snd_seq_client_type_t snd_seq_client_info_get_type(const snd_seq_client_info_t *info); -const char *snd_seq_client_info_get_name(snd_seq_client_info_t *info); -int snd_seq_client_info_get_broadcast_filter(const snd_seq_client_info_t *info); -int snd_seq_client_info_get_error_bounce(const snd_seq_client_info_t *info); -int snd_seq_client_info_get_card(const snd_seq_client_info_t *info); -int snd_seq_client_info_get_pid(const snd_seq_client_info_t *info); -const unsigned char *snd_seq_client_info_get_event_filter(const snd_seq_client_info_t *info); -int snd_seq_client_info_get_num_ports(const snd_seq_client_info_t *info); -int snd_seq_client_info_get_event_lost(const snd_seq_client_info_t *info); - -void snd_seq_client_info_set_client(snd_seq_client_info_t *info, int client); -void snd_seq_client_info_set_name(snd_seq_client_info_t *info, const char *name); -void snd_seq_client_info_set_broadcast_filter(snd_seq_client_info_t *info, int val); -void snd_seq_client_info_set_error_bounce(snd_seq_client_info_t *info, int val); -void snd_seq_client_info_set_event_filter(snd_seq_client_info_t *info, unsigned char *filter); - -void snd_seq_client_info_event_filter_clear(snd_seq_client_info_t *info); -void snd_seq_client_info_event_filter_add(snd_seq_client_info_t *info, int event_type); -void snd_seq_client_info_event_filter_del(snd_seq_client_info_t *info, int event_type); -int snd_seq_client_info_event_filter_check(snd_seq_client_info_t *info, int event_type); - -int snd_seq_get_client_info(snd_seq_t *handle, snd_seq_client_info_t *info); -int snd_seq_get_any_client_info(snd_seq_t *handle, int client, snd_seq_client_info_t *info); -int snd_seq_set_client_info(snd_seq_t *handle, snd_seq_client_info_t *info); -int snd_seq_query_next_client(snd_seq_t *handle, snd_seq_client_info_t *info); - -/* - */ - -/** client pool information container */ -typedef struct _snd_seq_client_pool snd_seq_client_pool_t; - -size_t snd_seq_client_pool_sizeof(void); -/** allocate a #snd_seq_client_pool_t container on stack */ -#define snd_seq_client_pool_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_client_pool) -int snd_seq_client_pool_malloc(snd_seq_client_pool_t **ptr); -void snd_seq_client_pool_free(snd_seq_client_pool_t *ptr); -void snd_seq_client_pool_copy(snd_seq_client_pool_t *dst, const snd_seq_client_pool_t *src); - -int snd_seq_client_pool_get_client(const snd_seq_client_pool_t *info); -size_t snd_seq_client_pool_get_output_pool(const snd_seq_client_pool_t *info); -size_t snd_seq_client_pool_get_input_pool(const snd_seq_client_pool_t *info); -size_t snd_seq_client_pool_get_output_room(const snd_seq_client_pool_t *info); -size_t snd_seq_client_pool_get_output_free(const snd_seq_client_pool_t *info); -size_t snd_seq_client_pool_get_input_free(const snd_seq_client_pool_t *info); -void snd_seq_client_pool_set_output_pool(snd_seq_client_pool_t *info, size_t size); -void snd_seq_client_pool_set_input_pool(snd_seq_client_pool_t *info, size_t size); -void snd_seq_client_pool_set_output_room(snd_seq_client_pool_t *info, size_t size); - -int snd_seq_get_client_pool(snd_seq_t *handle, snd_seq_client_pool_t *info); -int snd_seq_set_client_pool(snd_seq_t *handle, snd_seq_client_pool_t *info); - - -/** \} */ - - -/** - * \defgroup SeqPort Sequencer Port Interface - * Sequencer Port Interface - * \ingroup Sequencer - * \{ - */ - -/** port information container */ -typedef struct _snd_seq_port_info snd_seq_port_info_t; - -/** known port numbers */ -#define SND_SEQ_PORT_SYSTEM_TIMER 0 /**< system timer port */ -#define SND_SEQ_PORT_SYSTEM_ANNOUNCE 1 /**< system announce port */ - -/** port capabilities (32 bits) */ -#define SND_SEQ_PORT_CAP_READ (1<<0) /**< readable from this port */ -#define SND_SEQ_PORT_CAP_WRITE (1<<1) /**< writable to this port */ - -#define SND_SEQ_PORT_CAP_SYNC_READ (1<<2) /**< allow read subscriptions */ -#define SND_SEQ_PORT_CAP_SYNC_WRITE (1<<3) /**< allow write subscriptions */ - -#define SND_SEQ_PORT_CAP_DUPLEX (1<<4) /**< allow read/write duplex */ - -#define SND_SEQ_PORT_CAP_SUBS_READ (1<<5) /**< allow read subscription */ -#define SND_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /**< allow write subscription */ -#define SND_SEQ_PORT_CAP_NO_EXPORT (1<<7) /**< routing not allowed */ - -/* port type */ -/** Messages sent from/to this port have device-specific semantics. */ -#define SND_SEQ_PORT_TYPE_SPECIFIC (1<<0) -/** This port understands MIDI messages. */ -#define SND_SEQ_PORT_TYPE_MIDI_GENERIC (1<<1) -/** This port is compatible with the General MIDI specification. */ -#define SND_SEQ_PORT_TYPE_MIDI_GM (1<<2) -/** This port is compatible with the Roland GS standard. */ -#define SND_SEQ_PORT_TYPE_MIDI_GS (1<<3) -/** This port is compatible with the Yamaha XG specification. */ -#define SND_SEQ_PORT_TYPE_MIDI_XG (1<<4) -/** This port is compatible with the Roland MT-32. */ -#define SND_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) -/** This port is compatible with the General MIDI 2 specification. */ -#define SND_SEQ_PORT_TYPE_MIDI_GM2 (1<<6) -/** This port understands SND_SEQ_EVENT_SAMPLE_xxx messages - (these are not MIDI messages). */ -#define SND_SEQ_PORT_TYPE_SYNTH (1<<10) -/** Instruments can be downloaded to this port - (with SND_SEQ_EVENT_INSTR_xxx messages sent directly). */ -#define SND_SEQ_PORT_TYPE_DIRECT_SAMPLE (1<<11) -/** Instruments can be downloaded to this port - (with SND_SEQ_EVENT_INSTR_xxx messages sent directly or through a queue). */ -#define SND_SEQ_PORT_TYPE_SAMPLE (1<<12) -/** This port is implemented in hardware. */ -#define SND_SEQ_PORT_TYPE_HARDWARE (1<<16) -/** This port is implemented in software. */ -#define SND_SEQ_PORT_TYPE_SOFTWARE (1<<17) -/** Messages sent to this port will generate sounds. */ -#define SND_SEQ_PORT_TYPE_SYNTHESIZER (1<<18) -/** This port may connect to other devices - (whose characteristics are not known). */ -#define SND_SEQ_PORT_TYPE_PORT (1<<19) -/** This port belongs to an application, such as a sequencer or editor. */ -#define SND_SEQ_PORT_TYPE_APPLICATION (1<<20) - - -size_t snd_seq_port_info_sizeof(void); -/** allocate a #snd_seq_port_info_t container on stack */ -#define snd_seq_port_info_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_port_info) -int snd_seq_port_info_malloc(snd_seq_port_info_t **ptr); -void snd_seq_port_info_free(snd_seq_port_info_t *ptr); -void snd_seq_port_info_copy(snd_seq_port_info_t *dst, const snd_seq_port_info_t *src); - -int snd_seq_port_info_get_client(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_port(const snd_seq_port_info_t *info); -const snd_seq_addr_t *snd_seq_port_info_get_addr(const snd_seq_port_info_t *info); -const char *snd_seq_port_info_get_name(const snd_seq_port_info_t *info); -unsigned int snd_seq_port_info_get_capability(const snd_seq_port_info_t *info); -unsigned int snd_seq_port_info_get_type(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_midi_channels(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_midi_voices(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_synth_voices(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_read_use(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_write_use(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_port_specified(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_timestamping(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_timestamp_real(const snd_seq_port_info_t *info); -int snd_seq_port_info_get_timestamp_queue(const snd_seq_port_info_t *info); - -void snd_seq_port_info_set_client(snd_seq_port_info_t *info, int client); -void snd_seq_port_info_set_port(snd_seq_port_info_t *info, int port); -void snd_seq_port_info_set_addr(snd_seq_port_info_t *info, const snd_seq_addr_t *addr); -void snd_seq_port_info_set_name(snd_seq_port_info_t *info, const char *name); -void snd_seq_port_info_set_capability(snd_seq_port_info_t *info, unsigned int capability); -void snd_seq_port_info_set_type(snd_seq_port_info_t *info, unsigned int type); -void snd_seq_port_info_set_midi_channels(snd_seq_port_info_t *info, int channels); -void snd_seq_port_info_set_midi_voices(snd_seq_port_info_t *info, int voices); -void snd_seq_port_info_set_synth_voices(snd_seq_port_info_t *info, int voices); -void snd_seq_port_info_set_port_specified(snd_seq_port_info_t *info, int val); -void snd_seq_port_info_set_timestamping(snd_seq_port_info_t *info, int enable); -void snd_seq_port_info_set_timestamp_real(snd_seq_port_info_t *info, int realtime); -void snd_seq_port_info_set_timestamp_queue(snd_seq_port_info_t *info, int queue); - -int snd_seq_create_port(snd_seq_t *handle, snd_seq_port_info_t *info); -int snd_seq_delete_port(snd_seq_t *handle, int port); -int snd_seq_get_port_info(snd_seq_t *handle, int port, snd_seq_port_info_t *info); -int snd_seq_get_any_port_info(snd_seq_t *handle, int client, int port, snd_seq_port_info_t *info); -int snd_seq_set_port_info(snd_seq_t *handle, int port, snd_seq_port_info_t *info); -int snd_seq_query_next_port(snd_seq_t *handle, snd_seq_port_info_t *info); - -/** \} */ - - -/** - * \defgroup SeqSubscribe Sequencer Port Subscription - * Sequencer Port Subscription - * \ingroup Sequencer - * \{ - */ - -/** port subscription container */ -typedef struct _snd_seq_port_subscribe snd_seq_port_subscribe_t; - -size_t snd_seq_port_subscribe_sizeof(void); -/** allocate a #snd_seq_port_subscribe_t container on stack */ -#define snd_seq_port_subscribe_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_port_subscribe) -int snd_seq_port_subscribe_malloc(snd_seq_port_subscribe_t **ptr); -void snd_seq_port_subscribe_free(snd_seq_port_subscribe_t *ptr); -void snd_seq_port_subscribe_copy(snd_seq_port_subscribe_t *dst, const snd_seq_port_subscribe_t *src); - -const snd_seq_addr_t *snd_seq_port_subscribe_get_sender(const snd_seq_port_subscribe_t *info); -const snd_seq_addr_t *snd_seq_port_subscribe_get_dest(const snd_seq_port_subscribe_t *info); -int snd_seq_port_subscribe_get_queue(const snd_seq_port_subscribe_t *info); -int snd_seq_port_subscribe_get_exclusive(const snd_seq_port_subscribe_t *info); -int snd_seq_port_subscribe_get_time_update(const snd_seq_port_subscribe_t *info); -int snd_seq_port_subscribe_get_time_real(const snd_seq_port_subscribe_t *info); - -void snd_seq_port_subscribe_set_sender(snd_seq_port_subscribe_t *info, const snd_seq_addr_t *addr); -void snd_seq_port_subscribe_set_dest(snd_seq_port_subscribe_t *info, const snd_seq_addr_t *addr); -void snd_seq_port_subscribe_set_queue(snd_seq_port_subscribe_t *info, int q); -void snd_seq_port_subscribe_set_exclusive(snd_seq_port_subscribe_t *info, int val); -void snd_seq_port_subscribe_set_time_update(snd_seq_port_subscribe_t *info, int val); -void snd_seq_port_subscribe_set_time_real(snd_seq_port_subscribe_t *info, int val); - -int snd_seq_get_port_subscription(snd_seq_t *handle, snd_seq_port_subscribe_t *sub); -int snd_seq_subscribe_port(snd_seq_t *handle, snd_seq_port_subscribe_t *sub); -int snd_seq_unsubscribe_port(snd_seq_t *handle, snd_seq_port_subscribe_t *sub); - -/* - */ - -/** subscription query container */ -typedef struct _snd_seq_query_subscribe snd_seq_query_subscribe_t; - -/** type of query subscription */ -typedef enum { - SND_SEQ_QUERY_SUBS_READ, /**< query read subscriptions */ - SND_SEQ_QUERY_SUBS_WRITE /**< query write subscriptions */ -} snd_seq_query_subs_type_t; - -size_t snd_seq_query_subscribe_sizeof(void); -/** allocate a #snd_seq_query_subscribe_t container on stack */ -#define snd_seq_query_subscribe_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_query_subscribe) -int snd_seq_query_subscribe_malloc(snd_seq_query_subscribe_t **ptr); -void snd_seq_query_subscribe_free(snd_seq_query_subscribe_t *ptr); -void snd_seq_query_subscribe_copy(snd_seq_query_subscribe_t *dst, const snd_seq_query_subscribe_t *src); - -int snd_seq_query_subscribe_get_client(const snd_seq_query_subscribe_t *info); -int snd_seq_query_subscribe_get_port(const snd_seq_query_subscribe_t *info); -const snd_seq_addr_t *snd_seq_query_subscribe_get_root(const snd_seq_query_subscribe_t *info); -snd_seq_query_subs_type_t snd_seq_query_subscribe_get_type(const snd_seq_query_subscribe_t *info); -int snd_seq_query_subscribe_get_index(const snd_seq_query_subscribe_t *info); -int snd_seq_query_subscribe_get_num_subs(const snd_seq_query_subscribe_t *info); -const snd_seq_addr_t *snd_seq_query_subscribe_get_addr(const snd_seq_query_subscribe_t *info); -int snd_seq_query_subscribe_get_queue(const snd_seq_query_subscribe_t *info); -int snd_seq_query_subscribe_get_exclusive(const snd_seq_query_subscribe_t *info); -int snd_seq_query_subscribe_get_time_update(const snd_seq_query_subscribe_t *info); -int snd_seq_query_subscribe_get_time_real(const snd_seq_query_subscribe_t *info); - -void snd_seq_query_subscribe_set_client(snd_seq_query_subscribe_t *info, int client); -void snd_seq_query_subscribe_set_port(snd_seq_query_subscribe_t *info, int port); -void snd_seq_query_subscribe_set_root(snd_seq_query_subscribe_t *info, const snd_seq_addr_t *addr); -void snd_seq_query_subscribe_set_type(snd_seq_query_subscribe_t *info, snd_seq_query_subs_type_t type); -void snd_seq_query_subscribe_set_index(snd_seq_query_subscribe_t *info, int _index); - -int snd_seq_query_port_subscribers(snd_seq_t *seq, snd_seq_query_subscribe_t * subs); - -/** \} */ - - -/** - * \defgroup SeqQueue Sequencer Queue Interface - * Sequencer Queue Interface - * \ingroup Sequencer - * \{ - */ - -/** queue information container */ -typedef struct _snd_seq_queue_info snd_seq_queue_info_t; -/** queue status container */ -typedef struct _snd_seq_queue_status snd_seq_queue_status_t; -/** queue tempo container */ -typedef struct _snd_seq_queue_tempo snd_seq_queue_tempo_t; -/** queue timer information container */ -typedef struct _snd_seq_queue_timer snd_seq_queue_timer_t; - -/** special queue ids */ -#define SND_SEQ_QUEUE_DIRECT 253 /**< direct dispatch */ - -size_t snd_seq_queue_info_sizeof(void); -/** allocate a #snd_seq_queue_info_t container on stack */ -#define snd_seq_queue_info_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_queue_info) -int snd_seq_queue_info_malloc(snd_seq_queue_info_t **ptr); -void snd_seq_queue_info_free(snd_seq_queue_info_t *ptr); -void snd_seq_queue_info_copy(snd_seq_queue_info_t *dst, const snd_seq_queue_info_t *src); - -int snd_seq_queue_info_get_queue(const snd_seq_queue_info_t *info); -const char *snd_seq_queue_info_get_name(const snd_seq_queue_info_t *info); -int snd_seq_queue_info_get_owner(const snd_seq_queue_info_t *info); -int snd_seq_queue_info_get_locked(const snd_seq_queue_info_t *info); -unsigned int snd_seq_queue_info_get_flags(const snd_seq_queue_info_t *info); - -void snd_seq_queue_info_set_name(snd_seq_queue_info_t *info, const char *name); -void snd_seq_queue_info_set_owner(snd_seq_queue_info_t *info, int owner); -void snd_seq_queue_info_set_locked(snd_seq_queue_info_t *info, int locked); -void snd_seq_queue_info_set_flags(snd_seq_queue_info_t *info, unsigned int flags); - -int snd_seq_create_queue(snd_seq_t *seq, snd_seq_queue_info_t *info); -int snd_seq_alloc_named_queue(snd_seq_t *seq, const char *name); -int snd_seq_alloc_queue(snd_seq_t *handle); -int snd_seq_free_queue(snd_seq_t *handle, int q); -int snd_seq_get_queue_info(snd_seq_t *seq, int q, snd_seq_queue_info_t *info); -int snd_seq_set_queue_info(snd_seq_t *seq, int q, snd_seq_queue_info_t *info); -int snd_seq_query_named_queue(snd_seq_t *seq, const char *name); - -int snd_seq_get_queue_usage(snd_seq_t *handle, int q); -int snd_seq_set_queue_usage(snd_seq_t *handle, int q, int used); - -/* - */ -size_t snd_seq_queue_status_sizeof(void); -/** allocate a #snd_seq_queue_status_t container on stack */ -#define snd_seq_queue_status_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_queue_status) -int snd_seq_queue_status_malloc(snd_seq_queue_status_t **ptr); -void snd_seq_queue_status_free(snd_seq_queue_status_t *ptr); -void snd_seq_queue_status_copy(snd_seq_queue_status_t *dst, const snd_seq_queue_status_t *src); - -int snd_seq_queue_status_get_queue(const snd_seq_queue_status_t *info); -int snd_seq_queue_status_get_events(const snd_seq_queue_status_t *info); -snd_seq_tick_time_t snd_seq_queue_status_get_tick_time(const snd_seq_queue_status_t *info); -const snd_seq_real_time_t *snd_seq_queue_status_get_real_time(const snd_seq_queue_status_t *info); -unsigned int snd_seq_queue_status_get_status(const snd_seq_queue_status_t *info); - -int snd_seq_get_queue_status(snd_seq_t *handle, int q, snd_seq_queue_status_t *status); - -/* - */ -size_t snd_seq_queue_tempo_sizeof(void); -/** allocate a #snd_seq_queue_tempo_t container on stack */ -#define snd_seq_queue_tempo_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_queue_tempo) -int snd_seq_queue_tempo_malloc(snd_seq_queue_tempo_t **ptr); -void snd_seq_queue_tempo_free(snd_seq_queue_tempo_t *ptr); -void snd_seq_queue_tempo_copy(snd_seq_queue_tempo_t *dst, const snd_seq_queue_tempo_t *src); - -int snd_seq_queue_tempo_get_queue(const snd_seq_queue_tempo_t *info); -unsigned int snd_seq_queue_tempo_get_tempo(const snd_seq_queue_tempo_t *info); -int snd_seq_queue_tempo_get_ppq(const snd_seq_queue_tempo_t *info); -unsigned int snd_seq_queue_tempo_get_skew(const snd_seq_queue_tempo_t *info); -unsigned int snd_seq_queue_tempo_get_skew_base(const snd_seq_queue_tempo_t *info); -void snd_seq_queue_tempo_set_tempo(snd_seq_queue_tempo_t *info, unsigned int tempo); -void snd_seq_queue_tempo_set_ppq(snd_seq_queue_tempo_t *info, int ppq); -void snd_seq_queue_tempo_set_skew(snd_seq_queue_tempo_t *info, unsigned int skew); -void snd_seq_queue_tempo_set_skew_base(snd_seq_queue_tempo_t *info, unsigned int base); - -int snd_seq_get_queue_tempo(snd_seq_t *handle, int q, snd_seq_queue_tempo_t *tempo); -int snd_seq_set_queue_tempo(snd_seq_t *handle, int q, snd_seq_queue_tempo_t *tempo); - -/* - */ - -/** sequencer timer sources */ -typedef enum { - SND_SEQ_TIMER_ALSA = 0, /* ALSA timer */ - SND_SEQ_TIMER_MIDI_CLOCK = 1, /* Midi Clock (CLOCK event) */ - SND_SEQ_TIMER_MIDI_TICK = 2 /* Midi Timer Tick (TICK event */ -} snd_seq_queue_timer_type_t; - -size_t snd_seq_queue_timer_sizeof(void); -/** allocate a #snd_seq_queue_timer_t container on stack */ -#define snd_seq_queue_timer_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_queue_timer) -int snd_seq_queue_timer_malloc(snd_seq_queue_timer_t **ptr); -void snd_seq_queue_timer_free(snd_seq_queue_timer_t *ptr); -void snd_seq_queue_timer_copy(snd_seq_queue_timer_t *dst, const snd_seq_queue_timer_t *src); - -int snd_seq_queue_timer_get_queue(const snd_seq_queue_timer_t *info); -snd_seq_queue_timer_type_t snd_seq_queue_timer_get_type(const snd_seq_queue_timer_t *info); -const snd_timer_id_t *snd_seq_queue_timer_get_id(const snd_seq_queue_timer_t *info); -unsigned int snd_seq_queue_timer_get_resolution(const snd_seq_queue_timer_t *info); - -void snd_seq_queue_timer_set_type(snd_seq_queue_timer_t *info, snd_seq_queue_timer_type_t type); -void snd_seq_queue_timer_set_id(snd_seq_queue_timer_t *info, const snd_timer_id_t *id); -void snd_seq_queue_timer_set_resolution(snd_seq_queue_timer_t *info, unsigned int resolution); - -int snd_seq_get_queue_timer(snd_seq_t *handle, int q, snd_seq_queue_timer_t *timer); -int snd_seq_set_queue_timer(snd_seq_t *handle, int q, snd_seq_queue_timer_t *timer); - -/** \} */ - -/** - * \defgroup SeqEvent Sequencer Event API - * Sequencer Event API - * \ingroup Sequencer - * \{ - */ - -int snd_seq_free_event(snd_seq_event_t *ev); -ssize_t snd_seq_event_length(snd_seq_event_t *ev); -int snd_seq_event_output(snd_seq_t *handle, snd_seq_event_t *ev); -int snd_seq_event_output_buffer(snd_seq_t *handle, snd_seq_event_t *ev); -int snd_seq_event_output_direct(snd_seq_t *handle, snd_seq_event_t *ev); -int snd_seq_event_input(snd_seq_t *handle, snd_seq_event_t **ev); -int snd_seq_event_input_pending(snd_seq_t *seq, int fetch_sequencer); -int snd_seq_drain_output(snd_seq_t *handle); -int snd_seq_event_output_pending(snd_seq_t *seq); -int snd_seq_extract_output(snd_seq_t *handle, snd_seq_event_t **ev); -int snd_seq_drop_output(snd_seq_t *handle); -int snd_seq_drop_output_buffer(snd_seq_t *handle); -int snd_seq_drop_input(snd_seq_t *handle); -int snd_seq_drop_input_buffer(snd_seq_t *handle); - -/** event removal conditionals */ -typedef struct _snd_seq_remove_events snd_seq_remove_events_t; - -/** Remove conditional flags */ -#define SND_SEQ_REMOVE_INPUT (1<<0) /**< Flush input queues */ -#define SND_SEQ_REMOVE_OUTPUT (1<<1) /**< Flush output queues */ -#define SND_SEQ_REMOVE_DEST (1<<2) /**< Restrict by destination q:client:port */ -#define SND_SEQ_REMOVE_DEST_CHANNEL (1<<3) /**< Restrict by channel */ -#define SND_SEQ_REMOVE_TIME_BEFORE (1<<4) /**< Restrict to before time */ -#define SND_SEQ_REMOVE_TIME_AFTER (1<<5) /**< Restrict to time or after */ -#define SND_SEQ_REMOVE_TIME_TICK (1<<6) /**< Time is in ticks */ -#define SND_SEQ_REMOVE_EVENT_TYPE (1<<7) /**< Restrict to event type */ -#define SND_SEQ_REMOVE_IGNORE_OFF (1<<8) /**< Do not flush off events */ -#define SND_SEQ_REMOVE_TAG_MATCH (1<<9) /**< Restrict to events with given tag */ - -size_t snd_seq_remove_events_sizeof(void); -/** allocate a #snd_seq_remove_events_t container on stack */ -#define snd_seq_remove_events_alloca(ptr) \ - __snd_alloca(ptr, snd_seq_remove_events) -int snd_seq_remove_events_malloc(snd_seq_remove_events_t **ptr); -void snd_seq_remove_events_free(snd_seq_remove_events_t *ptr); -void snd_seq_remove_events_copy(snd_seq_remove_events_t *dst, const snd_seq_remove_events_t *src); - -unsigned int snd_seq_remove_events_get_condition(const snd_seq_remove_events_t *info); -int snd_seq_remove_events_get_queue(const snd_seq_remove_events_t *info); -const snd_seq_timestamp_t *snd_seq_remove_events_get_time(const snd_seq_remove_events_t *info); -const snd_seq_addr_t *snd_seq_remove_events_get_dest(const snd_seq_remove_events_t *info); -int snd_seq_remove_events_get_channel(const snd_seq_remove_events_t *info); -int snd_seq_remove_events_get_event_type(const snd_seq_remove_events_t *info); -int snd_seq_remove_events_get_tag(const snd_seq_remove_events_t *info); - -void snd_seq_remove_events_set_condition(snd_seq_remove_events_t *info, unsigned int flags); -void snd_seq_remove_events_set_queue(snd_seq_remove_events_t *info, int queue); -void snd_seq_remove_events_set_time(snd_seq_remove_events_t *info, const snd_seq_timestamp_t *time); -void snd_seq_remove_events_set_dest(snd_seq_remove_events_t *info, const snd_seq_addr_t *addr); -void snd_seq_remove_events_set_channel(snd_seq_remove_events_t *info, int channel); -void snd_seq_remove_events_set_event_type(snd_seq_remove_events_t *info, int type); -void snd_seq_remove_events_set_tag(snd_seq_remove_events_t *info, int tag); - -int snd_seq_remove_events(snd_seq_t *handle, snd_seq_remove_events_t *info); - -/** \} */ - -/** - * \defgroup SeqMisc Sequencer Miscellaneous - * Sequencer Miscellaneous - * \ingroup Sequencer - * \{ - */ - -void snd_seq_set_bit(int nr, void *array); -void snd_seq_unset_bit(int nr, void *array); -int snd_seq_change_bit(int nr, void *array); -int snd_seq_get_bit(int nr, void *array); - -/** \} */ - - -/** - * \defgroup SeqEvType Sequencer Event Type Checks - * Sequencer Event Type Checks - * \ingroup Sequencer - * \{ - */ - -/* event type macros */ -enum { - SND_SEQ_EVFLG_RESULT, - SND_SEQ_EVFLG_NOTE, - SND_SEQ_EVFLG_CONTROL, - SND_SEQ_EVFLG_QUEUE, - SND_SEQ_EVFLG_SYSTEM, - SND_SEQ_EVFLG_MESSAGE, - SND_SEQ_EVFLG_CONNECTION, - SND_SEQ_EVFLG_SAMPLE, - SND_SEQ_EVFLG_USERS, - SND_SEQ_EVFLG_INSTR, - SND_SEQ_EVFLG_QUOTE, - SND_SEQ_EVFLG_NONE, - SND_SEQ_EVFLG_RAW, - SND_SEQ_EVFLG_FIXED, - SND_SEQ_EVFLG_VARIABLE, - SND_SEQ_EVFLG_VARUSR -}; - -enum { - SND_SEQ_EVFLG_NOTE_ONEARG, - SND_SEQ_EVFLG_NOTE_TWOARG -}; - -enum { - SND_SEQ_EVFLG_QUEUE_NOARG, - SND_SEQ_EVFLG_QUEUE_TICK, - SND_SEQ_EVFLG_QUEUE_TIME, - SND_SEQ_EVFLG_QUEUE_VALUE -}; - -/** - * Exported event type table - * - * This table is referred by snd_seq_ev_is_xxx. - */ -extern const unsigned int snd_seq_event_types[]; - -#define _SND_SEQ_TYPE(x) (1<<(x)) /**< master type - 24bit */ -#define _SND_SEQ_TYPE_OPT(x) ((x)<<24) /**< optional type - 8bit */ - -/** check the event type */ -#define snd_seq_type_check(ev,x) (snd_seq_event_types[(ev)->type] & _SND_SEQ_TYPE(x)) - -/** event type check: result events */ -#define snd_seq_ev_is_result_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_RESULT) -/** event type check: note events */ -#define snd_seq_ev_is_note_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_NOTE) -/** event type check: control events */ -#define snd_seq_ev_is_control_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_CONTROL) -/** event type check: channel specific events */ -#define snd_seq_ev_is_channel_type(ev) \ - (snd_seq_event_types[(ev)->type] & (_SND_SEQ_TYPE(SND_SEQ_EVFLG_NOTE) | _SND_SEQ_TYPE(SND_SEQ_EVFLG_CONTROL))) - -/** event type check: queue control events */ -#define snd_seq_ev_is_queue_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_QUEUE) -/** event type check: system status messages */ -#define snd_seq_ev_is_message_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_MESSAGE) -/** event type check: system status messages */ -#define snd_seq_ev_is_subscribe_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_CONNECTION) -/** event type check: sample messages */ -#define snd_seq_ev_is_sample_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_SAMPLE) -/** event type check: user-defined messages */ -#define snd_seq_ev_is_user_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_USERS) -/** event type check: instrument layer events */ -#define snd_seq_ev_is_instr_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_INSTR) -/** event type check: fixed length events */ -#define snd_seq_ev_is_fixed_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_FIXED) -/** event type check: variable length events */ -#define snd_seq_ev_is_variable_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_VARIABLE) -/** event type check: user pointer events */ -#define snd_seq_ev_is_varusr_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_VARUSR) -/** event type check: reserved for kernel */ -#define snd_seq_ev_is_reserved(ev) \ - (! snd_seq_event_types[(ev)->type]) - -/** - * macros to check event flags - */ -/** prior events */ -#define snd_seq_ev_is_prior(ev) \ - (((ev)->flags & SND_SEQ_PRIORITY_MASK) == SND_SEQ_PRIORITY_HIGH) - -/** get the data length type */ -#define snd_seq_ev_length_type(ev) \ - ((ev)->flags & SND_SEQ_EVENT_LENGTH_MASK) -/** fixed length events */ -#define snd_seq_ev_is_fixed(ev) \ - (snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_FIXED) -/** variable length events */ -#define snd_seq_ev_is_variable(ev) \ - (snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_VARIABLE) -/** variable length on user-space */ -#define snd_seq_ev_is_varusr(ev) \ - (snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_VARUSR) - -/** time-stamp type */ -#define snd_seq_ev_timestamp_type(ev) \ - ((ev)->flags & SND_SEQ_TIME_STAMP_MASK) -/** event is in tick time */ -#define snd_seq_ev_is_tick(ev) \ - (snd_seq_ev_timestamp_type(ev) == SND_SEQ_TIME_STAMP_TICK) -/** event is in real-time */ -#define snd_seq_ev_is_real(ev) \ - (snd_seq_ev_timestamp_type(ev) == SND_SEQ_TIME_STAMP_REAL) - -/** time-mode type */ -#define snd_seq_ev_timemode_type(ev) \ - ((ev)->flags & SND_SEQ_TIME_MODE_MASK) -/** scheduled in absolute time */ -#define snd_seq_ev_is_abstime(ev) \ - (snd_seq_ev_timemode_type(ev) == SND_SEQ_TIME_MODE_ABS) -/** scheduled in relative time */ -#define snd_seq_ev_is_reltime(ev) \ - (snd_seq_ev_timemode_type(ev) == SND_SEQ_TIME_MODE_REL) - -/** direct dispatched events */ -#define snd_seq_ev_is_direct(ev) \ - ((ev)->queue == SND_SEQ_QUEUE_DIRECT) - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_SEQ_H */ - diff --git a/raylib/external/alsa/seq_event.h b/raylib/external/alsa/seq_event.h deleted file mode 100644 index 6bd0de3..0000000 --- a/raylib/external/alsa/seq_event.h +++ /dev/null @@ -1,325 +0,0 @@ -/** - * \file include/seq_event.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_SEQ_EVENT_H -#define __ALSA_SEQ_EVENT_H - -/** - * \defgroup SeqEvents Sequencer Event Definitions - * Sequencer Event Definitions - * \ingroup Sequencer - * \{ - */ - -/** - * Sequencer event data type - */ -typedef unsigned char snd_seq_event_type_t; - -/** Sequencer event type */ -enum snd_seq_event_type { - /** system status; event data type = #snd_seq_result_t */ - SND_SEQ_EVENT_SYSTEM = 0, - /** returned result status; event data type = #snd_seq_result_t */ - SND_SEQ_EVENT_RESULT, - - /** note on and off with duration; event data type = #snd_seq_ev_note_t */ - SND_SEQ_EVENT_NOTE = 5, - /** note on; event data type = #snd_seq_ev_note_t */ - SND_SEQ_EVENT_NOTEON, - /** note off; event data type = #snd_seq_ev_note_t */ - SND_SEQ_EVENT_NOTEOFF, - /** key pressure change (aftertouch); event data type = #snd_seq_ev_note_t */ - SND_SEQ_EVENT_KEYPRESS, - - /** controller; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_CONTROLLER = 10, - /** program change; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_PGMCHANGE, - /** channel pressure; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_CHANPRESS, - /** pitchwheel; event data type = #snd_seq_ev_ctrl_t; data is from -8192 to 8191) */ - SND_SEQ_EVENT_PITCHBEND, - /** 14 bit controller value; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_CONTROL14, - /** 14 bit NRPN; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_NONREGPARAM, - /** 14 bit RPN; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_REGPARAM, - - /** SPP with LSB and MSB values; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_SONGPOS = 20, - /** Song Select with song ID number; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_SONGSEL, - /** midi time code quarter frame; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_QFRAME, - /** SMF Time Signature event; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_TIMESIGN, - /** SMF Key Signature event; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_KEYSIGN, - - /** MIDI Real Time Start message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_START = 30, - /** MIDI Real Time Continue message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_CONTINUE, - /** MIDI Real Time Stop message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_STOP, - /** Set tick queue position; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_SETPOS_TICK, - /** Set real-time queue position; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_SETPOS_TIME, - /** (SMF) Tempo event; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_TEMPO, - /** MIDI Real Time Clock message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_CLOCK, - /** MIDI Real Time Tick message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_TICK, - /** Queue timer skew; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_QUEUE_SKEW, - /** Sync position changed; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_SYNC_POS, - - /** Tune request; event data type = none */ - SND_SEQ_EVENT_TUNE_REQUEST = 40, - /** Reset to power-on state; event data type = none */ - SND_SEQ_EVENT_RESET, - /** Active sensing event; event data type = none */ - SND_SEQ_EVENT_SENSING, - - /** Echo-back event; event data type = any type */ - SND_SEQ_EVENT_ECHO = 50, - /** OSS emulation raw event; event data type = any type */ - SND_SEQ_EVENT_OSS, - - /** New client has connected; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_CLIENT_START = 60, - /** Client has left the system; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_CLIENT_EXIT, - /** Client status/info has changed; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_CLIENT_CHANGE, - /** New port was created; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_PORT_START, - /** Port was deleted from system; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_PORT_EXIT, - /** Port status/info has changed; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_PORT_CHANGE, - - /** Ports connected; event data type = #snd_seq_connect_t */ - SND_SEQ_EVENT_PORT_SUBSCRIBED, - /** Ports disconnected; event data type = #snd_seq_connect_t */ - SND_SEQ_EVENT_PORT_UNSUBSCRIBED, - - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR0 = 90, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR1, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR2, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR3, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR4, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR5, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR6, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR7, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR8, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR9, - - /** system exclusive data (variable length); event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_SYSEX = 130, - /** error event; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_BOUNCE, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR0 = 135, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR1, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR2, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR3, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR4, - - /** NOP; ignored in any case */ - SND_SEQ_EVENT_NONE = 255 -}; - - -/** Sequencer event address */ -typedef struct snd_seq_addr { - unsigned char client; /**< Client id */ - unsigned char port; /**< Port id */ -} snd_seq_addr_t; - -/** Connection (subscription) between ports */ -typedef struct snd_seq_connect { - snd_seq_addr_t sender; /**< sender address */ - snd_seq_addr_t dest; /**< destination address */ -} snd_seq_connect_t; - - -/** Real-time data record */ -typedef struct snd_seq_real_time { - unsigned int tv_sec; /**< seconds */ - unsigned int tv_nsec; /**< nanoseconds */ -} snd_seq_real_time_t; - -/** (MIDI) Tick-time data record */ -typedef unsigned int snd_seq_tick_time_t; - -/** unioned time stamp */ -typedef union snd_seq_timestamp { - snd_seq_tick_time_t tick; /**< tick-time */ - struct snd_seq_real_time time; /**< real-time */ -} snd_seq_timestamp_t; - - -/** - * Event mode flags - * - * NOTE: only 8 bits available! - */ -#define SND_SEQ_TIME_STAMP_TICK (0<<0) /**< timestamp in clock ticks */ -#define SND_SEQ_TIME_STAMP_REAL (1<<0) /**< timestamp in real time */ -#define SND_SEQ_TIME_STAMP_MASK (1<<0) /**< mask for timestamp bits */ - -#define SND_SEQ_TIME_MODE_ABS (0<<1) /**< absolute timestamp */ -#define SND_SEQ_TIME_MODE_REL (1<<1) /**< relative to current time */ -#define SND_SEQ_TIME_MODE_MASK (1<<1) /**< mask for time mode bits */ - -#define SND_SEQ_EVENT_LENGTH_FIXED (0<<2) /**< fixed event size */ -#define SND_SEQ_EVENT_LENGTH_VARIABLE (1<<2) /**< variable event size */ -#define SND_SEQ_EVENT_LENGTH_VARUSR (2<<2) /**< variable event size - user memory space */ -#define SND_SEQ_EVENT_LENGTH_MASK (3<<2) /**< mask for event length bits */ - -#define SND_SEQ_PRIORITY_NORMAL (0<<4) /**< normal priority */ -#define SND_SEQ_PRIORITY_HIGH (1<<4) /**< event should be processed before others */ -#define SND_SEQ_PRIORITY_MASK (1<<4) /**< mask for priority bits */ - - -/** Note event */ -typedef struct snd_seq_ev_note { - unsigned char channel; /**< channel number */ - unsigned char note; /**< note */ - unsigned char velocity; /**< velocity */ - unsigned char off_velocity; /**< note-off velocity; only for #SND_SEQ_EVENT_NOTE */ - unsigned int duration; /**< duration until note-off; only for #SND_SEQ_EVENT_NOTE */ -} snd_seq_ev_note_t; - -/** Controller event */ -typedef struct snd_seq_ev_ctrl { - unsigned char channel; /**< channel number */ - unsigned char unused[3]; /**< reserved */ - unsigned int param; /**< control parameter */ - signed int value; /**< control value */ -} snd_seq_ev_ctrl_t; - -/** generic set of bytes (12x8 bit) */ -typedef struct snd_seq_ev_raw8 { - unsigned char d[12]; /**< 8 bit value */ -} snd_seq_ev_raw8_t; - -/** generic set of integers (3x32 bit) */ -typedef struct snd_seq_ev_raw32 { - unsigned int d[3]; /**< 32 bit value */ -} snd_seq_ev_raw32_t; - -/** external stored data */ -struct snd_seq_ev_ext { - unsigned int len; /**< length of data */ - void *ptr; /**< pointer to data (note: can be 64-bit) */ -} __attribute__((packed)); -/** external stored data */ -typedef struct snd_seq_ev_ext snd_seq_ev_ext_t; -#ifdef DOC_HIDDEN -/* redefine typedef for stupid doxygen */ -typedef snd_seq_ev_ext snd_seq_ev_ext_t; -#endif - -/** Result events */ -typedef struct snd_seq_result { - int event; /**< processed event type */ - int result; /**< status */ -} snd_seq_result_t; - -/** Queue skew values */ -typedef struct snd_seq_queue_skew { - unsigned int value; /**< skew value */ - unsigned int base; /**< skew base */ -} snd_seq_queue_skew_t; - -/** queue timer control */ -typedef struct snd_seq_ev_queue_control { - unsigned char queue; /**< affected queue */ - unsigned char unused[3]; /**< reserved */ - union { - signed int value; /**< affected value (e.g. tempo) */ - snd_seq_timestamp_t time; /**< time */ - unsigned int position; /**< sync position */ - snd_seq_queue_skew_t skew; /**< queue skew */ - unsigned int d32[2]; /**< any data */ - unsigned char d8[8]; /**< any data */ - } param; /**< data value union */ -} snd_seq_ev_queue_control_t; - - -/** Sequencer event */ -typedef struct snd_seq_event { - snd_seq_event_type_t type; /**< event type */ - unsigned char flags; /**< event flags */ - unsigned char tag; /**< tag */ - - unsigned char queue; /**< schedule queue */ - snd_seq_timestamp_t time; /**< schedule time */ - - snd_seq_addr_t source; /**< source address */ - snd_seq_addr_t dest; /**< destination address */ - - union { - snd_seq_ev_note_t note; /**< note information */ - snd_seq_ev_ctrl_t control; /**< MIDI control information */ - snd_seq_ev_raw8_t raw8; /**< raw8 data */ - snd_seq_ev_raw32_t raw32; /**< raw32 data */ - snd_seq_ev_ext_t ext; /**< external data */ - snd_seq_ev_queue_control_t queue; /**< queue control */ - snd_seq_timestamp_t time; /**< timestamp */ - snd_seq_addr_t addr; /**< address */ - snd_seq_connect_t connect; /**< connect information */ - snd_seq_result_t result; /**< operation result code */ - } data; /**< event data... */ -} snd_seq_event_t; - - -/** \} */ - -#endif /* __ALSA_SEQ_EVENT_H */ - diff --git a/raylib/external/alsa/seq_midi_event.h b/raylib/external/alsa/seq_midi_event.h deleted file mode 100644 index 9b8ee59..0000000 --- a/raylib/external/alsa/seq_midi_event.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * \file include/seq_midi_event.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_SEQ_MIDI_EVENT_H -#define __ALSA_SEQ_MIDI_EVENT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup MIDI_Event Sequencer event <-> MIDI byte stream coder - * \ingroup Sequencer - * Sequencer event <-> MIDI byte stream coder - * \{ - */ - -/** container for sequencer midi event parsers */ -typedef struct snd_midi_event snd_midi_event_t; - -int snd_midi_event_new(size_t bufsize, snd_midi_event_t **rdev); -int snd_midi_event_resize_buffer(snd_midi_event_t *dev, size_t bufsize); -void snd_midi_event_free(snd_midi_event_t *dev); -void snd_midi_event_init(snd_midi_event_t *dev); -void snd_midi_event_reset_encode(snd_midi_event_t *dev); -void snd_midi_event_reset_decode(snd_midi_event_t *dev); -void snd_midi_event_no_status(snd_midi_event_t *dev, int on); -/* encode from byte stream - return number of written bytes if success */ -long snd_midi_event_encode(snd_midi_event_t *dev, const unsigned char *buf, long count, snd_seq_event_t *ev); -int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev); -/* decode from event to bytes - return number of written bytes if success */ -long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, const snd_seq_event_t *ev); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_SEQ_MIDI_EVENT_H */ - diff --git a/raylib/external/alsa/seqmid.h b/raylib/external/alsa/seqmid.h deleted file mode 100644 index 68069b2..0000000 --- a/raylib/external/alsa/seqmid.h +++ /dev/null @@ -1,490 +0,0 @@ -/** - * \file include/seqmid.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_SEQMID_H -#define __ALSA_SEQMID_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup SeqMiddle Sequencer Middle Level Interface - * Sequencer Middle Level Interface - * \ingroup Sequencer - * \{ - */ - -/** - * \brief initialize event record - * \param ev event record pointer - * - * This macro clears the given event record pointer to the default status. - */ -#define snd_seq_ev_clear(ev) \ - memset(ev, 0, sizeof(snd_seq_event_t)) - -/** - * \brief set the tag for given event - * \param ev event record - * \param t event tag - * - * This macro sets the tag to the given event record. - */ -#define snd_seq_ev_set_tag(ev,t) \ - ((ev)->tag = (t)) - -/** - * \brief set the explicit destination - * \param ev event record - * \param c destination client id - * \param p destination port id - * - * This macro sets the client and port id numbers to the given event record. - * - * \sa snd_seq_ev_set_subs() - */ -#define snd_seq_ev_set_dest(ev,c,p) \ - ((ev)->dest.client = (c), (ev)->dest.port = (p)) - -/** - * \brief set broadcasting to subscribers - * \param ev event record - * - * This macro sets the destination as the subscribers. - * - * \sa snd_seq_ev_set_dest() - */ -#define snd_seq_ev_set_subs(ev) \ - ((ev)->dest.client = SND_SEQ_ADDRESS_SUBSCRIBERS,\ - (ev)->dest.port = SND_SEQ_ADDRESS_UNKNOWN) - -/** - * \brief set broadcasting to all clients/ports - * \param ev event record - * - * This macro sets the destination as the broadcasting. - * - * \sa snd_seq_ev_set_dest() - */ -#define snd_seq_ev_set_broadcast(ev) \ - ((ev)->dest.client = SND_SEQ_ADDRESS_BROADCAST,\ - (ev)->dest.port = SND_SEQ_ADDRESS_BROADCAST) - -/** - * \brief set the source port - * \param ev event record - * \param p source port id - * - * This macro sets the source port id number. - */ -#define snd_seq_ev_set_source(ev,p) \ - ((ev)->source.port = (p)) - -/** - * \brief set direct passing mode (without queued) - * \param ev event instance - * - * This macro sets the event to the direct passing mode - * to be delivered immediately without queueing. - * - * \sa snd_seq_ev_schedule_tick(), snd_seq_ev_schedule_real() - */ -#define snd_seq_ev_set_direct(ev) \ - ((ev)->queue = SND_SEQ_QUEUE_DIRECT) - -/** - * \brief set tick-scheduling mode on queue - * \param ev event instance - * \param q queue id to schedule - * \param relative relative time-stamp if non-zero - * \param ttick tick time-stamp to be delivered - * - * This macro sets the scheduling of the event in the - * MIDI tick mode. - * - * \sa snd_seq_ev_schedule_real(), snd_seq_ev_set_direct() - */ -#define snd_seq_ev_schedule_tick(ev, q, relative, ttick) \ - ((ev)->flags &= ~(SND_SEQ_TIME_STAMP_MASK | SND_SEQ_TIME_MODE_MASK),\ - (ev)->flags |= SND_SEQ_TIME_STAMP_TICK,\ - (ev)->flags |= (relative) ? SND_SEQ_TIME_MODE_REL : SND_SEQ_TIME_MODE_ABS,\ - (ev)->time.tick = (ttick),\ - (ev)->queue = (q)) - -/** - * \brief set real-time-scheduling mode on queue - * \param ev event instance - * \param q queue id to schedule - * \param relative relative time-stamp if non-zero - * \param rtime time-stamp to be delivered - * - * This macro sets the scheduling of the event in the - * realtime mode. - * - * \sa snd_seq_ev_schedule_tick(), snd_seq_ev_set_direct() - */ -#define snd_seq_ev_schedule_real(ev, q, relative, rtime) \ - ((ev)->flags &= ~(SND_SEQ_TIME_STAMP_MASK | SND_SEQ_TIME_MODE_MASK),\ - (ev)->flags |= SND_SEQ_TIME_STAMP_REAL,\ - (ev)->flags |= (relative) ? SND_SEQ_TIME_MODE_REL : SND_SEQ_TIME_MODE_ABS,\ - (ev)->time.time = *(rtime),\ - (ev)->queue = (q)) - -/** - * \brief set event priority - * \param ev event instance - * \param high_prior 1 for high priority mode - */ -#define snd_seq_ev_set_priority(ev, high_prior) \ - ((ev)->flags &= ~SND_SEQ_PRIORITY_MASK,\ - (ev)->flags |= (high_prior) ? SND_SEQ_PRIORITY_HIGH : SND_SEQ_PRIORITY_NORMAL) - -/** - * \brief set fixed data - * \param ev event instance - * - * Sets the event length mode as fixed size. - * - * \sa snd_seq_ev_set_variable(), snd_seq_ev_set_varusr() - */ -#define snd_seq_ev_set_fixed(ev) \ - ((ev)->flags &= ~SND_SEQ_EVENT_LENGTH_MASK,\ - (ev)->flags |= SND_SEQ_EVENT_LENGTH_FIXED) - -/** - * \brief set variable data - * \param ev event instance - * \param datalen length of the external data - * \param dataptr pointer of the external data - * - * Sets the event length mode as variable length and stores the data. - * - * \sa snd_seq_ev_set_fixed(), snd_seq_ev_set_varusr() - */ -#define snd_seq_ev_set_variable(ev, datalen, dataptr) \ - ((ev)->flags &= ~SND_SEQ_EVENT_LENGTH_MASK,\ - (ev)->flags |= SND_SEQ_EVENT_LENGTH_VARIABLE,\ - (ev)->data.ext.len = (datalen),\ - (ev)->data.ext.ptr = (dataptr)) - -/** - * \brief set varusr data - * \param ev event instance - * \param datalen length of the external data - * \param dataptr pointer of the external data - * - * Sets the event length mode as variable user-space data and stores the data. - * - * \sa snd_seq_ev_set_fixed(), snd_seq_ev_set_variable() - */ -#define snd_seq_ev_set_varusr(ev, datalen, dataptr) \ - ((ev)->flags &= ~SND_SEQ_EVENT_LENGTH_MASK,\ - (ev)->flags |= SND_SEQ_EVENT_LENGTH_VARUSR,\ - (ev)->data.ext.len = (datalen),\ - (ev)->data.ext.ptr = (dataptr)) - -/** - * \brief set queue controls - * \param ev event record - * \param typ event type - * \param q queue id - * \param val control value - */ -#define snd_seq_ev_set_queue_control(ev, typ, q, val) \ - ((ev)->type = (typ),\ - snd_seq_ev_set_dest(ev, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_TIMER),\ - (ev)->data.queue.queue = (q),\ - (ev)->data.queue.param.value = (val)) - -/** - * \brief set the start queue event - * \param ev event record - * \param q queue id to start - * - * \sa snd_seq_ev_set_queue_stop(), snd_seq_ev_set_queue_continue() - */ -#define snd_seq_ev_set_queue_start(ev, q) \ - snd_seq_ev_set_queue_control(ev, SND_SEQ_EVENT_START, q, 0) - -/** - * \brief set the stop queue event - * \param ev event record - * \param q queue id to stop - * - * \sa snd_seq_ev_set_queue_start(), snd_seq_ev_set_queue_continue() - */ -#define snd_seq_ev_set_queue_stop(ev, q) \ - snd_seq_ev_set_queue_control(ev, SND_SEQ_EVENT_STOP, q, 0) - -/** - * \brief set the stop queue event - * \param ev event record - * \param q queue id to continue - * - * \sa snd_seq_ev_set_queue_start(), snd_seq_ev_set_queue_stop() - */ -#define snd_seq_ev_set_queue_continue(ev, q) \ - snd_seq_ev_set_queue_control(ev, SND_SEQ_EVENT_CONTINUE, q, 0) - -/** - * \brief set the stop queue event - * \param ev event record - * \param q queue id to change tempo - * \param val the new tempo value - */ -#define snd_seq_ev_set_queue_tempo(ev, q, val) \ - snd_seq_ev_set_queue_control(ev, SND_SEQ_EVENT_TEMPO, q, val) - -/** - * \brief set the real-time position of a queue - * \param ev event record - * \param q queue id to change tempo - * \param rtime the new real-time pointer - */ -#define snd_seq_ev_set_queue_pos_real(ev, q, rtime) \ - ((ev)->type = SND_SEQ_EVENT_SETPOS_TIME,\ - snd_seq_ev_set_dest(ev, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_TIMER),\ - (ev)->data.queue.queue = (q),\ - (ev)->data.queue.param.time.time = *(rtime)) - -/** - * \brief set the tick-time position of a queue - * \param ev event record - * \param q queue id to change tempo - * \param ttime the new tick-time - */ -#define snd_seq_ev_set_queue_pos_tick(ev, q, ttime) \ - ((ev)->type = SND_SEQ_EVENT_SETPOS_TICK,\ - snd_seq_ev_set_dest(ev, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_TIMER),\ - (ev)->data.queue.queue = (q),\ - (ev)->data.queue.param.time.tick = (ttime)) - -/* set and send a queue control event */ -int snd_seq_control_queue(snd_seq_t *seq, int q, int type, int value, snd_seq_event_t *ev); - -/** - * \brief start the specified queue - * \param seq sequencer handle - * \param q queue id to start - * \param ev optional event record (see #snd_seq_control_queue) - */ -#define snd_seq_start_queue(seq, q, ev) \ - snd_seq_control_queue(seq, q, SND_SEQ_EVENT_START, 0, ev) - -/** - * \brief stop the specified queue - * \param seq sequencer handle - * \param q queue id to stop - * \param ev optional event record (see #snd_seq_control_queue) - */ -#define snd_seq_stop_queue(seq, q, ev) \ - snd_seq_control_queue(seq, q, SND_SEQ_EVENT_STOP, 0, ev) - -/** - * \brief continue the specified queue - * \param seq sequencer handle - * \param q queue id to continue - * \param ev optional event record (see #snd_seq_control_queue) - */ -#define snd_seq_continue_queue(seq, q, ev) \ - snd_seq_control_queue(seq, q, SND_SEQ_EVENT_CONTINUE, 0, ev) - -/** - * \brief change the tempo of the specified queue - * \param seq sequencer handle - * \param q queue id - * \param tempo the new tempo value - * \param ev optional event record (see #snd_seq_control_queue) - */ -#define snd_seq_change_queue_tempo(seq, q, tempo, ev) \ - snd_seq_control_queue(seq, q, SND_SEQ_EVENT_TEMPO, tempo, ev) - -/* create a port - simple version - return the port number */ -int snd_seq_create_simple_port(snd_seq_t *seq, const char *name, - unsigned int caps, unsigned int type); -/* delete the port */ -int snd_seq_delete_simple_port(snd_seq_t *seq, int port); - -/* simple subscription between this port and another port - (w/o exclusive & time conversion) - */ -int snd_seq_connect_from(snd_seq_t *seq, int my_port, int src_client, int src_port); -int snd_seq_connect_to(snd_seq_t *seq, int my_port, int dest_client, int dest_port); -int snd_seq_disconnect_from(snd_seq_t *seq, int my_port, int src_client, int src_port); -int snd_seq_disconnect_to(snd_seq_t *seq, int my_port, int dest_client, int dest_port); - -/* - * set client information - */ -int snd_seq_set_client_name(snd_seq_t *seq, const char *name); -int snd_seq_set_client_event_filter(snd_seq_t *seq, int event_type); -int snd_seq_set_client_pool_output(snd_seq_t *seq, size_t size); -int snd_seq_set_client_pool_output_room(snd_seq_t *seq, size_t size); -int snd_seq_set_client_pool_input(snd_seq_t *seq, size_t size); -/* sync output queue */ -int snd_seq_sync_output_queue(snd_seq_t *seq); - -/* - * parse the given string and get the sequencer address - */ -int snd_seq_parse_address(snd_seq_t *seq, snd_seq_addr_t *addr, const char *str); - -/* - * reset client input/output pool - */ -int snd_seq_reset_pool_output(snd_seq_t *seq); -int snd_seq_reset_pool_input(snd_seq_t *seq); - -/** - * \brief set note event - * \param ev event record - * \param ch channel number - * \param key note key - * \param vel velocity - * \param dur duration (in tick or msec) - */ -#define snd_seq_ev_set_note(ev, ch, key, vel, dur) \ - ((ev)->type = SND_SEQ_EVENT_NOTE,\ - snd_seq_ev_set_fixed(ev),\ - (ev)->data.note.channel = (ch),\ - (ev)->data.note.note = (key),\ - (ev)->data.note.velocity = (vel),\ - (ev)->data.note.duration = (dur)) - -/** - * \brief set note-on event - * \param ev event record - * \param ch channel number - * \param key note key - * \param vel velocity - */ -#define snd_seq_ev_set_noteon(ev, ch, key, vel) \ - ((ev)->type = SND_SEQ_EVENT_NOTEON,\ - snd_seq_ev_set_fixed(ev),\ - (ev)->data.note.channel = (ch),\ - (ev)->data.note.note = (key),\ - (ev)->data.note.velocity = (vel)) - -/** - * \brief set note-off event - * \param ev event record - * \param ch channel number - * \param key note key - * \param vel velocity - */ -#define snd_seq_ev_set_noteoff(ev, ch, key, vel) \ - ((ev)->type = SND_SEQ_EVENT_NOTEOFF,\ - snd_seq_ev_set_fixed(ev),\ - (ev)->data.note.channel = (ch),\ - (ev)->data.note.note = (key),\ - (ev)->data.note.velocity = (vel)) - -/** - * \brief set key-pressure event - * \param ev event record - * \param ch channel number - * \param key note key - * \param vel velocity - */ -#define snd_seq_ev_set_keypress(ev,ch,key,vel) \ - ((ev)->type = SND_SEQ_EVENT_KEYPRESS,\ - snd_seq_ev_set_fixed(ev),\ - (ev)->data.note.channel = (ch),\ - (ev)->data.note.note = (key),\ - (ev)->data.note.velocity = (vel)) - -/** - * \brief set MIDI controller event - * \param ev event record - * \param ch channel number - * \param cc controller number - * \param val control value - */ -#define snd_seq_ev_set_controller(ev,ch,cc,val) \ - ((ev)->type = SND_SEQ_EVENT_CONTROLLER,\ - snd_seq_ev_set_fixed(ev),\ - (ev)->data.control.channel = (ch),\ - (ev)->data.control.param = (cc),\ - (ev)->data.control.value = (val)) - -/** - * \brief set program change event - * \param ev event record - * \param ch channel number - * \param val program number - */ -#define snd_seq_ev_set_pgmchange(ev,ch,val) \ - ((ev)->type = SND_SEQ_EVENT_PGMCHANGE,\ - snd_seq_ev_set_fixed(ev),\ - (ev)->data.control.channel = (ch),\ - (ev)->data.control.value = (val)) - -/** - * \brief set pitch-bend event - * \param ev event record - * \param ch channel number - * \param val pitch bend; zero centered from -8192 to 8191 - */ -#define snd_seq_ev_set_pitchbend(ev,ch,val) \ - ((ev)->type = SND_SEQ_EVENT_PITCHBEND,\ - snd_seq_ev_set_fixed(ev),\ - (ev)->data.control.channel = (ch),\ - (ev)->data.control.value = (val)) - -/** - * \brief set channel pressure event - * \param ev event record - * \param ch channel number - * \param val channel pressure value - */ -#define snd_seq_ev_set_chanpress(ev,ch,val) \ - ((ev)->type = SND_SEQ_EVENT_CHANPRESS,\ - snd_seq_ev_set_fixed(ev),\ - (ev)->data.control.channel = (ch),\ - (ev)->data.control.value = (val)) - -/** - * \brief set sysex event - * \param ev event record - * \param datalen length of sysex data - * \param dataptr sysex data pointer - * - * the sysex data must contain the start byte 0xf0 and the end byte 0xf7. - */ -#define snd_seq_ev_set_sysex(ev,datalen,dataptr) \ - ((ev)->type = SND_SEQ_EVENT_SYSEX,\ - snd_seq_ev_set_variable(ev, datalen, dataptr)) - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_SEQMID_H */ - diff --git a/raylib/external/alsa/sound/asoc.h b/raylib/external/alsa/sound/asoc.h deleted file mode 100644 index 0f5d9f9..0000000 --- a/raylib/external/alsa/sound/asoc.h +++ /dev/null @@ -1,551 +0,0 @@ -/* - * uapi/sound/asoc.h -- ALSA SoC Firmware Controls and DAPM - * - * Copyright (C) 2012 Texas Instruments Inc. - * Copyright (C) 2015 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Simple file API to load FW that includes mixers, coefficients, DAPM graphs, - * algorithms, equalisers, DAIs, widgets etc. -*/ - -#ifndef __LINUX_UAPI_SND_ASOC_H -#define __LINUX_UAPI_SND_ASOC_H - -/* - * Maximum number of channels topology kcontrol can represent. - */ -#define SND_SOC_TPLG_MAX_CHAN 8 - -/* - * Maximum number of PCM formats capability - */ -#define SND_SOC_TPLG_MAX_FORMATS 16 - -/* - * Maximum number of PCM stream configs - */ -#define SND_SOC_TPLG_STREAM_CONFIG_MAX 8 - -/* - * Maximum number of physical link's hardware configs - */ -#define SND_SOC_TPLG_HW_CONFIG_MAX 8 - -/* individual kcontrol info types - can be mixed with other types */ -#define SND_SOC_TPLG_CTL_VOLSW 1 -#define SND_SOC_TPLG_CTL_VOLSW_SX 2 -#define SND_SOC_TPLG_CTL_VOLSW_XR_SX 3 -#define SND_SOC_TPLG_CTL_ENUM 4 -#define SND_SOC_TPLG_CTL_BYTES 5 -#define SND_SOC_TPLG_CTL_ENUM_VALUE 6 -#define SND_SOC_TPLG_CTL_RANGE 7 -#define SND_SOC_TPLG_CTL_STROBE 8 - - -/* individual widget kcontrol info types - can be mixed with other types */ -#define SND_SOC_TPLG_DAPM_CTL_VOLSW 64 -#define SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE 65 -#define SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT 66 -#define SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE 67 -#define SND_SOC_TPLG_DAPM_CTL_PIN 68 - -/* DAPM widget types - add new items to the end */ -#define SND_SOC_TPLG_DAPM_INPUT 0 -#define SND_SOC_TPLG_DAPM_OUTPUT 1 -#define SND_SOC_TPLG_DAPM_MUX 2 -#define SND_SOC_TPLG_DAPM_MIXER 3 -#define SND_SOC_TPLG_DAPM_PGA 4 -#define SND_SOC_TPLG_DAPM_OUT_DRV 5 -#define SND_SOC_TPLG_DAPM_ADC 6 -#define SND_SOC_TPLG_DAPM_DAC 7 -#define SND_SOC_TPLG_DAPM_SWITCH 8 -#define SND_SOC_TPLG_DAPM_PRE 9 -#define SND_SOC_TPLG_DAPM_POST 10 -#define SND_SOC_TPLG_DAPM_AIF_IN 11 -#define SND_SOC_TPLG_DAPM_AIF_OUT 12 -#define SND_SOC_TPLG_DAPM_DAI_IN 13 -#define SND_SOC_TPLG_DAPM_DAI_OUT 14 -#define SND_SOC_TPLG_DAPM_DAI_LINK 15 -#define SND_SOC_TPLG_DAPM_BUFFER 16 -#define SND_SOC_TPLG_DAPM_SCHEDULER 17 -#define SND_SOC_TPLG_DAPM_EFFECT 18 -#define SND_SOC_TPLG_DAPM_SIGGEN 19 -#define SND_SOC_TPLG_DAPM_SRC 20 -#define SND_SOC_TPLG_DAPM_ASRC 21 -#define SND_SOC_TPLG_DAPM_ENCODER 22 -#define SND_SOC_TPLG_DAPM_DECODER 23 -#define SND_SOC_TPLG_DAPM_LAST SND_SOC_TPLG_DAPM_DECODER - -/* Header magic number and string sizes */ -#define SND_SOC_TPLG_MAGIC 0x41536F43 /* ASoC */ - -/* string sizes */ -#define SND_SOC_TPLG_NUM_TEXTS 16 - -/* ABI version */ -#define SND_SOC_TPLG_ABI_VERSION 0x5 /* current version */ -#define SND_SOC_TPLG_ABI_VERSION_MIN 0x4 /* oldest version supported */ - -/* Max size of TLV data */ -#define SND_SOC_TPLG_TLV_SIZE 32 - -/* - * File and Block header data types. - * Add new generic and vendor types to end of list. - * Generic types are handled by the core whilst vendors types are passed - * to the component drivers for handling. - */ -#define SND_SOC_TPLG_TYPE_MIXER 1 -#define SND_SOC_TPLG_TYPE_BYTES 2 -#define SND_SOC_TPLG_TYPE_ENUM 3 -#define SND_SOC_TPLG_TYPE_DAPM_GRAPH 4 -#define SND_SOC_TPLG_TYPE_DAPM_WIDGET 5 -#define SND_SOC_TPLG_TYPE_DAI_LINK 6 -#define SND_SOC_TPLG_TYPE_PCM 7 -#define SND_SOC_TPLG_TYPE_MANIFEST 8 -#define SND_SOC_TPLG_TYPE_CODEC_LINK 9 -#define SND_SOC_TPLG_TYPE_BACKEND_LINK 10 -#define SND_SOC_TPLG_TYPE_PDATA 11 -#define SND_SOC_TPLG_TYPE_DAI 12 -#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_DAI - -/* vendor block IDs - please add new vendor types to end */ -#define SND_SOC_TPLG_TYPE_VENDOR_FW 1000 -#define SND_SOC_TPLG_TYPE_VENDOR_CONFIG 1001 -#define SND_SOC_TPLG_TYPE_VENDOR_COEFF 1002 -#define SND_SOC_TPLG_TYPEVENDOR_CODEC 1003 - -#define SND_SOC_TPLG_STREAM_PLAYBACK 0 -#define SND_SOC_TPLG_STREAM_CAPTURE 1 - -/* vendor tuple types */ -#define SND_SOC_TPLG_TUPLE_TYPE_UUID 0 -#define SND_SOC_TPLG_TUPLE_TYPE_STRING 1 -#define SND_SOC_TPLG_TUPLE_TYPE_BOOL 2 -#define SND_SOC_TPLG_TUPLE_TYPE_BYTE 3 -#define SND_SOC_TPLG_TUPLE_TYPE_WORD 4 -#define SND_SOC_TPLG_TUPLE_TYPE_SHORT 5 - -/* DAI flags */ -#define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES (1 << 0) -#define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS (1 << 1) -#define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2) - -/* DAI physical PCM data formats. - * Add new formats to the end of the list. - */ -#define SND_SOC_DAI_FORMAT_I2S 1 /* I2S mode */ -#define SND_SOC_DAI_FORMAT_RIGHT_J 2 /* Right Justified mode */ -#define SND_SOC_DAI_FORMAT_LEFT_J 3 /* Left Justified mode */ -#define SND_SOC_DAI_FORMAT_DSP_A 4 /* L data MSB after FRM LRC */ -#define SND_SOC_DAI_FORMAT_DSP_B 5 /* L data MSB during FRM LRC */ -#define SND_SOC_DAI_FORMAT_AC97 6 /* AC97 */ -#define SND_SOC_DAI_FORMAT_PDM 7 /* Pulse density modulation */ - -/* left and right justified also known as MSB and LSB respectively */ -#define SND_SOC_DAI_FORMAT_MSB SND_SOC_DAI_FORMAT_LEFT_J -#define SND_SOC_DAI_FORMAT_LSB SND_SOC_DAI_FORMAT_RIGHT_J - -/* DAI link flags */ -#define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES (1 << 0) -#define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS (1 << 1) -#define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2) -#define SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP (1 << 3) - -/* - * Block Header. - * This header precedes all object and object arrays below. - */ -struct snd_soc_tplg_hdr { - __le32 magic; /* magic number */ - __le32 abi; /* ABI version */ - __le32 version; /* optional vendor specific version details */ - __le32 type; /* SND_SOC_TPLG_TYPE_ */ - __le32 size; /* size of this structure */ - __le32 vendor_type; /* optional vendor specific type info */ - __le32 payload_size; /* data bytes, excluding this header */ - __le32 index; /* identifier for block */ - __le32 count; /* number of elements in block */ -} __attribute__((packed)); - -/* vendor tuple for uuid */ -struct snd_soc_tplg_vendor_uuid_elem { - __le32 token; - char uuid[16]; -} __attribute__((packed)); - -/* vendor tuple for a bool/byte/short/word value */ -struct snd_soc_tplg_vendor_value_elem { - __le32 token; - __le32 value; -} __attribute__((packed)); - -/* vendor tuple for string */ -struct snd_soc_tplg_vendor_string_elem { - __le32 token; - char string[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; -} __attribute__((packed)); - -struct snd_soc_tplg_vendor_array { - __le32 size; /* size in bytes of the array, including all elements */ - __le32 type; /* SND_SOC_TPLG_TUPLE_TYPE_ */ - __le32 num_elems; /* number of elements in array */ - union { - struct snd_soc_tplg_vendor_uuid_elem uuid[0]; - struct snd_soc_tplg_vendor_value_elem value[0]; - struct snd_soc_tplg_vendor_string_elem string[0]; - }; -} __attribute__((packed)); - -/* - * Private data. - * All topology objects may have private data that can be used by the driver or - * firmware. Core will ignore this data. - */ -struct snd_soc_tplg_private { - __le32 size; /* in bytes of private data */ - union { - char data[0]; - struct snd_soc_tplg_vendor_array array[0]; - }; -} __attribute__((packed)); - -/* - * Kcontrol TLV data. - */ -struct snd_soc_tplg_tlv_dbscale { - __le32 min; - __le32 step; - __le32 mute; -} __attribute__((packed)); - -struct snd_soc_tplg_ctl_tlv { - __le32 size; /* in bytes of this structure */ - __le32 type; /* SNDRV_CTL_TLVT_*, type of TLV */ - union { - __le32 data[SND_SOC_TPLG_TLV_SIZE]; - struct snd_soc_tplg_tlv_dbscale scale; - }; -} __attribute__((packed)); - -/* - * Kcontrol channel data - */ -struct snd_soc_tplg_channel { - __le32 size; /* in bytes of this structure */ - __le32 reg; - __le32 shift; - __le32 id; /* ID maps to Left, Right, LFE etc */ -} __attribute__((packed)); - -/* - * Genericl Operations IDs, for binding Kcontrol or Bytes ext ops - * Kcontrol ops need get/put/info. - * Bytes ext ops need get/put. - */ -struct snd_soc_tplg_io_ops { - __le32 get; - __le32 put; - __le32 info; -} __attribute__((packed)); - -/* - * kcontrol header - */ -struct snd_soc_tplg_ctl_hdr { - __le32 size; /* in bytes of this structure */ - __le32 type; - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le32 access; - struct snd_soc_tplg_io_ops ops; - struct snd_soc_tplg_ctl_tlv tlv; -} __attribute__((packed)); - -/* - * Stream Capabilities - */ -struct snd_soc_tplg_stream_caps { - __le32 size; /* in bytes of this structure */ - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le64 formats; /* supported formats SNDRV_PCM_FMTBIT_* */ - __le32 rates; /* supported rates SNDRV_PCM_RATE_* */ - __le32 rate_min; /* min rate */ - __le32 rate_max; /* max rate */ - __le32 channels_min; /* min channels */ - __le32 channels_max; /* max channels */ - __le32 periods_min; /* min number of periods */ - __le32 periods_max; /* max number of periods */ - __le32 period_size_min; /* min period size bytes */ - __le32 period_size_max; /* max period size bytes */ - __le32 buffer_size_min; /* min buffer size bytes */ - __le32 buffer_size_max; /* max buffer size bytes */ - __le32 sig_bits; /* number of bits of content */ -} __attribute__((packed)); - -/* - * FE or BE Stream configuration supported by SW/FW - */ -struct snd_soc_tplg_stream { - __le32 size; /* in bytes of this structure */ - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* Name of the stream */ - __le64 format; /* SNDRV_PCM_FMTBIT_* */ - __le32 rate; /* SNDRV_PCM_RATE_* */ - __le32 period_bytes; /* size of period in bytes */ - __le32 buffer_bytes; /* size of buffer in bytes */ - __le32 channels; /* channels */ -} __attribute__((packed)); - - -/* - * Describes a physical link's runtime supported hardware config, - * i.e. hardware audio formats. - */ -struct snd_soc_tplg_hw_config { - __le32 size; /* in bytes of this structure */ - __le32 id; /* unique ID - - used to match */ - __le32 fmt; /* SND_SOC_DAI_FORMAT_ format value */ - __u8 clock_gated; /* 1 if clock can be gated to save power */ - __u8 invert_bclk; /* 1 for inverted BCLK, 0 for normal */ - __u8 invert_fsync; /* 1 for inverted frame clock, 0 for normal */ - __u8 bclk_master; /* 1 for master of BCLK, 0 for slave */ - __u8 fsync_master; /* 1 for master of FSYNC, 0 for slave */ - __u8 mclk_direction; /* 0 for input, 1 for output */ - __le16 reserved; /* for 32bit alignment */ - __le32 mclk_rate; /* MCLK or SYSCLK freqency in Hz */ - __le32 bclk_rate; /* BCLK freqency in Hz */ - __le32 fsync_rate; /* frame clock in Hz */ - __le32 tdm_slots; /* number of TDM slots in use */ - __le32 tdm_slot_width; /* width in bits for each slot */ - __le32 tx_slots; /* bit mask for active Tx slots */ - __le32 rx_slots; /* bit mask for active Rx slots */ - __le32 tx_channels; /* number of Tx channels */ - __le32 tx_chanmap[SND_SOC_TPLG_MAX_CHAN]; /* array of slot number */ - __le32 rx_channels; /* number of Rx channels */ - __le32 rx_chanmap[SND_SOC_TPLG_MAX_CHAN]; /* array of slot number */ -} __attribute__((packed)); - -/* - * Manifest. List totals for each payload type. Not used in parsing, but will - * be passed to the component driver before any other objects in order for any - * global component resource allocations. - * - * File block representation for manifest :- - * +-----------------------------------+----+ - * | struct snd_soc_tplg_hdr | 1 | - * +-----------------------------------+----+ - * | struct snd_soc_tplg_manifest | 1 | - * +-----------------------------------+----+ - */ -struct snd_soc_tplg_manifest { - __le32 size; /* in bytes of this structure */ - __le32 control_elems; /* number of control elements */ - __le32 widget_elems; /* number of widget elements */ - __le32 graph_elems; /* number of graph elements */ - __le32 pcm_elems; /* number of PCM elements */ - __le32 dai_link_elems; /* number of DAI link elements */ - __le32 dai_elems; /* number of physical DAI elements */ - __le32 reserved[20]; /* reserved for new ABI element types */ - struct snd_soc_tplg_private priv; -} __attribute__((packed)); - -/* - * Mixer kcontrol. - * - * File block representation for mixer kcontrol :- - * +-----------------------------------+----+ - * | struct snd_soc_tplg_hdr | 1 | - * +-----------------------------------+----+ - * | struct snd_soc_tplg_mixer_control | N | - * +-----------------------------------+----+ - */ -struct snd_soc_tplg_mixer_control { - struct snd_soc_tplg_ctl_hdr hdr; - __le32 size; /* in bytes of this structure */ - __le32 min; - __le32 max; - __le32 platform_max; - __le32 invert; - __le32 num_channels; - struct snd_soc_tplg_channel channel[SND_SOC_TPLG_MAX_CHAN]; - struct snd_soc_tplg_private priv; -} __attribute__((packed)); - -/* - * Enumerated kcontrol - * - * File block representation for enum kcontrol :- - * +-----------------------------------+----+ - * | struct snd_soc_tplg_hdr | 1 | - * +-----------------------------------+----+ - * | struct snd_soc_tplg_enum_control | N | - * +-----------------------------------+----+ - */ -struct snd_soc_tplg_enum_control { - struct snd_soc_tplg_ctl_hdr hdr; - __le32 size; /* in bytes of this structure */ - __le32 num_channels; - struct snd_soc_tplg_channel channel[SND_SOC_TPLG_MAX_CHAN]; - __le32 items; - __le32 mask; - __le32 count; - char texts[SND_SOC_TPLG_NUM_TEXTS][SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le32 values[SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN / 4]; - struct snd_soc_tplg_private priv; -} __attribute__((packed)); - -/* - * Bytes kcontrol - * - * File block representation for bytes kcontrol :- - * +-----------------------------------+----+ - * | struct snd_soc_tplg_hdr | 1 | - * +-----------------------------------+----+ - * | struct snd_soc_tplg_bytes_control | N | - * +-----------------------------------+----+ - */ -struct snd_soc_tplg_bytes_control { - struct snd_soc_tplg_ctl_hdr hdr; - __le32 size; /* in bytes of this structure */ - __le32 max; - __le32 mask; - __le32 base; - __le32 num_regs; - struct snd_soc_tplg_io_ops ext_ops; - struct snd_soc_tplg_private priv; -} __attribute__((packed)); - -/* - * DAPM Graph Element - * - * File block representation for DAPM graph elements :- - * +-------------------------------------+----+ - * | struct snd_soc_tplg_hdr | 1 | - * +-------------------------------------+----+ - * | struct snd_soc_tplg_dapm_graph_elem | N | - * +-------------------------------------+----+ - */ -struct snd_soc_tplg_dapm_graph_elem { - char sink[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char control[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char source[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; -} __attribute__((packed)); - -/* - * DAPM Widget. - * - * File block representation for DAPM widget :- - * +-------------------------------------+-----+ - * | struct snd_soc_tplg_hdr | 1 | - * +-------------------------------------+-----+ - * | struct snd_soc_tplg_dapm_widget | N | - * +-------------------------------------+-----+ - * | struct snd_soc_tplg_enum_control | 0|1 | - * | struct snd_soc_tplg_mixer_control | 0|N | - * +-------------------------------------+-----+ - * - * Optional enum or mixer control can be appended to the end of each widget - * in the block. - */ -struct snd_soc_tplg_dapm_widget { - __le32 size; /* in bytes of this structure */ - __le32 id; /* SND_SOC_DAPM_CTL */ - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char sname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - - __le32 reg; /* negative reg = no direct dapm */ - __le32 shift; /* bits to shift */ - __le32 mask; /* non-shifted mask */ - __le32 subseq; /* sort within widget type */ - __le32 invert; /* invert the power bit */ - __le32 ignore_suspend; /* kept enabled over suspend */ - __le16 event_flags; - __le16 event_type; - __le32 num_kcontrols; - struct snd_soc_tplg_private priv; - /* - * kcontrols that relate to this widget - * follow here after widget private data - */ -} __attribute__((packed)); - - -/* - * Describes SW/FW specific features of PCM (FE DAI & DAI link). - * - * File block representation for PCM :- - * +-----------------------------------+-----+ - * | struct snd_soc_tplg_hdr | 1 | - * +-----------------------------------+-----+ - * | struct snd_soc_tplg_pcm | N | - * +-----------------------------------+-----+ - */ -struct snd_soc_tplg_pcm { - __le32 size; /* in bytes of this structure */ - char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le32 pcm_id; /* unique ID - used to match with DAI link */ - __le32 dai_id; /* unique ID - used to match */ - __le32 playback; /* supports playback mode */ - __le32 capture; /* supports capture mode */ - __le32 compress; /* 1 = compressed; 0 = PCM */ - struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */ - __le32 num_streams; /* number of streams */ - struct snd_soc_tplg_stream_caps caps[2]; /* playback and capture for DAI */ - __le32 flag_mask; /* bitmask of flags to configure */ - __le32 flags; /* SND_SOC_TPLG_LNK_FLGBIT_* flag value */ - struct snd_soc_tplg_private priv; -} __attribute__((packed)); - - -/* - * Describes the physical link runtime supported configs or params - * - * File block representation for physical link config :- - * +-----------------------------------+-----+ - * | struct snd_soc_tplg_hdr | 1 | - * +-----------------------------------+-----+ - * | struct snd_soc_tplg_link_config | N | - * +-----------------------------------+-----+ - */ -struct snd_soc_tplg_link_config { - __le32 size; /* in bytes of this structure */ - __le32 id; /* unique ID - used to match */ - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* name - used to match */ - char stream_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* stream name - used to match */ - struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */ - __le32 num_streams; /* number of streams */ - struct snd_soc_tplg_hw_config hw_config[SND_SOC_TPLG_HW_CONFIG_MAX]; /* hw configs */ - __le32 num_hw_configs; /* number of hw configs */ - __le32 default_hw_config_id; /* default hw config ID for init */ - __le32 flag_mask; /* bitmask of flags to configure */ - __le32 flags; /* SND_SOC_TPLG_LNK_FLGBIT_* flag value */ - struct snd_soc_tplg_private priv; -} __attribute__((packed)); - -/* - * Describes SW/FW specific features of physical DAI. - * It can be used to configure backend DAIs for DPCM. - * - * File block representation for physical DAI :- - * +-----------------------------------+-----+ - * | struct snd_soc_tplg_hdr | 1 | - * +-----------------------------------+-----+ - * | struct snd_soc_tplg_dai | N | - * +-----------------------------------+-----+ - */ -struct snd_soc_tplg_dai { - __le32 size; /* in bytes of this structure */ - char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* name - used to match */ - __le32 dai_id; /* unique ID - used to match */ - __le32 playback; /* supports playback mode */ - __le32 capture; /* supports capture mode */ - struct snd_soc_tplg_stream_caps caps[2]; /* playback and capture for DAI */ - __le32 flag_mask; /* bitmask of flags to configure */ - __le32 flags; /* SND_SOC_TPLG_DAI_FLGBIT_* */ - struct snd_soc_tplg_private priv; -} __attribute__((packed)); -#endif diff --git a/raylib/external/alsa/sound/asound_fm.h b/raylib/external/alsa/sound/asound_fm.h deleted file mode 100644 index c2a4b96..0000000 --- a/raylib/external/alsa/sound/asound_fm.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef __SOUND_ASOUND_FM_H -#define __SOUND_ASOUND_FM_H - -/* - * Advanced Linux Sound Architecture - ALSA - * - * Interface file between ALSA driver & user space - * Copyright (c) 1994-98 by Jaroslav Kysela , - * 4Front Technologies - * - * Direct FM control - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#define SNDRV_DM_FM_MODE_OPL2 0x00 -#define SNDRV_DM_FM_MODE_OPL3 0x01 - -struct snd_dm_fm_info { - unsigned char fm_mode; /* OPL mode, see SNDRV_DM_FM_MODE_XXX */ - unsigned char rhythm; /* percussion mode flag */ -}; - -/* - * Data structure composing an FM "note" or sound event. - */ - -struct snd_dm_fm_voice { - unsigned char op; /* operator cell (0 or 1) */ - unsigned char voice; /* FM voice (0 to 17) */ - - unsigned char am; /* amplitude modulation */ - unsigned char vibrato; /* vibrato effect */ - unsigned char do_sustain; /* sustain phase */ - unsigned char kbd_scale; /* keyboard scaling */ - unsigned char harmonic; /* 4 bits: harmonic and multiplier */ - unsigned char scale_level; /* 2 bits: decrease output freq rises */ - unsigned char volume; /* 6 bits: volume */ - - unsigned char attack; /* 4 bits: attack rate */ - unsigned char decay; /* 4 bits: decay rate */ - unsigned char sustain; /* 4 bits: sustain level */ - unsigned char release; /* 4 bits: release rate */ - - unsigned char feedback; /* 3 bits: feedback for op0 */ - unsigned char connection; /* 0 for serial, 1 for parallel */ - unsigned char left; /* stereo left */ - unsigned char right; /* stereo right */ - unsigned char waveform; /* 3 bits: waveform shape */ -}; - -/* - * This describes an FM note by its voice, octave, frequency number (10bit) - * and key on/off. - */ - -struct snd_dm_fm_note { - unsigned char voice; /* 0-17 voice channel */ - unsigned char octave; /* 3 bits: what octave to play */ - unsigned int fnum; /* 10 bits: frequency number */ - unsigned char key_on; /* set for active, clear for silent */ -}; - -/* - * FM parameters that apply globally to all voices, and thus are not "notes" - */ - -struct snd_dm_fm_params { - unsigned char am_depth; /* amplitude modulation depth (1=hi) */ - unsigned char vib_depth; /* vibrato depth (1=hi) */ - unsigned char kbd_split; /* keyboard split */ - unsigned char rhythm; /* percussion mode select */ - - /* This block is the percussion instrument data */ - unsigned char bass; - unsigned char snare; - unsigned char tomtom; - unsigned char cymbal; - unsigned char hihat; -}; - -/* - * FM mode ioctl settings - */ - -#define SNDRV_DM_FM_IOCTL_INFO _IOR('H', 0x20, struct snd_dm_fm_info) -#define SNDRV_DM_FM_IOCTL_RESET _IO ('H', 0x21) -#define SNDRV_DM_FM_IOCTL_PLAY_NOTE _IOW('H', 0x22, struct snd_dm_fm_note) -#define SNDRV_DM_FM_IOCTL_SET_VOICE _IOW('H', 0x23, struct snd_dm_fm_voice) -#define SNDRV_DM_FM_IOCTL_SET_PARAMS _IOW('H', 0x24, struct snd_dm_fm_params) -#define SNDRV_DM_FM_IOCTL_SET_MODE _IOW('H', 0x25, int) -/* for OPL3 only */ -#define SNDRV_DM_FM_IOCTL_SET_CONNECTION _IOW('H', 0x26, int) -/* SBI patch management */ -#define SNDRV_DM_FM_IOCTL_CLEAR_PATCHES _IO ('H', 0x40) - -#define SNDRV_DM_FM_OSS_IOCTL_RESET 0x20 -#define SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE 0x21 -#define SNDRV_DM_FM_OSS_IOCTL_SET_VOICE 0x22 -#define SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS 0x23 -#define SNDRV_DM_FM_OSS_IOCTL_SET_MODE 0x24 -#define SNDRV_DM_FM_OSS_IOCTL_SET_OPL 0x25 - -/* - * Patch Record - fixed size for write - */ - -#define FM_KEY_SBI "SBI\032" -#define FM_KEY_2OP "2OP\032" -#define FM_KEY_4OP "4OP\032" - -struct sbi_patch { - unsigned char prog; - unsigned char bank; - char key[4]; - char name[25]; - char extension[7]; - unsigned char data[32]; -}; - -#endif /* __SOUND_ASOUND_FM_H */ diff --git a/raylib/external/alsa/sound/emu10k1.h b/raylib/external/alsa/sound/emu10k1.h deleted file mode 100644 index 94018b7..0000000 --- a/raylib/external/alsa/sound/emu10k1.h +++ /dev/null @@ -1,349 +0,0 @@ -#ifndef __SOUND_EMU10K1_H -#define __SOUND_EMU10K1_H - -/* - * Copyright (c) by Jaroslav Kysela , - * Creative Labs, Inc. - * Definitions for EMU10K1 (SB Live!) chips - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include - -/* - * ---- FX8010 ---- - */ - -#define EMU10K1_CARD_CREATIVE 0x00000000 -#define EMU10K1_CARD_EMUAPS 0x00000001 - -#define EMU10K1_FX8010_PCM_COUNT 8 - -/* instruction set */ -#define iMAC0 0x00 /* R = A + (X * Y >> 31) ; saturation */ -#define iMAC1 0x01 /* R = A + (-X * Y >> 31) ; saturation */ -#define iMAC2 0x02 /* R = A + (X * Y >> 31) ; wraparound */ -#define iMAC3 0x03 /* R = A + (-X * Y >> 31) ; wraparound */ -#define iMACINT0 0x04 /* R = A + X * Y ; saturation */ -#define iMACINT1 0x05 /* R = A + X * Y ; wraparound (31-bit) */ -#define iACC3 0x06 /* R = A + X + Y ; saturation */ -#define iMACMV 0x07 /* R = A, acc += X * Y >> 31 */ -#define iANDXOR 0x08 /* R = (A & X) ^ Y */ -#define iTSTNEG 0x09 /* R = (A >= Y) ? X : ~X */ -#define iLIMITGE 0x0a /* R = (A >= Y) ? X : Y */ -#define iLIMITLT 0x0b /* R = (A < Y) ? X : Y */ -#define iLOG 0x0c /* R = linear_data, A (log_data), X (max_exp), Y (format_word) */ -#define iEXP 0x0d /* R = log_data, A (linear_data), X (max_exp), Y (format_word) */ -#define iINTERP 0x0e /* R = A + (X * (Y - A) >> 31) ; saturation */ -#define iSKIP 0x0f /* R = A (cc_reg), X (count), Y (cc_test) */ - -/* GPRs */ -#define FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x0f */ -#define EXTIN(x) (0x10 + (x)) /* x = 0x00 - 0x0f */ -#define EXTOUT(x) (0x20 + (x)) /* x = 0x00 - 0x0f */ -#define C_00000000 0x40 -#define C_00000001 0x41 -#define C_00000002 0x42 -#define C_00000003 0x43 -#define C_00000004 0x44 -#define C_00000008 0x45 -#define C_00000010 0x46 -#define C_00000020 0x47 -#define C_00000100 0x48 -#define C_00010000 0x49 -#define C_00080000 0x4a -#define C_10000000 0x4b -#define C_20000000 0x4c -#define C_40000000 0x4d -#define C_80000000 0x4e -#define C_7fffffff 0x4f -#define C_ffffffff 0x50 -#define C_fffffffe 0x51 -#define C_c0000000 0x52 -#define C_4f1bbcdc 0x53 -#define C_5a7ef9db 0x54 -#define C_00100000 0x55 /* ?? */ -#define GPR_ACCU 0x56 /* ACCUM, accumulator */ -#define GPR_COND 0x57 /* CCR, condition register */ -#define GPR_NOISE0 0x58 /* noise source */ -#define GPR_NOISE1 0x59 /* noise source */ -#define GPR_IRQ 0x5a /* IRQ register */ -#define GPR_DBAC 0x5b /* TRAM Delay Base Address Counter */ -#define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ -#define ITRAM_DATA(x) (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ -#define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ -#define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ -#define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ - -#define A_FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x3f? */ -#define A_EXTIN(x) (0x40 + (x)) /* x = 0x00 - 0x1f? */ -#define A_EXTOUT(x) (0x60 + (x)) /* x = 0x00 - 0x1f? */ -#define A_GPR(x) (A_FXGPREGBASE + (x)) - -/* cc_reg constants */ -#define CC_REG_NORMALIZED C_00000001 -#define CC_REG_BORROW C_00000002 -#define CC_REG_MINUS C_00000004 -#define CC_REG_ZERO C_00000008 -#define CC_REG_SATURATE C_00000010 -#define CC_REG_NONZERO C_00000100 - -/* FX buses */ -#define FXBUS_PCM_LEFT 0x00 -#define FXBUS_PCM_RIGHT 0x01 -#define FXBUS_PCM_LEFT_REAR 0x02 -#define FXBUS_PCM_RIGHT_REAR 0x03 -#define FXBUS_MIDI_LEFT 0x04 -#define FXBUS_MIDI_RIGHT 0x05 -#define FXBUS_PCM_CENTER 0x06 -#define FXBUS_PCM_LFE 0x07 -#define FXBUS_PCM_LEFT_FRONT 0x08 -#define FXBUS_PCM_RIGHT_FRONT 0x09 -#define FXBUS_MIDI_REVERB 0x0c -#define FXBUS_MIDI_CHORUS 0x0d -#define FXBUS_PCM_LEFT_SIDE 0x0e -#define FXBUS_PCM_RIGHT_SIDE 0x0f -#define FXBUS_PT_LEFT 0x14 -#define FXBUS_PT_RIGHT 0x15 - -/* Inputs */ -#define EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ -#define EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ -#define EXTIN_SPDIF_CD_L 0x02 /* internal S/PDIF CD - onboard - left */ -#define EXTIN_SPDIF_CD_R 0x03 /* internal S/PDIF CD - onboard - right */ -#define EXTIN_ZOOM_L 0x04 /* Zoom Video I2S - left */ -#define EXTIN_ZOOM_R 0x05 /* Zoom Video I2S - right */ -#define EXTIN_TOSLINK_L 0x06 /* LiveDrive - TOSLink Optical - left */ -#define EXTIN_TOSLINK_R 0x07 /* LiveDrive - TOSLink Optical - right */ -#define EXTIN_LINE1_L 0x08 /* LiveDrive - Line/Mic 1 - left */ -#define EXTIN_LINE1_R 0x09 /* LiveDrive - Line/Mic 1 - right */ -#define EXTIN_COAX_SPDIF_L 0x0a /* LiveDrive - Coaxial S/PDIF - left */ -#define EXTIN_COAX_SPDIF_R 0x0b /* LiveDrive - Coaxial S/PDIF - right */ -#define EXTIN_LINE2_L 0x0c /* LiveDrive - Line/Mic 2 - left */ -#define EXTIN_LINE2_R 0x0d /* LiveDrive - Line/Mic 2 - right */ - -/* Outputs */ -#define EXTOUT_AC97_L 0x00 /* AC'97 playback channel - left */ -#define EXTOUT_AC97_R 0x01 /* AC'97 playback channel - right */ -#define EXTOUT_TOSLINK_L 0x02 /* LiveDrive - TOSLink Optical - left */ -#define EXTOUT_TOSLINK_R 0x03 /* LiveDrive - TOSLink Optical - right */ -#define EXTOUT_AC97_CENTER 0x04 /* SB Live 5.1 - center */ -#define EXTOUT_AC97_LFE 0x05 /* SB Live 5.1 - LFE */ -#define EXTOUT_HEADPHONE_L 0x06 /* LiveDrive - Headphone - left */ -#define EXTOUT_HEADPHONE_R 0x07 /* LiveDrive - Headphone - right */ -#define EXTOUT_REAR_L 0x08 /* Rear channel - left */ -#define EXTOUT_REAR_R 0x09 /* Rear channel - right */ -#define EXTOUT_ADC_CAP_L 0x0a /* ADC Capture buffer - left */ -#define EXTOUT_ADC_CAP_R 0x0b /* ADC Capture buffer - right */ -#define EXTOUT_MIC_CAP 0x0c /* MIC Capture buffer */ -#define EXTOUT_AC97_REAR_L 0x0d /* SB Live 5.1 (c) 2003 - Rear Left */ -#define EXTOUT_AC97_REAR_R 0x0e /* SB Live 5.1 (c) 2003 - Rear Right */ -#define EXTOUT_ACENTER 0x11 /* Analog Center */ -#define EXTOUT_ALFE 0x12 /* Analog LFE */ - -/* Audigy Inputs */ -#define A_EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ -#define A_EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ -#define A_EXTIN_SPDIF_CD_L 0x02 /* digital CD left */ -#define A_EXTIN_SPDIF_CD_R 0x03 /* digital CD left */ -#define A_EXTIN_OPT_SPDIF_L 0x04 /* audigy drive Optical SPDIF - left */ -#define A_EXTIN_OPT_SPDIF_R 0x05 /* right */ -#define A_EXTIN_LINE2_L 0x08 /* audigy drive line2/mic2 - left */ -#define A_EXTIN_LINE2_R 0x09 /* right */ -#define A_EXTIN_ADC_L 0x0a /* Philips ADC - left */ -#define A_EXTIN_ADC_R 0x0b /* right */ -#define A_EXTIN_AUX2_L 0x0c /* audigy drive aux2 - left */ -#define A_EXTIN_AUX2_R 0x0d /* - right */ - -/* Audigiy Outputs */ -#define A_EXTOUT_FRONT_L 0x00 /* digital front left */ -#define A_EXTOUT_FRONT_R 0x01 /* right */ -#define A_EXTOUT_CENTER 0x02 /* digital front center */ -#define A_EXTOUT_LFE 0x03 /* digital front lfe */ -#define A_EXTOUT_HEADPHONE_L 0x04 /* headphone audigy drive left */ -#define A_EXTOUT_HEADPHONE_R 0x05 /* right */ -#define A_EXTOUT_REAR_L 0x06 /* digital rear left */ -#define A_EXTOUT_REAR_R 0x07 /* right */ -#define A_EXTOUT_AFRONT_L 0x08 /* analog front left */ -#define A_EXTOUT_AFRONT_R 0x09 /* right */ -#define A_EXTOUT_ACENTER 0x0a /* analog center */ -#define A_EXTOUT_ALFE 0x0b /* analog LFE */ -#define A_EXTOUT_ASIDE_L 0x0c /* analog side left - Audigy 2 ZS */ -#define A_EXTOUT_ASIDE_R 0x0d /* right - Audigy 2 ZS */ -#define A_EXTOUT_AREAR_L 0x0e /* analog rear left */ -#define A_EXTOUT_AREAR_R 0x0f /* right */ -#define A_EXTOUT_AC97_L 0x10 /* AC97 left (front) */ -#define A_EXTOUT_AC97_R 0x11 /* right */ -#define A_EXTOUT_ADC_CAP_L 0x16 /* ADC capture buffer left */ -#define A_EXTOUT_ADC_CAP_R 0x17 /* right */ -#define A_EXTOUT_MIC_CAP 0x18 /* Mic capture buffer */ - -/* Audigy constants */ -#define A_C_00000000 0xc0 -#define A_C_00000001 0xc1 -#define A_C_00000002 0xc2 -#define A_C_00000003 0xc3 -#define A_C_00000004 0xc4 -#define A_C_00000008 0xc5 -#define A_C_00000010 0xc6 -#define A_C_00000020 0xc7 -#define A_C_00000100 0xc8 -#define A_C_00010000 0xc9 -#define A_C_00000800 0xca -#define A_C_10000000 0xcb -#define A_C_20000000 0xcc -#define A_C_40000000 0xcd -#define A_C_80000000 0xce -#define A_C_7fffffff 0xcf -#define A_C_ffffffff 0xd0 -#define A_C_fffffffe 0xd1 -#define A_C_c0000000 0xd2 -#define A_C_4f1bbcdc 0xd3 -#define A_C_5a7ef9db 0xd4 -#define A_C_00100000 0xd5 -#define A_GPR_ACCU 0xd6 /* ACCUM, accumulator */ -#define A_GPR_COND 0xd7 /* CCR, condition register */ -#define A_GPR_NOISE0 0xd8 /* noise source */ -#define A_GPR_NOISE1 0xd9 /* noise source */ -#define A_GPR_IRQ 0xda /* IRQ register */ -#define A_GPR_DBAC 0xdb /* TRAM Delay Base Address Counter - internal */ -#define A_GPR_DBACE 0xde /* TRAM Delay Base Address Counter - external */ - -/* definitions for debug register */ -#define EMU10K1_DBG_ZC 0x80000000 /* zero tram counter */ -#define EMU10K1_DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ -#define EMU10K1_DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ -#define EMU10K1_DBG_SINGLE_STEP 0x00008000 /* single step mode */ -#define EMU10K1_DBG_STEP 0x00004000 /* start single step */ -#define EMU10K1_DBG_CONDITION_CODE 0x00003e00 /* condition code */ -#define EMU10K1_DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ - -/* tank memory address line */ -#ifndef __KERNEL__ -#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ -#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ -#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ -#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ -#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ -#endif - -typedef struct { - unsigned int internal_tram_size; /* in samples */ - unsigned int external_tram_size; /* in samples */ - char fxbus_names[16][32]; /* names of FXBUSes */ - char extin_names[16][32]; /* names of external inputs */ - char extout_names[32][32]; /* names of external outputs */ - unsigned int gpr_controls; /* count of GPR controls */ -} emu10k1_fx8010_info_t; - -#define EMU10K1_GPR_TRANSLATION_NONE 0 -#define EMU10K1_GPR_TRANSLATION_TABLE100 1 -#define EMU10K1_GPR_TRANSLATION_BASS 2 -#define EMU10K1_GPR_TRANSLATION_TREBLE 3 -#define EMU10K1_GPR_TRANSLATION_ONOFF 4 - -enum emu10k1_ctl_elem_iface { - EMU10K1_CTL_ELEM_IFACE_MIXER = 2, /* virtual mixer device */ - EMU10K1_CTL_ELEM_IFACE_PCM = 3, /* PCM device */ -}; - -typedef struct { - unsigned int pad; /* don't use */ - int iface; /* interface identifier */ - unsigned int device; /* device/client number */ - unsigned int subdevice; /* subdevice (substream) number */ - unsigned char name[44]; /* ASCII name of item */ - unsigned int index; /* index of item */ -} emu10k1_ctl_elem_id_t; - -typedef struct { - emu10k1_ctl_elem_id_t id; /* full control ID definition */ - unsigned int vcount; /* visible count */ - unsigned int count; /* count of GPR (1..16) */ - unsigned short gpr[32]; /* GPR number(s) */ - unsigned int value[32]; /* initial values */ - unsigned int min; /* minimum range */ - unsigned int max; /* maximum range */ - unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */ - unsigned int *tlv; -} emu10k1_fx8010_control_gpr_t; - -typedef struct { - char name[128]; - - unsigned long gpr_valid[0x200/(sizeof(unsigned long)*8)]; /* bitmask of valid initializers */ - uint32_t *gpr_map; /* initializers */ - - unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */ - emu10k1_fx8010_control_gpr_t *gpr_add_controls; /* GPR controls to add/replace */ - - unsigned int gpr_del_control_count; /* count of GPR controls to remove */ - emu10k1_ctl_elem_id_t *gpr_del_controls; /* IDs of GPR controls to remove */ - - unsigned int gpr_list_control_count; /* count of GPR controls to list */ - unsigned int gpr_list_control_total; /* total count of GPR controls */ - emu10k1_fx8010_control_gpr_t *gpr_list_controls; /* listed GPR controls */ - - unsigned long tram_valid[0x100/(sizeof(unsigned long)*8)]; /* bitmask of valid initializers */ - uint32_t *tram_data_map; /* data initializers */ - uint32_t *tram_addr_map; /* map initializers */ - - unsigned long code_valid[1024/(sizeof(unsigned long)*8)]; /* bitmask of valid instructions */ - uint32_t *code; /* one instruction - 64 bits */ -} emu10k1_fx8010_code_t; - -typedef struct { - unsigned int address; /* 31.bit == 1 -> external TRAM */ - unsigned int size; /* size in samples (4 bytes) */ - unsigned int *samples; /* pointer to samples (20-bit) */ - /* NULL->clear memory */ -} emu10k1_fx8010_tram_t; - -typedef struct { - unsigned int substream; /* substream number */ - unsigned int res1; /* reserved */ - unsigned int channels; /* 16-bit channels count, zero = remove this substream */ - unsigned int tram_start; /* ring buffer position in TRAM (in samples) */ - unsigned int buffer_size; /* count of buffered samples */ - unsigned short gpr_size; /* GPR containing size of ringbuffer in samples (host) */ - unsigned short gpr_ptr; /* GPR containing current pointer in the ring buffer (host = reset, FX8010) */ - unsigned short gpr_count; /* GPR containing count of samples between two interrupts (host) */ - unsigned short gpr_tmpcount; /* GPR containing current count of samples to interrupt (host = set, FX8010) */ - unsigned short gpr_trigger; /* GPR containing trigger (activate) information (host) */ - unsigned short gpr_running; /* GPR containing info if PCM is running (FX8010) */ - unsigned char pad; /* reserved */ - unsigned char etram[32]; /* external TRAM address & data (one per channel) */ - unsigned int res2; /* reserved */ -} emu10k1_fx8010_pcm_t; - -#define SNDRV_EMU10K1_IOCTL_INFO _IOR ('H', 0x10, emu10k1_fx8010_info_t) -#define SNDRV_EMU10K1_IOCTL_CODE_POKE _IOW ('H', 0x11, emu10k1_fx8010_code_t) -#define SNDRV_EMU10K1_IOCTL_CODE_PEEK _IOWR('H', 0x12, emu10k1_fx8010_code_t) -#define SNDRV_EMU10K1_IOCTL_TRAM_SETUP _IOW ('H', 0x20, int) -#define SNDRV_EMU10K1_IOCTL_TRAM_POKE _IOW ('H', 0x21, emu10k1_fx8010_tram_t) -#define SNDRV_EMU10K1_IOCTL_TRAM_PEEK _IOWR('H', 0x22, emu10k1_fx8010_tram_t) -#define SNDRV_EMU10K1_IOCTL_PCM_POKE _IOW ('H', 0x30, emu10k1_fx8010_pcm_t) -#define SNDRV_EMU10K1_IOCTL_PCM_PEEK _IOWR('H', 0x31, emu10k1_fx8010_pcm_t) -#define SNDRV_EMU10K1_IOCTL_PVERSION _IOR ('H', 0x40, int) -#define SNDRV_EMU10K1_IOCTL_STOP _IO ('H', 0x80) -#define SNDRV_EMU10K1_IOCTL_CONTINUE _IO ('H', 0x81) -#define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82) -#define SNDRV_EMU10K1_IOCTL_SINGLE_STEP _IOW ('H', 0x83, int) -#define SNDRV_EMU10K1_IOCTL_DBG_READ _IOR ('H', 0x84, int) - -#endif /* __SOUND_EMU10K1_H */ diff --git a/raylib/external/alsa/sound/hdsp.h b/raylib/external/alsa/sound/hdsp.h deleted file mode 100644 index 5adaf7b..0000000 --- a/raylib/external/alsa/sound/hdsp.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef __SOUND_HDSP_H -#define __SOUND_HDSP_H - -/* - * Copyright (C) 2003 Thomas Charbonnel (thomas@undata.org) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include - -#define HDSP_MATRIX_MIXER_SIZE 2048 - -typedef enum { - Digiface, - Multiface, - H9652, - H9632, - RPM, - Undefined, -} HDSP_IO_Type; - -typedef struct _snd_hdsp_peak_rms hdsp_peak_rms_t; - -struct _snd_hdsp_peak_rms { - uint32_t input_peaks[26]; - uint32_t playback_peaks[26]; - uint32_t output_peaks[28]; - uint64_t input_rms[26]; - uint64_t playback_rms[26]; - /* These are only used for H96xx cards */ - uint64_t output_rms[26]; -}; - -#define SNDRV_HDSP_IOCTL_GET_PEAK_RMS _IOR('H', 0x40, hdsp_peak_rms_t) - -typedef struct _snd_hdsp_config_info hdsp_config_info_t; - -struct _snd_hdsp_config_info { - unsigned char pref_sync_ref; - unsigned char wordclock_sync_check; - unsigned char spdif_sync_check; - unsigned char adatsync_sync_check; - unsigned char adat_sync_check[3]; - unsigned char spdif_in; - unsigned char spdif_out; - unsigned char spdif_professional; - unsigned char spdif_emphasis; - unsigned char spdif_nonaudio; - unsigned int spdif_sample_rate; - unsigned int system_sample_rate; - unsigned int autosync_sample_rate; - unsigned char system_clock_mode; - unsigned char clock_source; - unsigned char autosync_ref; - unsigned char line_out; - unsigned char passthru; - unsigned char da_gain; - unsigned char ad_gain; - unsigned char phone_gain; - unsigned char xlr_breakout_cable; - unsigned char analog_extension_board; -}; - -#define SNDRV_HDSP_IOCTL_GET_CONFIG_INFO _IOR('H', 0x41, hdsp_config_info_t) - -typedef struct _snd_hdsp_firmware hdsp_firmware_t; - -struct _snd_hdsp_firmware { - void *firmware_data; /* 24413 x 4 bytes */ -}; - -#define SNDRV_HDSP_IOCTL_UPLOAD_FIRMWARE _IOW('H', 0x42, hdsp_firmware_t) - -typedef struct _snd_hdsp_version hdsp_version_t; - -struct _snd_hdsp_version { - HDSP_IO_Type io_type; - unsigned short firmware_rev; -}; - -#define SNDRV_HDSP_IOCTL_GET_VERSION _IOR('H', 0x43, hdsp_version_t) - -typedef struct _snd_hdsp_mixer hdsp_mixer_t; - -struct _snd_hdsp_mixer { - unsigned short matrix[HDSP_MATRIX_MIXER_SIZE]; -}; - -#define SNDRV_HDSP_IOCTL_GET_MIXER _IOR('H', 0x44, hdsp_mixer_t) - -typedef struct _snd_hdsp_9632_aeb hdsp_9632_aeb_t; - -struct _snd_hdsp_9632_aeb { - int aebi; - int aebo; -}; - -#define SNDRV_HDSP_IOCTL_GET_9632_AEB _IOR('H', 0x45, hdsp_9632_aeb_t) - -#endif /* __SOUND_HDSP_H */ diff --git a/raylib/external/alsa/sound/hdspm.h b/raylib/external/alsa/sound/hdspm.h deleted file mode 100644 index fe9c5f6..0000000 --- a/raylib/external/alsa/sound/hdspm.h +++ /dev/null @@ -1,229 +0,0 @@ -#ifndef __SOUND_HDSPM_H -#define __SOUND_HDSPM_H -/* - * Copyright (C) 2003 Winfried Ritsch (IEM) - * based on hdsp.h from Thomas Charbonnel (thomas@undata.org) - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */ -#define HDSPM_MAX_CHANNELS 64 - -enum hdspm_io_type { - MADI, - MADIface, - AIO, - AES32, - RayDAT -}; - -enum hdspm_speed { - ss, - ds, - qs -}; - -/* -------------------- IOCTL Peak/RMS Meters -------------------- */ - -struct hdspm_peak_rms { - uint32_t input_peaks[64]; - uint32_t playback_peaks[64]; - uint32_t output_peaks[64]; - - uint64_t input_rms[64]; - uint64_t playback_rms[64]; - uint64_t output_rms[64]; - - uint8_t speed; /* enum {ss, ds, qs} */ - int status2; -}; - -#define SNDRV_HDSPM_IOCTL_GET_PEAK_RMS \ - _IOR('H', 0x42, struct hdspm_peak_rms) - -/* ------------ CONFIG block IOCTL ---------------------- */ - -struct hdspm_config { - unsigned char pref_sync_ref; - unsigned char wordclock_sync_check; - unsigned char madi_sync_check; - unsigned int system_sample_rate; - unsigned int autosync_sample_rate; - unsigned char system_clock_mode; - unsigned char clock_source; - unsigned char autosync_ref; - unsigned char line_out; - unsigned int passthru; - unsigned int analog_out; -}; - -#define SNDRV_HDSPM_IOCTL_GET_CONFIG \ - _IOR('H', 0x41, struct hdspm_config) - -/** - * If there's a TCO (TimeCode Option) board installed, - * there are further options and status data available. - * The hdspm_ltc structure contains the current SMPTE - * timecode and some status information and can be - * obtained via SNDRV_HDSPM_IOCTL_GET_LTC or in the - * hdspm_status struct. - **/ - -enum hdspm_ltc_format { - format_invalid, - fps_24, - fps_25, - fps_2997, - fps_30 -}; - -enum hdspm_ltc_frame { - frame_invalid, - drop_frame, - full_frame -}; - -enum hdspm_ltc_input_format { - ntsc, - pal, - no_video -}; - -struct hdspm_ltc { - unsigned int ltc; - - enum hdspm_ltc_format format; - enum hdspm_ltc_frame frame; - enum hdspm_ltc_input_format input_format; -}; - -#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_ltc) - -/** - * The status data reflects the device's current state - * as determined by the card's configuration and - * connection status. - **/ - -enum hdspm_sync { - hdspm_sync_no_lock = 0, - hdspm_sync_lock = 1, - hdspm_sync_sync = 2 -}; - -enum hdspm_madi_input { - hdspm_input_optical = 0, - hdspm_input_coax = 1 -}; - -enum hdspm_madi_channel_format { - hdspm_format_ch_64 = 0, - hdspm_format_ch_56 = 1 -}; - -enum hdspm_madi_frame_format { - hdspm_frame_48 = 0, - hdspm_frame_96 = 1 -}; - -enum hdspm_syncsource { - syncsource_wc = 0, - syncsource_madi = 1, - syncsource_tco = 2, - syncsource_sync = 3, - syncsource_none = 4 -}; - -struct hdspm_status { - uint8_t card_type; /* enum hdspm_io_type */ - enum hdspm_syncsource autosync_source; - - uint64_t card_clock; - uint32_t master_period; - - union { - struct { - uint8_t sync_wc; /* enum hdspm_sync */ - uint8_t sync_madi; /* enum hdspm_sync */ - uint8_t sync_tco; /* enum hdspm_sync */ - uint8_t sync_in; /* enum hdspm_sync */ - uint8_t madi_input; /* enum hdspm_madi_input */ - uint8_t channel_format; /* enum hdspm_madi_channel_format */ - uint8_t frame_format; /* enum hdspm_madi_frame_format */ - } madi; - } card_specific; -}; - -#define SNDRV_HDSPM_IOCTL_GET_STATUS \ - _IOR('H', 0x47, struct hdspm_status) - -/** - * Get information about the card and its add-ons. - **/ - -#define HDSPM_ADDON_TCO 1 - -struct hdspm_version { - uint8_t card_type; /* enum hdspm_io_type */ - char cardname[20]; - unsigned int serial; - unsigned short firmware_rev; - int addons; -}; - -#define SNDRV_HDSPM_IOCTL_GET_VERSION _IOR('H', 0x48, struct hdspm_version) - -/* ------------- get Matrix Mixer IOCTL --------------- */ - -/* MADI mixer: 64inputs+64playback in 64outputs = 8192 => *4Byte = - * 32768 Bytes - */ - -/* organisation is 64 channelfader in a continous memory block */ -/* equivalent to hardware definition, maybe for future feature of mmap of - * them - */ -/* each of 64 outputs has 64 infader and 64 outfader: - Ins to Outs mixer[out].in[in], Outstreams to Outs mixer[out].pb[pb] */ - -#define HDSPM_MIXER_CHANNELS HDSPM_MAX_CHANNELS - -struct hdspm_channelfader { - unsigned int in[HDSPM_MIXER_CHANNELS]; - unsigned int pb[HDSPM_MIXER_CHANNELS]; -}; - -struct hdspm_mixer { - struct hdspm_channelfader ch[HDSPM_MIXER_CHANNELS]; -}; - -struct hdspm_mixer_ioctl { - struct hdspm_mixer *mixer; -}; - -/* use indirect access due to the limit of ioctl bit size */ -#define SNDRV_HDSPM_IOCTL_GET_MIXER _IOR('H', 0x44, struct hdspm_mixer_ioctl) - -/* typedefs for compatibility to user-space */ -typedef struct hdspm_peak_rms hdspm_peak_rms_t; -typedef struct hdspm_config_info hdspm_config_info_t; -typedef struct hdspm_version hdspm_version_t; -typedef struct hdspm_channelfader snd_hdspm_channelfader_t; -typedef struct hdspm_mixer hdspm_mixer_t; - - -#endif diff --git a/raylib/external/alsa/sound/sb16_csp.h b/raylib/external/alsa/sound/sb16_csp.h deleted file mode 100644 index 78817b4..0000000 --- a/raylib/external/alsa/sound/sb16_csp.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef __SOUND_SB16_CSP_H -#define __SOUND_SB16_CSP_H - -/* - * Copyright (c) 1999 by Uros Bizjak - * Takashi Iwai - * - * SB16ASP/AWE32 CSP control - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* CSP modes */ -#define SNDRV_SB_CSP_MODE_NONE 0x00 -#define SNDRV_SB_CSP_MODE_DSP_READ 0x01 /* Record from DSP */ -#define SNDRV_SB_CSP_MODE_DSP_WRITE 0x02 /* Play to DSP */ -#define SNDRV_SB_CSP_MODE_QSOUND 0x04 /* QSound */ - -/* CSP load flags */ -#define SNDRV_SB_CSP_LOAD_FROMUSER 0x01 -#define SNDRV_SB_CSP_LOAD_INITBLOCK 0x02 - -/* CSP sample width */ -#define SNDRV_SB_CSP_SAMPLE_8BIT 0x01 -#define SNDRV_SB_CSP_SAMPLE_16BIT 0x02 - -/* CSP channels */ -#define SNDRV_SB_CSP_MONO 0x01 -#define SNDRV_SB_CSP_STEREO 0x02 - -/* CSP rates */ -#define SNDRV_SB_CSP_RATE_8000 0x01 -#define SNDRV_SB_CSP_RATE_11025 0x02 -#define SNDRV_SB_CSP_RATE_22050 0x04 -#define SNDRV_SB_CSP_RATE_44100 0x08 -#define SNDRV_SB_CSP_RATE_ALL 0x0f - -/* CSP running state */ -#define SNDRV_SB_CSP_ST_IDLE 0x00 -#define SNDRV_SB_CSP_ST_LOADED 0x01 -#define SNDRV_SB_CSP_ST_RUNNING 0x02 -#define SNDRV_SB_CSP_ST_PAUSED 0x04 -#define SNDRV_SB_CSP_ST_AUTO 0x08 -#define SNDRV_SB_CSP_ST_QSOUND 0x10 - -/* maximum QSound value (180 degrees right) */ -#define SNDRV_SB_CSP_QSOUND_MAX_RIGHT 0x20 - -/* maximum microcode RIFF file size */ -#define SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE 0x3000 - -/* microcode header */ -typedef struct snd_sb_csp_mc_header { - char codec_name[16]; /* id name of codec */ - unsigned short func_req; /* requested function */ -} snd_sb_csp_mc_header_t; - -/* microcode to be loaded */ -typedef struct snd_sb_csp_microcode { - snd_sb_csp_mc_header_t info; - unsigned char data[SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE]; -} snd_sb_csp_microcode_t; - -/* start CSP with sample_width in mono/stereo */ -typedef struct snd_sb_csp_start { - int sample_width; /* sample width, look above */ - int channels; /* channels, look above */ -} snd_sb_csp_start_t; - -/* CSP information */ -typedef struct snd_sb_csp_info { - char codec_name[16]; /* id name of codec */ - unsigned short func_nr; /* function number */ - unsigned int acc_format; /* accepted PCM formats */ - unsigned short acc_channels; /* accepted channels */ - unsigned short acc_width; /* accepted sample width */ - unsigned short acc_rates; /* accepted sample rates */ - unsigned short csp_mode; /* CSP mode, see above */ - unsigned short run_channels; /* current channels */ - unsigned short run_width; /* current sample width */ - unsigned short version; /* version id: 0x10 - 0x1f */ - unsigned short state; /* state bits */ -} snd_sb_csp_info_t; - -/* HWDEP controls */ -/* get CSP information */ -#define SNDRV_SB_CSP_IOCTL_INFO _IOR('H', 0x10, snd_sb_csp_info_t) -/* load microcode to CSP */ -#define SNDRV_SB_CSP_IOCTL_LOAD_CODE _IOW('H', 0x11, snd_sb_csp_microcode_t) -/* unload microcode from CSP */ -#define SNDRV_SB_CSP_IOCTL_UNLOAD_CODE _IO('H', 0x12) -/* start CSP */ -#define SNDRV_SB_CSP_IOCTL_START _IOW('H', 0x13, snd_sb_csp_start_t) -/* stop CSP */ -#define SNDRV_SB_CSP_IOCTL_STOP _IO('H', 0x14) -/* pause CSP and DMA transfer */ -#define SNDRV_SB_CSP_IOCTL_PAUSE _IO('H', 0x15) -/* restart CSP and DMA transfer */ -#define SNDRV_SB_CSP_IOCTL_RESTART _IO('H', 0x16) - - -#endif /* __SOUND_SB16_CSP */ diff --git a/raylib/external/alsa/sound/sscape_ioctl.h b/raylib/external/alsa/sound/sscape_ioctl.h deleted file mode 100644 index c6653eb..0000000 --- a/raylib/external/alsa/sound/sscape_ioctl.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef SSCAPE_IOCTL_H -#define SSCAPE_IOCTL_H - - -struct sscape_bootblock -{ - unsigned char code[256]; - unsigned version; -}; - -#define SSCAPE_MICROCODE_SIZE 65536 - -struct sscape_microcode -{ - unsigned char *code; -}; - -#define SND_SSCAPE_LOAD_BOOTB _IOWR('P', 100, struct sscape_bootblock) -#define SND_SSCAPE_LOAD_MCODE _IOW ('P', 101, struct sscape_microcode) - -#endif diff --git a/raylib/external/alsa/sound/tlv.h b/raylib/external/alsa/sound/tlv.h deleted file mode 100644 index b4df440..0000000 --- a/raylib/external/alsa/sound/tlv.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __UAPI_SOUND_TLV_H -#define __UAPI_SOUND_TLV_H - -#define SNDRV_CTL_TLVT_CONTAINER 0 /* one level down - group of TLVs */ -#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */ -#define SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */ -#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */ -#define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */ -#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */ - -/* - * channel-mapping TLV items - * TLV length must match with num_channels - */ -#define SNDRV_CTL_TLVT_CHMAP_FIXED 0x101 /* fixed channel position */ -#define SNDRV_CTL_TLVT_CHMAP_VAR 0x102 /* channels freely swappable */ -#define SNDRV_CTL_TLVT_CHMAP_PAIRED 0x103 /* pair-wise swappable */ - -/* - * TLV structure is right behind the struct snd_ctl_tlv: - * unsigned int type - see SNDRV_CTL_TLVT_* - * unsigned int length - * .... data aligned to sizeof(unsigned int), use - * block_length = (length + (sizeof(unsigned int) - 1)) & - * ~(sizeof(unsigned int) - 1)) .... - */ -#define SNDRV_CTL_TLVD_ITEM(type, ...) \ - (type), SNDRV_CTL_TLVD_LENGTH(__VA_ARGS__), __VA_ARGS__ -#define SNDRV_CTL_TLVD_LENGTH(...) \ - ((unsigned int)sizeof((const unsigned int[]) { __VA_ARGS__ })) - -#define SNDRV_CTL_TLVD_CONTAINER_ITEM(...) \ - SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_CONTAINER, __VA_ARGS__) -#define SNDRV_CTL_TLVD_DECLARE_CONTAINER(name, ...) \ - unsigned int name[] = { \ - SNDRV_CTL_TLVD_CONTAINER_ITEM(__VA_ARGS__) \ - } - -#define SNDRV_CTL_TLVD_DB_SCALE_MASK 0xffff -#define SNDRV_CTL_TLVD_DB_SCALE_MUTE 0x10000 -#define SNDRV_CTL_TLVD_DB_SCALE_ITEM(min, step, mute) \ - SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_DB_SCALE, \ - (min), \ - ((step) & SNDRV_CTL_TLVD_DB_SCALE_MASK) | \ - ((mute) ? SNDRV_CTL_TLVD_DB_SCALE_MUTE : 0)) -#define SNDRV_CTL_TLVD_DECLARE_DB_SCALE(name, min, step, mute) \ - unsigned int name[] = { \ - SNDRV_CTL_TLVD_DB_SCALE_ITEM(min, step, mute) \ - } - -/* dB scale specified with min/max values instead of step */ -#define SNDRV_CTL_TLVD_DB_MINMAX_ITEM(min_dB, max_dB) \ - SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_DB_MINMAX, (min_dB), (max_dB)) -#define SNDRV_CTL_TLVD_DB_MINMAX_MUTE_ITEM(min_dB, max_dB) \ - SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_DB_MINMAX_MUTE, (min_dB), (max_dB)) -#define SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(name, min_dB, max_dB) \ - unsigned int name[] = { \ - SNDRV_CTL_TLVD_DB_MINMAX_ITEM(min_dB, max_dB) \ - } -#define SNDRV_CTL_TLVD_DECLARE_DB_MINMAX_MUTE(name, min_dB, max_dB) \ - unsigned int name[] = { \ - SNDRV_CTL_TLVD_DB_MINMAX_MUTE_ITEM(min_dB, max_dB) \ - } - -/* linear volume between min_dB and max_dB (.01dB unit) */ -#define SNDRV_CTL_TLVD_DB_LINEAR_ITEM(min_dB, max_dB) \ - SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_DB_LINEAR, (min_dB), (max_dB)) -#define SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(name, min_dB, max_dB) \ - unsigned int name[] = { \ - SNDRV_CTL_TLVD_DB_LINEAR_ITEM(min_dB, max_dB) \ - } - -/* dB range container: - * Items in dB range container must be ordered by their values and by their - * dB values. This implies that larger values must correspond with larger - * dB values (which is also required for all other mixer controls). - */ -/* Each item is: */ -#define SNDRV_CTL_TLVD_DB_RANGE_ITEM(...) \ - SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_DB_RANGE, __VA_ARGS__) -#define SNDRV_CTL_TLVD_DECLARE_DB_RANGE(name, ...) \ - unsigned int name[] = { \ - SNDRV_CTL_TLVD_DB_RANGE_ITEM(__VA_ARGS__) \ - } - -#define SNDRV_CTL_TLVD_DB_GAIN_MUTE -9999999 - -#endif diff --git a/raylib/external/alsa/sound/type_compat.h b/raylib/external/alsa/sound/type_compat.h deleted file mode 100644 index e973ff3..0000000 --- a/raylib/external/alsa/sound/type_compat.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __TYPE_COMPAT_H -#define __TYPE_COMPAT_H - -#ifndef DOC_HIDDEN -#include -typedef uint8_t __u8; -typedef uint16_t __u16; -typedef uint32_t __u32; -typedef int8_t __s8; -typedef int16_t __s16; -typedef int32_t __s32; - -#include -#include -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define __cpu_to_le32(x) (x) -#define __cpu_to_be32(x) bswap_32(x) -#define __cpu_to_le16(x) (x) -#define __cpu_to_be16(x) bswap_16(x) -#else -#define __cpu_to_le32(x) bswap_32(x) -#define __cpu_to_be32(x) (x) -#define __cpu_to_le16(x) bswap_16(x) -#define __cpu_to_be16(x) (x) -#endif - -#define __le32_to_cpu __cpu_to_le32 -#define __be32_to_cpu __cpu_to_be32 -#define __le16_to_cpu __cpu_to_le16 -#define __be16_to_cpu __cpu_to_be16 - -#define __le64 __u64 -#define __le32 __u32 -#define __le16 __u16 -#define __le8 __u8 -#define __be64 __u64 -#define __be32 __u32 -#define __be16 __u16 -#define __be8 __u8 -#endif /* DOC_HIDDEN */ - -#endif /* __TYPE_COMPAT_H */ diff --git a/raylib/external/alsa/timer.h b/raylib/external/alsa/timer.h deleted file mode 100644 index 32d9b89..0000000 --- a/raylib/external/alsa/timer.h +++ /dev/null @@ -1,259 +0,0 @@ -/** - * \file include/timer.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __ALSA_TIMER_H -#define __ALSA_TIMER_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup Timer Timer Interface - * Timer Interface. See \ref timer page for more details. - * \{ - */ - -/** dlsym version for interface entry callback */ -#define SND_TIMER_DLSYM_VERSION _dlsym_timer_001 -/** dlsym version for interface entry callback */ -#define SND_TIMER_QUERY_DLSYM_VERSION _dlsym_timer_query_001 - -/** timer identification structure */ -typedef struct _snd_timer_id snd_timer_id_t; -/** timer global info structure */ -typedef struct _snd_timer_ginfo snd_timer_ginfo_t; -/** timer global params structure */ -typedef struct _snd_timer_gparams snd_timer_gparams_t; -/** timer global status structure */ -typedef struct _snd_timer_gstatus snd_timer_gstatus_t; -/** timer info structure */ -typedef struct _snd_timer_info snd_timer_info_t; -/** timer params structure */ -typedef struct _snd_timer_params snd_timer_params_t; -/** timer status structure */ -typedef struct _snd_timer_status snd_timer_status_t; -/** timer master class */ -typedef enum _snd_timer_class { - SND_TIMER_CLASS_NONE = -1, /**< invalid */ - SND_TIMER_CLASS_SLAVE = 0, /**< slave timer */ - SND_TIMER_CLASS_GLOBAL, /**< global timer */ - SND_TIMER_CLASS_CARD, /**< card timer */ - SND_TIMER_CLASS_PCM, /**< PCM timer */ - SND_TIMER_CLASS_LAST = SND_TIMER_CLASS_PCM /**< last timer */ -} snd_timer_class_t; - -/** timer slave class */ -typedef enum _snd_timer_slave_class { - SND_TIMER_SCLASS_NONE = 0, /**< none */ - SND_TIMER_SCLASS_APPLICATION, /**< for internal use */ - SND_TIMER_SCLASS_SEQUENCER, /**< sequencer timer */ - SND_TIMER_SCLASS_OSS_SEQUENCER, /**< OSS sequencer timer */ - SND_TIMER_SCLASS_LAST = SND_TIMER_SCLASS_OSS_SEQUENCER /**< last slave timer */ -} snd_timer_slave_class_t; - -/** timer read event identification */ -typedef enum _snd_timer_event { - SND_TIMER_EVENT_RESOLUTION = 0, /* val = resolution in ns */ - SND_TIMER_EVENT_TICK, /* val = ticks */ - SND_TIMER_EVENT_START, /* val = resolution in ns */ - SND_TIMER_EVENT_STOP, /* val = 0 */ - SND_TIMER_EVENT_CONTINUE, /* val = resolution in ns */ - SND_TIMER_EVENT_PAUSE, /* val = 0 */ - SND_TIMER_EVENT_EARLY, /* val = 0 */ - SND_TIMER_EVENT_SUSPEND, /* val = 0 */ - SND_TIMER_EVENT_RESUME, /* val = resolution in ns */ - /* master timer events for slave timer instances */ - SND_TIMER_EVENT_MSTART = SND_TIMER_EVENT_START + 10, - SND_TIMER_EVENT_MSTOP = SND_TIMER_EVENT_STOP + 10, - SND_TIMER_EVENT_MCONTINUE = SND_TIMER_EVENT_CONTINUE + 10, - SND_TIMER_EVENT_MPAUSE = SND_TIMER_EVENT_PAUSE + 10, - SND_TIMER_EVENT_MSUSPEND = SND_TIMER_EVENT_SUSPEND + 10, - SND_TIMER_EVENT_MRESUME = SND_TIMER_EVENT_RESUME + 10 -} snd_timer_event_t; - -/** timer read structure */ -typedef struct _snd_timer_read { - unsigned int resolution; /**< tick resolution in nanoseconds */ - unsigned int ticks; /**< count of happened ticks */ -} snd_timer_read_t; - -/** timer tstamp + event read structure */ -typedef struct _snd_timer_tread { - snd_timer_event_t event; /**< Timer event */ - snd_htimestamp_t tstamp; /**< Time stamp of each event */ - unsigned int val; /**< Event value */ -} snd_timer_tread_t; - -/** global timer - system */ -#define SND_TIMER_GLOBAL_SYSTEM 0 -/** global timer - RTC */ -#define SND_TIMER_GLOBAL_RTC 1 /* Obsoleted, due to enough legacy. */ -/** global timer - HPET */ -#define SND_TIMER_GLOBAL_HPET 2 -/** global timer - HRTIMER */ -#define SND_TIMER_GLOBAL_HRTIMER 3 - -/** timer open mode flag - non-blocking behaviour */ -#define SND_TIMER_OPEN_NONBLOCK (1<<0) -/** use timestamps and event notification - enhanced read */ -#define SND_TIMER_OPEN_TREAD (1<<1) - -/** timer handle type */ -typedef enum _snd_timer_type { - /** Kernel level HwDep */ - SND_TIMER_TYPE_HW = 0, - /** Shared memory client timer (not yet implemented) */ - SND_TIMER_TYPE_SHM, - /** INET client timer (not yet implemented) */ - SND_TIMER_TYPE_INET -} snd_timer_type_t; - -/** timer query handle */ -typedef struct _snd_timer_query snd_timer_query_t; -/** timer handle */ -typedef struct _snd_timer snd_timer_t; - - -int snd_timer_query_open(snd_timer_query_t **handle, const char *name, int mode); -int snd_timer_query_open_lconf(snd_timer_query_t **handle, const char *name, int mode, snd_config_t *lconf); -int snd_timer_query_close(snd_timer_query_t *handle); -int snd_timer_query_next_device(snd_timer_query_t *handle, snd_timer_id_t *tid); -int snd_timer_query_info(snd_timer_query_t *handle, snd_timer_ginfo_t *info); -int snd_timer_query_params(snd_timer_query_t *handle, snd_timer_gparams_t *params); -int snd_timer_query_status(snd_timer_query_t *handle, snd_timer_gstatus_t *status); - -int snd_timer_open(snd_timer_t **handle, const char *name, int mode); -int snd_timer_open_lconf(snd_timer_t **handle, const char *name, int mode, snd_config_t *lconf); -int snd_timer_close(snd_timer_t *handle); -int snd_async_add_timer_handler(snd_async_handler_t **handler, snd_timer_t *timer, - snd_async_callback_t callback, void *private_data); -snd_timer_t *snd_async_handler_get_timer(snd_async_handler_t *handler); -int snd_timer_poll_descriptors_count(snd_timer_t *handle); -int snd_timer_poll_descriptors(snd_timer_t *handle, struct pollfd *pfds, unsigned int space); -int snd_timer_poll_descriptors_revents(snd_timer_t *timer, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -int snd_timer_info(snd_timer_t *handle, snd_timer_info_t *timer); -int snd_timer_params(snd_timer_t *handle, snd_timer_params_t *params); -int snd_timer_status(snd_timer_t *handle, snd_timer_status_t *status); -int snd_timer_start(snd_timer_t *handle); -int snd_timer_stop(snd_timer_t *handle); -int snd_timer_continue(snd_timer_t *handle); -ssize_t snd_timer_read(snd_timer_t *handle, void *buffer, size_t size); - -size_t snd_timer_id_sizeof(void); -/** allocate #snd_timer_id_t container on stack */ -#define snd_timer_id_alloca(ptr) __snd_alloca(ptr, snd_timer_id) -int snd_timer_id_malloc(snd_timer_id_t **ptr); -void snd_timer_id_free(snd_timer_id_t *obj); -void snd_timer_id_copy(snd_timer_id_t *dst, const snd_timer_id_t *src); - -void snd_timer_id_set_class(snd_timer_id_t *id, int dev_class); -int snd_timer_id_get_class(snd_timer_id_t *id); -void snd_timer_id_set_sclass(snd_timer_id_t *id, int dev_sclass); -int snd_timer_id_get_sclass(snd_timer_id_t *id); -void snd_timer_id_set_card(snd_timer_id_t *id, int card); -int snd_timer_id_get_card(snd_timer_id_t *id); -void snd_timer_id_set_device(snd_timer_id_t *id, int device); -int snd_timer_id_get_device(snd_timer_id_t *id); -void snd_timer_id_set_subdevice(snd_timer_id_t *id, int subdevice); -int snd_timer_id_get_subdevice(snd_timer_id_t *id); - -size_t snd_timer_ginfo_sizeof(void); -/** allocate #snd_timer_ginfo_t container on stack */ -#define snd_timer_ginfo_alloca(ptr) __snd_alloca(ptr, snd_timer_ginfo) -int snd_timer_ginfo_malloc(snd_timer_ginfo_t **ptr); -void snd_timer_ginfo_free(snd_timer_ginfo_t *obj); -void snd_timer_ginfo_copy(snd_timer_ginfo_t *dst, const snd_timer_ginfo_t *src); - -int snd_timer_ginfo_set_tid(snd_timer_ginfo_t *obj, snd_timer_id_t *tid); -snd_timer_id_t *snd_timer_ginfo_get_tid(snd_timer_ginfo_t *obj); -unsigned int snd_timer_ginfo_get_flags(snd_timer_ginfo_t *obj); -int snd_timer_ginfo_get_card(snd_timer_ginfo_t *obj); -char *snd_timer_ginfo_get_id(snd_timer_ginfo_t *obj); -char *snd_timer_ginfo_get_name(snd_timer_ginfo_t *obj); -unsigned long snd_timer_ginfo_get_resolution(snd_timer_ginfo_t *obj); -unsigned long snd_timer_ginfo_get_resolution_min(snd_timer_ginfo_t *obj); -unsigned long snd_timer_ginfo_get_resolution_max(snd_timer_ginfo_t *obj); -unsigned int snd_timer_ginfo_get_clients(snd_timer_ginfo_t *obj); - -size_t snd_timer_info_sizeof(void); -/** allocate #snd_timer_info_t container on stack */ -#define snd_timer_info_alloca(ptr) __snd_alloca(ptr, snd_timer_info) -int snd_timer_info_malloc(snd_timer_info_t **ptr); -void snd_timer_info_free(snd_timer_info_t *obj); -void snd_timer_info_copy(snd_timer_info_t *dst, const snd_timer_info_t *src); - -int snd_timer_info_is_slave(snd_timer_info_t * info); -int snd_timer_info_get_card(snd_timer_info_t * info); -const char *snd_timer_info_get_id(snd_timer_info_t * info); -const char *snd_timer_info_get_name(snd_timer_info_t * info); -long snd_timer_info_get_resolution(snd_timer_info_t * info); - -size_t snd_timer_params_sizeof(void); -/** allocate #snd_timer_params_t container on stack */ -#define snd_timer_params_alloca(ptr) __snd_alloca(ptr, snd_timer_params) -int snd_timer_params_malloc(snd_timer_params_t **ptr); -void snd_timer_params_free(snd_timer_params_t *obj); -void snd_timer_params_copy(snd_timer_params_t *dst, const snd_timer_params_t *src); - -int snd_timer_params_set_auto_start(snd_timer_params_t * params, int auto_start); -int snd_timer_params_get_auto_start(snd_timer_params_t * params); -int snd_timer_params_set_exclusive(snd_timer_params_t * params, int exclusive); -int snd_timer_params_get_exclusive(snd_timer_params_t * params); -int snd_timer_params_set_early_event(snd_timer_params_t * params, int early_event); -int snd_timer_params_get_early_event(snd_timer_params_t * params); -void snd_timer_params_set_ticks(snd_timer_params_t * params, long ticks); -long snd_timer_params_get_ticks(snd_timer_params_t * params); -void snd_timer_params_set_queue_size(snd_timer_params_t * params, long queue_size); -long snd_timer_params_get_queue_size(snd_timer_params_t * params); -void snd_timer_params_set_filter(snd_timer_params_t * params, unsigned int filter); -unsigned int snd_timer_params_get_filter(snd_timer_params_t * params); - -size_t snd_timer_status_sizeof(void); -/** allocate #snd_timer_status_t container on stack */ -#define snd_timer_status_alloca(ptr) __snd_alloca(ptr, snd_timer_status) -int snd_timer_status_malloc(snd_timer_status_t **ptr); -void snd_timer_status_free(snd_timer_status_t *obj); -void snd_timer_status_copy(snd_timer_status_t *dst, const snd_timer_status_t *src); - -snd_htimestamp_t snd_timer_status_get_timestamp(snd_timer_status_t * status); -long snd_timer_status_get_resolution(snd_timer_status_t * status); -long snd_timer_status_get_lost(snd_timer_status_t * status); -long snd_timer_status_get_overrun(snd_timer_status_t * status); -long snd_timer_status_get_queue(snd_timer_status_t * status); - -/* deprecated functions, for compatibility */ -long snd_timer_info_get_ticks(snd_timer_info_t * info); - -/** \} */ - -#ifdef __cplusplus -} -#endif - -#endif /** __ALSA_TIMER_H */ - diff --git a/raylib/external/alsa/topology.h b/raylib/external/alsa/topology.h deleted file mode 100644 index 42d2376..0000000 --- a/raylib/external/alsa/topology.h +++ /dev/null @@ -1,1115 +0,0 @@ -/* - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Copyright (C) 2015 Intel Corporation - * - */ - -#ifndef __ALSA_TOPOLOGY_H -#define __ALSA_TOPOLOGY_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup topology Topology Interface - * \{ - */ - -/*! \page topology ALSA Topology Interface - * - * The topology interface allows developers to define DSP topologies in a text - * file format and to convert the text topology to a binary topology - * representation that can be understood by the kernel. The topology core - * currently recognises the following object types :- - * - * * Controls (mixer, enumerated and byte) including TLV data. - * * PCMs (Front End DAI & DAI link) - * * DAPM widgets - * * DAPM graph elements. - * * Physical DAI & DAI links - * * Private data for each object type. - * * Manifest (containing count of each object type) - * - *

Topology File Format

- * - * The topology text format uses the standard ALSA configuration file format to - * describe each topology object type. This allows topology objects to include - * other topology objects as part of their definition. i.e. a TLV data object - * can be shared amongst many control objects that use the same TLV data. - * - * - *

Controls

- * Topology audio controls can belong to three different types :- - * * Mixer control - * * Enumerated control - * * Byte control - * - * Each control type can contain TLV data, private data, operations and also - * belong to widget objects.
- * - *
Control Operations
- * Driver Kcontrol callback info(), get() and put() operations are mapped with - * the CTL ops section in topology configuration files. The ctl ops section can - * assign operations using the standard names (listed below) for the standard - * kcontrol types or use ID numbers (>256) to map to bespoke driver controls.
- * - *
- *
- *	ops."ctl" {
- *		info "volsw"
- *		get "257"
- *		put "257"
- *	}
- *
- * 
- * - * This mapping shows info() using the standard "volsw" info callback whilst - * the get() and put() are mapped to bespoke driver callbacks.
- * - * The Standard operations names for control get(), put() and info calls - * are :- - * * volsw - * * volsw_sx - * * volsw_xr_sx - * * enum - * * bytes - * * enum_value - * * range - * * strobe - * -*
Control Access
- * Controls access can be specified using the "access" section. If no "access" - * section is defined then default RW access flags are set for normal and TLV - * controls. - * - *
- *	access [
- *		read
- *		write
- *		tlv_command
- *	]
- * 
- * - * The standard access flags are as follows :- - * * read - * * write - * * read_write - * * volatile - * * timestamp - * * tlv_read - * * tlv_write - * * tlv_read_write - * * tlv_command - * * inactive - * * lock - * * owner - * * tlv_callback - * * user - * - *
Control TLV Data
- * Controls can also use TLV data to represent dB information. This can be done - * by defining a TLV section and using the TLV section within the control. - * The TLV data for DBScale types are defined as follows :- - * - *
- *	scale {
- *		min "-9000"
- *		step "300"
- *		mute "1"
- *	}
- * 
- * - * Where the meanings and values for min, step and mute are exactly the same - * as defined in driver code. - * - *
Control Channel Mapping
- * Controls can also specify which channels they are mapped with. This is useful - * for userspace as it allows applications to determine the correct control - * channel for Left and Right etc. Channel maps are defined as follows :- - * - *
- *	channel."name" {
- *		reg "0"
- *		shift "0"
- *	}
- * 
- * - * The channel map reg is the register offset for the control, shift is the - * bit shift within the register for the channel and the section name is the - * channel name and can be one of the following :- - * - *
- *  * mono		# mono stream
- *  * fl 		# front left
- *  * fr		# front right
- *  * rl		# rear left
- *  * rr		# rear right
- *  * fc		# front center
- *  * lfe		# LFE
- *  * sl		# side left
- *  * sr		# side right
- *  * rc		# rear center
- *  * flc		# front left center
- *  * frc		# front right center
- *  * rlc		# rear left center
- *  * rrc		# rear right center
- *  * flw		# front left wide
- *  * frw		# front right wide
- *  * flh		# front left high
- *  * fch		# front center high
- *  * frh		# front right high
- *  * tc		# top center
- *  * tfl		# top front left
- *  * tfr		# top front right
- *  * tfc		# top front center
- *  * trl		# top rear left
- *  * trr		# top rear right
- *  * trc		# top rear center
- *  * tflc		# top front left center
- *  * tfrc		# top front right center
- *  * tsl		# top side left
- *  * tsr		# top side right
- *  * llfe		# left LFE
- *  * rlfe		# right LFE
- *  * bc		# bottom center
- *  * blc		# bottom left center
- *  * brc		# bottom right center
- * 
- * - *
Control Private Data
- * Controls can also have private data. This can be done by defining a private - * data section and including the section within the control. The private data - * section is defined as follows :- - * - *
- * SectionData."pdata for EQU1" {
- *	file "/path/to/file"
- *	bytes "0x12,0x34,0x56,0x78"
- *	shorts "0x1122,0x3344,0x5566,0x7788"
- *	words "0xaabbccdd,0x11223344,0x66aa77bb,0xefef1234"
- *	tuples "section id of the vendor tuples"
- * };
- * 
- * The file, bytes, shorts, words and tuples keywords are all mutually - * exclusive as the private data should only be taken from one source. - * The private data can either be read from a separate file or defined in - * the topology file using the bytes, shorts, words or tuples keywords. - * The keyword tuples is to define vendor specific tuples. Please refer to - * section Vendor Tokens and Vendor tuples. - * - * It's easy to use a vendor tuples object to define a C structure instance. - * And a data section can include multiple vendor tuples objects: - * - *
- * SectionData."data element name" {
- *	index "1"	#Index number
- *	tuples [
- *		"id of the 1st vendor tuples section"
- *		"id of the 2nd vendor tuples section"
- *		...
- *	]
- * };
- * 
- * - *
How to define an element with private data
- * An element can refer to a single data section or multiple data - * sections. - * - *
To refer to a single data section:
- *
- * Sectionxxx."element name" {
- *    ...
- *	data "name of data section"		# optional private data
- * }
- * 
- * - *
To refer to multiple data sections:
- *
- * Sectionxxx."element name" {
- *	...
- *	data [						# optional private data
- *		"name of 1st data section"
- *		"name of 2nd data section"
- *		...
- *	]
- * }
- * 
- * And data of these sections will be merged in the same order as they are - * in the list, as the element's private data for kernel. - * - * - * - *
Vendor Tokens
- * A vendor token list is defined as a new section. Each token element is - * a pair of string ID and integer value. And both the ID and value are - * vendor-specific. - * - *
- * SectionVendorTokens."id of the vendor tokens" {
- *	comment "optional comments"
- *	VENDOR_TOKEN_ID1 "1"
- *	VENDOR_TOKEN_ID2 "2"
- *	VENDOR_TOKEN_ID3 "3"
- *	...
- * }
- * 
- * - *
Vendor Tuples
- * Vendor tuples are defined as a new section. It contains a reference to - * a vendor token list and several tuple arrays. - * All arrays share a vendor token list, defined by the tokens keyword. - * Each tuple array is for a specific type, defined by the string following - * the tuples keyword. Supported types are: string, uuid, bool, byte, - * short and word. - * - *
- * SectionVendorTuples."id of the vendor tuples" {
- *	tokens "id of the vendor tokens"
- *
- *	tuples."string" {
- *		VENDOR_TOKEN_ID1 "character string"
- *		...
- *	}
- *
- *	tuples."uuid" {			# 16 characters separated by commas
- *		VENDOR_TOKEN_ID2 "0x01,0x02,...,0x0f"
- *		...
- *	}
- *
- *	tuples."bool" {
- *		VENDOR_TOKEN_ID3 "true/false"
- *		...
- *	}
- *
- *	tuples."byte" {
- *		VENDOR_TOKEN_ID4 "0x11"
- *		VENDOR_TOKEN_ID5 "0x22"
- *		...
- *	}
- *
- *	tuples."short" {
- *		VENDOR_TOKEN_ID6 "0x1122"
- *		VENDOR_TOKEN_ID7 "0x3344"
- *		...
- *	}
- *
- *	tuples."word" {
- *		VENDOR_TOKEN_ID8 "0x11223344"
- *		VENDOR_TOKEN_ID9 "0x55667788"
- *		...
- *	}
- * }
- * 
- * To define multiple vendor tuples of same type, please append some - * characters after the type string ("string", "uuid", "bool", "byte", "short" - * or "word"), to avoid ID duplication in the SectionVendorTuples.
- * The parser will check the first few characters in ID to get the tuple type. - * Here is an example: - *
- * SectionVendorTuples."id of the vendor tuples" {
- *    ...
- *	tuples."word.module0" {
- *		VENDOR_TOKEN_PARAM_ID1 "0x00112233"
- *		VENDOR_TOKEN_PARAM_ID2 "0x44556677"
- *		...
- *	}
- *
- *	tuples."word.module2" {
- *		VENDOR_TOKEN_PARAM_ID1 "0x11223344"
- *		VENDOR_TOKEN_PARAM_ID2 "0x55667788"
- *		...
- *	}
- *	...
- * }
- *
- * 
- * - *
Mixer Controls
- * A mixer control is defined as a new section that can include channel mapping, - * TLV data, callback operations and private data. The mixer section also - * includes a few other config options that are shown here :- - * - *
- * SectionControlMixer."mixer name" {
- *	comment "optional comments"
- *
- *	index "1"			# Index number
- *
- *	channel."name" {		# Channel maps
- *	   ....
- *	}
- *
- *	ops."ctl" {			# Ops callback functions
- *	   ....
- *	}
- *
- *	max "32"			# Max control value
- *	invert "0"			# Whether control values are inverted
- *
- *	tlv "tld_data"			# optional TLV data
- *
- *	data "pdata for mixer1"		# optional private data
- * }
- * 
- * - * The section name is used to define the mixer name. The index number can be - * used to identify topology objects groups(index "0" is common, fit for all - * user cases).This allows driver operations on objects with index number N and - * can be used to add/remove pipelines of objects whilst other objects are - * unaffected. - * - *
Byte Controls
- * A byte control is defined as a new section that can include channel mapping, - * TLV data, callback operations and private data. The bytes section also - * includes a few other config options that are shown here :- - * - *
- * SectionControlBytes."name" {
- *	comment "optional comments"
- *
- *	index "1"			# Index number
- *
- *	channel."name" {		# Channel maps
- *	   ....
- *	}
- *
- *	ops."ctl" {			# Ops callback functions
- *	   ....
- *	}
- *
- *	base "0"			# Register base
- *	num_regs "16"			# Number of registers
- *	mask "0xff"			# Mask
- *	max "255"			# Maximum value
- *
- *	tlv "tld_data"			# optional TLV data
- *
- *	data "pdata for mixer1"		# optional private data
- * }
- * 
- * - *
Enumerated Controls
- * A enumerated control is defined as a new section (like mixer and byte) that - * can include channel mapping, callback operations, private data and - * text strings to represent the enumerated control options.
- * - * The text strings for the enumerated controls are defined in a separate - * section as follows :- - * - *
- * SectionText."name" {
- *
- *		Values [
- *			"value1"
- *			"value2"
-			"value3"
- *		]
- * }
- * 
- * - * All the enumerated text values are listed in the values list.
- * The enumerated control is similar to the other controls and defined as - * follows :- - * - *
- * SectionControlMixer."name" {
- *	comment "optional comments"
- *
- *	index "1"			# Index number
- *
- *	texts "EQU1"			# Enumerated text items
- *
- *	channel."name" {		# Channel maps
- *	   ....
- *	}
- *
- *	ops."ctl" {			# Ops callback functions
- *	   ....
- *	}
- *
- *	data "pdata for mixer1"		# optional private data
- * }
- * 
- * - *

DAPM Graph

- * DAPM graphs can easily be defined using the topology file. The format is - * very similar to the DAPM graph kernel format. :- - * - *
- * SectionGraph."dsp" {
- *	index "1"			# Index number
- *
- *	lines [
- *		"sink1, control, source1"
- *		"sink2, , source2"
- *	]
- * }
- * 
- * - * The lines in the graph are defined as a variable size list of sinks, - * controls and sources. The control name is optional as some graph lines have - * no associated controls. The section name can be used to differentiate the - * graph with other graphs, it's not used by the kernel atm. - * - *

DAPM Widgets

- * DAPM widgets are similar to controls in that they can include many other - * objects. Widgets can contain private data, mixer controls and enum controls. - * - * The following widget types are supported and match the driver types :- - * - * * input - * * output - * * mux - * * mixer - * * pga - * * out_drv - * * adc - * * dac - * * switch - * * pre - * * post - * * aif_in - * * aif_out - * * dai_in - * * dai_out - * * dai_link - * - * Widgets are defined as follows :- - * - *
- * SectionWidget."name" {
- *
- *	index "1"			# Index number
- *
- *	type "aif_in"			# Widget type - detailed above
- *	stream_name "name"		# Stream name
- *
- *	no_pm "true"			# No PM control bit.
- *	reg "20"			# PM bit register offset
- *	shift "0"			# PM bit register shift
- *	invert "1			# PM bit is inverted
- *	subseq "8"			# subsequence number
- *
- *	event_type "1"			# DAPM widget event type
- *	event_flags "1"			# DAPM widget event flags
- *
- *	mixer "name"			# Optional Mixer Control
- *	enum "name"			# Optional Enum Control
- *
- *	data "name"			# optional private data
- * }
- * 
- * - * The section name is the widget name. The mixer and enum fields are mutually - * exclusive and used to include controls into the widget. The index and data - * fields are the same for widgets as they are for controls whilst the other - * fields map on very closely to the driver widget fields. - * - *
Widget Private Data
- * Widget can have private data. For the format of the private data, please - * refer to section Control Private Data. - * - *

PCM Capabilities

- * Topology can also define the PCM capabilities of front end or physical DAIs. - * Capabilities can be defined with the following section :- - * - *
- * SectionPCMCapabilities."name" {
- *
- *	formats "S24_LE,S16_LE"		# Supported formats
- *	rates "48000"			# Supported rates
- *	rate_min "48000"		# Max supported sample rate
- *	rate_max "48000"		# Min supported sample rate
- *	channels_min "2"		# Min number of channels
- *	channels_max "2"		# max number of channels
- * }
- * 
- * The supported formats use the same naming convention as the driver macros. - * The PCM capabilities name can be referred to and included by PCM and - * physical DAI sections. - * - *

PCM Configurations

- * PCM runtime configurations can be defined for playback and capture stream - * directions with the following section :- - * - *
- * SectionPCMConfig."name" {
- *
- *	config."playback" {		# playback config
- *		format "S16_LE"		# playback format
- *		rate "48000"		# playback sample rate
- *		channels "2"		# playback channels
- *		tdm_slot "0xf"		# playback TDM slot
- *	}
- *
- *	config."capture" {		# capture config
- *		format "S16_LE"		# capture format
- *		rate "48000"		# capture sample rate
- *		channels "2"		# capture channels
- *		tdm_slot "0xf"		# capture TDM slot
- *	}
- * }
- * 
- * - * The supported formats use the same naming convention as the driver macros. - * The PCM configuration name can be referred to and included by PCM and - * physical link sections. - * - *

PCM (Front-end DAI & DAI link)

- * PCM sections define the supported capabilities and configurations for - * supported playback and capture streams, names and flags for front end - * DAI & DAI links. Topology kernel driver will use a PCM object to create - * a pair of FE DAI & DAI links. - * - *
- * SectionPCM."name" {
- *
- *	index "1"			# Index number
- *
- *	id "0"				# used for binding to the PCM
- *
- *	dai."name of front-end DAI" {
- *		id "0"		# used for binding to the front-end DAI
- *	}
- *
- *	pcm."playback" {
- *		capabilities "capabilities1"	# capabilities for playback
- *
- *		configs [		# supported configs for playback
- *			"config1"
- *			"config2"
- *		]
- *	}
- *
- *	pcm."capture" {
- *		capabilities "capabilities2"	# capabilities for capture
- *
- *		configs [		# supported configs for capture
- *			"config1"
- *			"config2"
- *			"config3"
- *		]
- *	}
- *
- *	# Optional boolean flags
- *	symmetric_rates			"true"
- *	symmetric_channels		"true"
- *	symmetric_sample_bits		"false"
- *
- *	data "name"			# optional private data
- * }
- * 
- * - *

Physical DAI Link Configurations

- * The runtime configurations of a physical DAI link can be defined by - * SectionLink.
Backend DAI links belong to physical links, and can - * be configured by either SectionLink or SectionBE, with same syntax. - * But SectionBE is deprecated atm since the internal processing is - * actually same. - * - *
- * SectionLink."name" {
- *
- *	index "1"			# Index number
- *
- *	id "0"				# used for binding to the link
- *
- *	stream_name "name"		# used for binding to the link
- *
- *	hw_configs [	# runtime supported HW configurations, optional
- *		"config1"
- *		"config2"
- *		...
- *	]
- *
- *	default_hw_conf_id "1"		#default HW config ID for init
- *
- *	# Optional boolean flags
- *	symmetric_rates			"true"
- *	symmetric_channels		"false"
- *	symmetric_sample_bits		"true"
- *
- *	data "name"			# optional private data
- * }
- * 
- * - * A physical link can refer to multiple runtime supported hardware - * configurations, which is defined by SectionHWConfig. - * - *
- * SectionHWConfig."name" {
- *
- *	id "1"				# used for binding to the config
- *	format "I2S"			# physical audio format.
- *	bclk   "master"			# Platform is master of bit clock
- *	fsync  "slave"			# Platform is slave of fsync
- * }
- * 
- * - *

Physical DAI

- * A physical DAI (e.g. backend DAI for DPCM) is defined as a new section - * that can include a unique ID, playback and capture stream capabilities, - * optional flags, and private data.
- * Its PCM stream capablities are same as those for PCM objects, - * please refer to section 'PCM Capabilities'. - * - *
- * SectionDAI."name" {
- *
- *	index "1"			# Index number
- *
- *	id "0"				# used for binding to the Backend DAI
- *
- *	pcm."playback" {
- *		capabilities "capabilities1"	# capabilities for playback
- *	}
- *
- *	pcm."capture" {
- *		capabilities "capabilities2"	# capabilities for capture
- *	}
- *
- *	symmetric_rates "true"			# optional flags
- *	symmetric_channels "true"
- *	symmetric_sample_bits "false"
- *
- *	data "name"			# optional private data
- * }
- * 
- * - *

Manifest Private Data

- * Manfiest may have private data. Users need to define a manifest section - * and add the references to 1 or multiple data sections. Please refer to - * section 'How to define an element with private data'.
- * And the text conf file can have at most 1 manifest section.

- * - * Manifest section is defined as follows :- - * - *
- * SectionManifest"name" {
- *
- *	data "name"			# optional private data
- * }
- * 
- * - *

Include other files

- * Users may include other files in a text conf file via alsaconf syntax - * . This allows users to define common info - * in separate files (e.g. vendor tokens, tuples) and share them for - * different platforms, thus save the total size of config files.
- * Users can also specifiy additional configuraiton directories relative - * to "/usr/share/alsa/" to search the included files, via alsaconf syntax - * .

- * - * For example, file A and file B are two text conf files for platform X, - * they will be installed to /usr/share/alsa/topology/platformx. If we - * need file A to include file B, in file A we can add:
- * - *
- *

- * - * ALSA conf will search and open an included file in the following order - * of priority: - * 1. directly open the file by its name; - * 2. search for the file name in "/usr/share/alsa"; - * 3. search for the file name in user specified subdirectories under - * "/usr/share/alsa". - * - * The order of the included files need not to be same as their - * dependencies, since the topology library will load them all before - * parsing their dependencies.
- * - * The configuration directories defined by a file will only be used to search - * the files included by this file. - */ - -/** Maximum number of channels supported in one control */ -#define SND_TPLG_MAX_CHAN 8 - -/** Topology context */ -typedef struct snd_tplg snd_tplg_t; - -/** Topology object types */ -enum snd_tplg_type { - SND_TPLG_TYPE_TLV = 0, /*!< TLV Data */ - SND_TPLG_TYPE_MIXER, /*!< Mixer control*/ - SND_TPLG_TYPE_ENUM, /*!< Enumerated control */ - SND_TPLG_TYPE_TEXT, /*!< Text data */ - SND_TPLG_TYPE_DATA, /*!< Private data */ - SND_TPLG_TYPE_BYTES, /*!< Byte control */ - SND_TPLG_TYPE_STREAM_CONFIG, /*!< PCM Stream configuration */ - SND_TPLG_TYPE_STREAM_CAPS, /*!< PCM Stream capabilities */ - SND_TPLG_TYPE_PCM, /*!< PCM stream device */ - SND_TPLG_TYPE_DAPM_WIDGET, /*!< DAPM widget */ - SND_TPLG_TYPE_DAPM_GRAPH, /*!< DAPM graph elements */ - SND_TPLG_TYPE_BE, /*!< BE DAI link */ - SND_TPLG_TYPE_CC, /*!< Hostless codec <-> codec link */ - SND_TPLG_TYPE_MANIFEST, /*!< Topology manifest */ - SND_TPLG_TYPE_TOKEN, /*!< Vendor tokens */ - SND_TPLG_TYPE_TUPLE, /*!< Vendor tuples */ - SND_TPLG_TYPE_LINK, /*!< Physical DAI link */ - SND_TPLG_TYPE_HW_CONFIG, /*!< Link HW config */ - SND_TPLG_TYPE_DAI, /*!< Physical DAI */ -}; - -/** Fit for all user cases */ -#define SND_TPLG_INDEX_ALL 0 - -/** - * \brief Create a new topology parser instance. - * \return New topology parser instance - */ -snd_tplg_t *snd_tplg_new(void); - -/** - * \brief Free a topology parser instance. - * \param tplg Topology parser instance - */ -void snd_tplg_free(snd_tplg_t *tplg); - -/** - * \brief Parse and build topology text file into binary file. - * \param tplg Topology instance. - * \param infile Topology text input file to be parsed - * \param outfile Binary topology output file. - * \return Zero on success, otherwise a negative error code - */ -int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, - const char *outfile); - -/** - * \brief Enable verbose reporting of binary file output - * \param tplg Topology Instance - * \param verbose Enable verbose output level if non zero - */ -void snd_tplg_verbose(snd_tplg_t *tplg, int verbose); - -/** \struct snd_tplg_tlv_template - * \brief Template type for all TLV objects. - */ -struct snd_tplg_tlv_template { - int type; /*!< TLV type SNDRV_CTL_TLVT_ */ -}; - -/** \struct snd_tplg_tlv_dbscale_template - * \brief Template type for TLV Scale objects. - */ -struct snd_tplg_tlv_dbscale_template { - struct snd_tplg_tlv_template hdr; /*!< TLV type header */ - int min; /*!< dB minimum value in 0.1dB */ - int step; /*!< dB step size in 0.1dB */ - int mute; /*!< is min dB value mute ? */ -}; - -/** \struct snd_tplg_channel_template - * \brief Template type for single channel mapping. - */ -struct snd_tplg_channel_elem { - int size; /*!< size in bytes of this structure */ - int reg; /*!< channel control register */ - int shift; /*!< channel shift for control bits */ - int id; /*!< ID maps to Left, Right, LFE etc */ -}; - -/** \struct snd_tplg_channel_map_template - * \brief Template type for channel mapping. - */ -struct snd_tplg_channel_map_template { - int num_channels; /*!< number of channel mappings */ - struct snd_tplg_channel_elem channel[SND_TPLG_MAX_CHAN]; /*!< mapping */ -}; - -/** \struct snd_tplg_pdata_template - * \brief Template type for private data objects. - */ -struct snd_tplg_pdata_template { - unsigned int length; /*!< data length */ - const void *data; /*!< data */ -}; - -/** \struct snd_tplg_io_ops_template - * \brief Template type for object operations mapping. - */ -struct snd_tplg_io_ops_template { - int get; /*!< get callback ID */ - int put; /*!< put callback ID */ - int info; /*!< info callback ID */ -}; - -/** \struct snd_tplg_ctl_template - * \brief Template type for control objects. - */ -struct snd_tplg_ctl_template { - int type; /*!< Control type */ - const char *name; /*!< Control name */ - int access; /*!< Control access */ - struct snd_tplg_io_ops_template ops; /*!< operations */ - struct snd_tplg_tlv_template *tlv; /*!< non NULL means we have TLV data */ -}; - -/** \struct snd_tplg_mixer_template - * \brief Template type for mixer control objects. - */ -struct snd_tplg_mixer_template { - struct snd_tplg_ctl_template hdr; /*!< control type header */ - struct snd_tplg_channel_map_template *map; /*!< channel map */ - int min; /*!< min value for mixer */ - int max; /*!< max value for mixer */ - int platform_max; /*!< max value for platform control */ - int invert; /*!< whether controls bits are inverted */ - struct snd_soc_tplg_private *priv; /*!< control private data */ -}; - -/** \struct snd_tplg_enum_template - * \brief Template type for enumerated control objects. - */ -struct snd_tplg_enum_template { - struct snd_tplg_ctl_template hdr; /*!< control type header */ - struct snd_tplg_channel_map_template *map; /*!< channel map */ - int items; /*!< number of enumerated items in control */ - int mask; /*!< register mask size */ - const char **texts; /*!< control text items */ - const int **values; /*!< control value items */ - struct snd_soc_tplg_private *priv; /*!< control private data */ -}; - -/** \struct snd_tplg_bytes_template - * \brief Template type for TLV Scale objects. - */ -struct snd_tplg_bytes_template { - struct snd_tplg_ctl_template hdr; /*!< control type header */ - int max; /*!< max byte control value */ - int mask; /*!< byte control mask */ - int base; /*!< base register */ - int num_regs; /*!< number of registers */ - struct snd_tplg_io_ops_template ext_ops; /*!< ops mapping */ - struct snd_soc_tplg_private *priv; /*!< control private data */ -}; - -/** \struct snd_tplg_graph_elem - * \brief Template type for single DAPM graph element. - */ -struct snd_tplg_graph_elem { - const char *src; /*!< source widget name */ - const char *ctl; /*!< control name or NULL if no control */ - const char *sink; /*!< sink widget name */ -}; - -/** \struct snd_tplg_graph_template - * \brief Template type for array of DAPM graph elements. - */ -struct snd_tplg_graph_template { - int count; /*!< Number of graph elements */ - struct snd_tplg_graph_elem elem[0]; /*!< graph elements */ -}; - -/** \struct snd_tplg_widget_template - * \brief Template type for DAPM widget objects. - */ -struct snd_tplg_widget_template { - int id; /*!< SND_SOC_DAPM_CTL */ - const char *name; /*!< widget name */ - const char *sname; /*!< stream name (certain widgets only) */ - int reg; /*!< negative reg = no direct dapm */ - int shift; /*!< bits to shift */ - int mask; /*!< non-shifted mask */ - int subseq; /*!< sort within widget type */ - unsigned int invert; /*!< invert the power bit */ - unsigned int ignore_suspend; /*!< kept enabled over suspend */ - unsigned short event_flags; /*!< PM event sequence flags */ - unsigned short event_type; /*!< PM event sequence type */ - struct snd_soc_tplg_private *priv; /*!< widget private data */ - int num_ctls; /*!< Number of controls used by widget */ - struct snd_tplg_ctl_template *ctl[0]; /*!< array of widget controls */ -}; - -/** \struct snd_tplg_stream_template - * \brief Stream configurations. - */ -struct snd_tplg_stream_template { - const char *name; /*!< name of the stream config */ - int format; /*!< SNDRV_PCM_FMTBIT_* */ - int rate; /*!< SNDRV_PCM_RATE_* */ - int period_bytes; /*!< size of period in bytes */ - int buffer_bytes; /*!< size of buffer in bytes. */ - int channels; /*!< number of channels */ -}; - -/** \struct snd_tplg_stream_caps_template - * \brief Stream Capabilities. - */ -struct snd_tplg_stream_caps_template { - const char *name; /*!< name of the stream caps */ - uint64_t formats; /*!< supported formats SNDRV_PCM_FMTBIT_* */ - unsigned int rates; /*!< supported rates SNDRV_PCM_RATE_* */ - unsigned int rate_min; /*!< min rate */ - unsigned int rate_max; /*!< max rate */ - unsigned int channels_min; /*!< min channels */ - unsigned int channels_max; /*!< max channels */ - unsigned int periods_min; /*!< min number of periods */ - unsigned int periods_max; /*!< max number of periods */ - unsigned int period_size_min; /*!< min period size bytes */ - unsigned int period_size_max; /*!< max period size bytes */ - unsigned int buffer_size_min; /*!< min buffer size bytes */ - unsigned int buffer_size_max; /*!< max buffer size bytes */ - unsigned int sig_bits; /*!< number of bits of content */ -}; - -/** \struct snd_tplg_pcm_template - * \brief Template type for PCM (FE DAI & DAI links). - */ -struct snd_tplg_pcm_template { - const char *pcm_name; /*!< PCM stream name */ - const char *dai_name; /*!< DAI name */ - unsigned int pcm_id; /*!< unique ID - used to match */ - unsigned int dai_id; /*!< unique ID - used to match */ - unsigned int playback; /*!< supports playback mode */ - unsigned int capture; /*!< supports capture mode */ - unsigned int compress; /*!< 1 = compressed; 0 = PCM */ - struct snd_tplg_stream_caps_template *caps[2]; /*!< playback & capture for DAI */ - unsigned int flag_mask; /*!< bitmask of flags to configure */ - unsigned int flags; /*!< flag value SND_SOC_TPLG_LNK_FLGBIT_* */ - struct snd_soc_tplg_private *priv; /*!< private data */ - int num_streams; /*!< number of supported configs */ - struct snd_tplg_stream_template stream[0]; /*!< supported configs */ -}; - - /** \struct snd_tplg_hw_config_template - * \brief Template type to describe a physical link runtime supported - * hardware config, i.e. hardware audio formats. - */ -struct snd_tplg_hw_config_template { - int id; /* unique ID - - used to match */ - unsigned int fmt; /* SND_SOC_DAI_FORMAT_ format value */ - unsigned char clock_gated; /* 1 if clock can be gated to save power */ - unsigned char invert_bclk; /* 1 for inverted BCLK, 0 for normal */ - unsigned char invert_fsync; /* 1 for inverted frame clock, 0 for normal */ - unsigned char bclk_master; /* 1 for master of BCLK, 0 for slave */ - unsigned char fsync_master; /* 1 for master of FSYNC, 0 for slave */ - unsigned char mclk_direction; /* 0 for input, 1 for output */ - unsigned short reserved; /* for 32bit alignment */ - unsigned int mclk_rate; /* MCLK or SYSCLK freqency in Hz */ - unsigned int bclk_rate; /* BCLK freqency in Hz */ - unsigned int fsync_rate; /* frame clock in Hz */ - unsigned int tdm_slots; /* number of TDM slots in use */ - unsigned int tdm_slot_width; /* width in bits for each slot */ - unsigned int tx_slots; /* bit mask for active Tx slots */ - unsigned int rx_slots; /* bit mask for active Rx slots */ - unsigned int tx_channels; /* number of Tx channels */ - unsigned int *tx_chanmap; /* array of slot number */ - unsigned int rx_channels; /* number of Rx channels */ - unsigned int *rx_chanmap; /* array of slot number */ -}; - -/** \struct snd_tplg_dai_template - * \brief Template type for physical DAI. - * It can be used to configure backend DAIs for DPCM. - */ -struct snd_tplg_dai_template { - const char *dai_name; /*!< DAI name */ - unsigned int dai_id; /*!< unique ID - used to match */ - unsigned int playback; /*!< supports playback mode */ - unsigned int capture; /*!< supports capture mode */ - struct snd_tplg_stream_caps_template *caps[2]; /*!< playback & capture for DAI */ - unsigned int flag_mask; /*!< bitmask of flags to configure */ - unsigned int flags; /*!< SND_SOC_TPLG_DAI_FLGBIT_* */ - struct snd_soc_tplg_private *priv; /*!< private data */ - -}; - -/** \struct snd_tplg_link_template - * \brief Template type for physical DAI Links. - */ -struct snd_tplg_link_template { - const char *name; /*!< link name, used to match */ - int id; /*!< unique ID - used to match with existing physical links */ - const char *stream_name; /*!< link stream name, used to match */ - - int num_streams; /*!< number of configs */ - struct snd_tplg_stream_template *stream; /*!< supported configs */ - - struct snd_tplg_hw_config_template *hw_config; /*!< supported HW configs */ - int num_hw_configs; /* number of hw configs */ - int default_hw_config_id; /* default hw config ID for init */ - - unsigned int flag_mask; /* bitmask of flags to configure */ - unsigned int flags; /* SND_SOC_TPLG_LNK_FLGBIT_* flag value */ - struct snd_soc_tplg_private *priv; /*!< private data */ -}; - -/** \struct snd_tplg_obj_template - * \brief Generic Template Object - */ -typedef struct snd_tplg_obj_template { - enum snd_tplg_type type; /*!< template object type */ - int index; /*!< group index for object */ - int version; /*!< optional vendor specific version details */ - int vendor_type; /*!< optional vendor specific type info */ - union { - struct snd_tplg_widget_template *widget; /*!< DAPM widget */ - struct snd_tplg_mixer_template *mixer; /*!< Mixer control */ - struct snd_tplg_bytes_template *bytes_ctl; /*!< Bytes control */ - struct snd_tplg_enum_template *enum_ctl; /*!< Enum control */ - struct snd_tplg_graph_template *graph; /*!< Graph elements */ - struct snd_tplg_pcm_template *pcm; /*!< PCM elements */ - struct snd_tplg_link_template *link; /*!< physical DAI Links */ - struct snd_tplg_dai_template *dai; /*!< Physical DAI */ - }; -} snd_tplg_obj_template_t; - -/** - * \brief Register topology template object. - * \param tplg Topology instance. - * \param t Template object. - * \return Zero on success, otherwise a negative error code - */ -int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); - -/** - * \brief Build all registered topology data into binary file. - * \param tplg Topology instance. - * \param outfile Binary topology output file. - * \return Zero on success, otherwise a negative error code - */ -int snd_tplg_build(snd_tplg_t *tplg, const char *outfile); - -/** - * \brief Attach private data to topology manifest. - * \param tplg Topology instance. - * \param data Private data. - * \param len Length of data in bytes. - * \return Zero on success, otherwise a negative error code - */ -int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len); - -/** - * \brief Set an optional vendor specific version number. - * \param tplg Topology instance. - * \param version Vendor specific version number. - * \return Zero on success, otherwise a negative error code - */ -int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version); - -/* \} */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_TOPOLOGY_H */ diff --git a/raylib/external/alsa/use-case.h b/raylib/external/alsa/use-case.h deleted file mode 100644 index ae22bde..0000000 --- a/raylib/external/alsa/use-case.h +++ /dev/null @@ -1,433 +0,0 @@ -/** - * \file include/use-case.h - * \brief use case interface for the ALSA driver - * \author Liam Girdwood - * \author Stefan Schmidt - * \author Jaroslav Kysela - * \author Justin Xu - * \date 2008-2010 - */ -/* - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Copyright (C) 2008-2010 SlimLogic Ltd - * Copyright (C) 2010 Wolfson Microelectronics PLC - * Copyright (C) 2010 Texas Instruments Inc. - * - * Support for the verb/device/modifier core logic and API, - * command line tool and file parser was kindly sponsored by - * Texas Instruments Inc. - * Support for multiple active modifiers and devices, - * transition sequences, multiple client access and user defined use - * cases was kindly sponsored by Wolfson Microelectronics PLC. - */ - -#ifndef __ALSA_USE_CASE_H -#define __ALSA_USE_CASE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \defgroup ucm Use Case Interface - * The ALSA Use Case manager interface. - * See \ref Usecase page for more details. - * \{ - */ - -/*! \page Usecase ALSA Use Case Interface - * - * The use case manager works by configuring the sound card ALSA kcontrols to - * change the hardware digital and analog audio routing to match the requested - * device use case. The use case manager kcontrol configurations are stored in - * easy to modify text files. - * - * An audio use case can be defined by a verb and device parameter. The verb - * describes the use case action i.e. a phone call, listening to music, recording - * a conversation etc. The device describes the physical audio capture and playback - * hardware i.e. headphones, phone handset, bluetooth headset, etc. - * - * It's intended clients will mostly only need to set the use case verb and - * device for each system use case change (as the verb and device parameters - * cover most audio use cases). - * - * However there are times when a use case has to be modified at runtime. e.g. - * - * + Incoming phone call when the device is playing music - * + Recording sections of a phone call - * + Playing tones during a call. - * - * In order to allow asynchronous runtime use case adaptations, we have a third - * optional modifier parameter that can be used to further configure - * the use case during live audio runtime. - * - * This interface allows clients to :- - * - * + Query the supported use case verbs, devices and modifiers for the machine. - * + Set and Get use case verbs, devices and modifiers for the machine. - * + Get the ALSA PCM playback and capture device PCMs for use case verb, - * use case device and modifier. - * + Get the TQ parameter for each use case verb, use case device and - * modifier. - * + Get the ALSA master playback and capture volume/switch kcontrols - * for each use case. - */ - - -/* - * Use Case Verb. - * - * The use case verb is the main device audio action. e.g. the "HiFi" use - * case verb will configure the audio hardware for HiFi Music playback - * and capture. - */ -#define SND_USE_CASE_VERB_INACTIVE "Inactive" /**< Inactive Verb */ -#define SND_USE_CASE_VERB_HIFI "HiFi" /**< HiFi Verb */ -#define SND_USE_CASE_VERB_HIFI_LOW_POWER "HiFi Low Power" /**< HiFi Low Power Verb */ -#define SND_USE_CASE_VERB_VOICE "Voice" /**< Voice Verb */ -#define SND_USE_CASE_VERB_VOICE_LOW_POWER "Voice Low Power" /**< Voice Low Power Verb */ -#define SND_USE_CASE_VERB_VOICECALL "Voice Call" /**< Voice Call Verb */ -#define SND_USE_CASE_VERB_IP_VOICECALL "Voice Call IP" /**< Voice Call IP Verb */ -#define SND_USE_CASE_VERB_ANALOG_RADIO "FM Analog Radio" /**< FM Analog Radio Verb */ -#define SND_USE_CASE_VERB_DIGITAL_RADIO "FM Digital Radio" /**< FM Digital Radio Verb */ -/* add new verbs to end of list */ - - -/* - * Use Case Device. - * - * Physical system devices the render and capture audio. Devices can be OR'ed - * together to support audio on simultaneous devices. - */ -#define SND_USE_CASE_DEV_NONE "None" /**< None Device */ -#define SND_USE_CASE_DEV_SPEAKER "Speaker" /**< Speaker Device */ -#define SND_USE_CASE_DEV_LINE "Line" /**< Line Device */ -#define SND_USE_CASE_DEV_HEADPHONES "Headphones" /**< Headphones Device */ -#define SND_USE_CASE_DEV_HEADSET "Headset" /**< Headset Device */ -#define SND_USE_CASE_DEV_HANDSET "Handset" /**< Handset Device */ -#define SND_USE_CASE_DEV_BLUETOOTH "Bluetooth" /**< Bluetooth Device */ -#define SND_USE_CASE_DEV_EARPIECE "Earpiece" /**< Earpiece Device */ -#define SND_USE_CASE_DEV_SPDIF "SPDIF" /**< SPDIF Device */ -#define SND_USE_CASE_DEV_HDMI "HDMI" /**< HDMI Device */ -/* add new devices to end of list */ - - -/* - * Use Case Modifiers. - * - * The use case modifier allows runtime configuration changes to deal with - * asynchronous events. - * - * e.g. to record a voice call :- - * 1. Set verb to SND_USE_CASE_VERB_VOICECALL (for voice call) - * 2. Set modifier SND_USE_CASE_MOD_CAPTURE_VOICE when capture required. - * 3. Call snd_use_case_get("CapturePCM") to get ALSA source PCM name - * with captured voice pcm data. - * - * e.g. to play a ring tone when listenin to MP3 Music :- - * 1. Set verb to SND_USE_CASE_VERB_HIFI (for MP3 playback) - * 2. Set modifier to SND_USE_CASE_MOD_PLAY_TONE when incoming call happens. - * 3. Call snd_use_case_get("PlaybackPCM") to get ALSA PCM sink name for - * ringtone pcm data. - */ -#define SND_USE_CASE_MOD_CAPTURE_VOICE "Capture Voice" /**< Capture Voice Modifier */ -#define SND_USE_CASE_MOD_CAPTURE_MUSIC "Capture Music" /**< Capture Music Modifier */ -#define SND_USE_CASE_MOD_PLAY_MUSIC "Play Music" /**< Play Music Modifier */ -#define SND_USE_CASE_MOD_PLAY_VOICE "Play Voice" /**< Play Voice Modifier */ -#define SND_USE_CASE_MOD_PLAY_TONE "Play Tone" /**< Play Tone Modifier */ -#define SND_USE_CASE_MOD_ECHO_REF "Echo Reference" /**< Echo Reference Modifier */ -/* add new modifiers to end of list */ - - -/** - * TQ - Tone Quality - * - * The interface allows clients to determine the audio TQ required for each - * use case verb and modifier. It's intended as an optional hint to the - * audio driver in order to lower power consumption. - * - */ -#define SND_USE_CASE_TQ_MUSIC "Music" /**< Music Tone Quality */ -#define SND_USE_CASE_TQ_VOICE "Voice" /**< Voice Tone Quality */ -#define SND_USE_CASE_TQ_TONES "Tones" /**< Tones Tone Quality */ - -/** use case container */ -typedef struct snd_use_case_mgr snd_use_case_mgr_t; - -/** - * \brief Create an identifier - * \param fmt Format (sprintf like) - * \param ... Optional arguments for sprintf like format - * \return Allocated string identifier or NULL on error - */ -char *snd_use_case_identifier(const char *fmt, ...); - -/** - * \brief Free a string list - * \param list The string list to free - * \param items Count of strings - * \return Zero if success, otherwise a negative error code - */ -int snd_use_case_free_list(const char *list[], int items); - -/** - * \brief Obtain a list of entries - * \param uc_mgr Use case manager (may be NULL - card list) - * \param identifier (may be NULL - card list) - * \param list Returned allocated list - * \return Number of list entries if success, otherwise a negative error code - * - * Defined identifiers: - * - NULL - get card list - * (in pair cardname+comment) - * - _verbs - get verb list - * (in pair verb+comment) - * - _devices[/{verb}] - get list of supported devices - * (in pair device+comment) - * - _modifiers[/{verb}] - get list of supported modifiers - * (in pair modifier+comment) - * - TQ[/{verb}] - get list of TQ identifiers - * - _enadevs - get list of enabled devices - * - _enamods - get list of enabled modifiers - * - * - _supporteddevs/{modifier}|{device}[/{verb}] - list of supported devices - * - _conflictingdevs/{modifier}|{device}[/{verb}] - list of conflicting devices - * - * Note that at most one of the supported/conflicting devs lists has - * any entries, and when neither is present, all devices are supported. - * - */ -int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, - const char *identifier, - const char **list[]); - - -/** - * \brief Get current - string - * \param uc_mgr Use case manager - * \param identifier - * \param value Value pointer - * \return Zero if success, otherwise a negative error code - * - * Note: The returned string is dynamically allocated, use free() to - * deallocate this string. (Yes, the value parameter shouldn't be marked as - * "const", but it's too late to fix it, sorry about that.) - * - * Known identifiers: - * - NULL - return current card - * - _verb - return current verb - * - _file - return configuration file loaded for current card - * - * - [=]{NAME}[/[{modifier}|{/device}][/{verb}]] - * - value identifier {NAME} - * - Search starts at given modifier or device if any, - * else at a verb - * - Search starts at given verb if any, - * else current verb - * - Searches modifier/device, then verb, then defaults - * - Specify a leading "=" to search only the exact - * device/modifier/verb specified, and not search - * through each object in turn. - * - Examples: - * - "PlaybackPCM/Play Music" - * - "CapturePCM/SPDIF" - * - From ValueDefaults only: - * "=Variable" - * - From current active verb: - * "=Variable//" - * - From verb "Verb": - * "=Variable//Verb" - * - From "Modifier" in current active verb: - * "=Variable/Modifier/" - * - From "Modifier" in "Verb": - * "=Variable/Modifier/Verb" - * - * Recommended names for values: - * - TQ - * - Tone Quality - * - PlaybackPCM - * - full PCM playback device name - * - PlaybackPCMIsDummy - * - Valid values: "yes" and "no". If set to "yes", the PCM named by the - * PlaybackPCM value is a dummy device, meaning that opening it enables - * an audio path in the hardware, but writing to the PCM device has no - * effect. - * - CapturePCM - * - full PCM capture device name - * - CapturePCMIsDummy - * - Valid values: "yes" and "no". If set to "yes", the PCM named by the - * CapturePCM value is a dummy device, meaning that opening it enables - * an audio path in the hardware, but reading from the PCM device has no - * effect. - * - PlaybackRate - * - playback device sample rate - * - PlaybackChannels - * - playback device channel count - * - PlaybackCTL - * - playback control device name - * - PlaybackVolume - * - playback control volume ID string - * - PlaybackSwitch - * - playback control switch ID string - * - CaptureRate - * - capture device sample rate - * - CaptureChannels - * - capture device channel count - * - CaptureCTL - * - capture control device name - * - CaptureVolume - * - capture control volume ID string - * - CaptureSwitch - * - capture control switch ID string - * - PlaybackMixer - * - name of playback mixer - * - PlaybackMixerID - * - mixer playback ID - * - CaptureMixer - * - name of capture mixer - * - CaptureMixerID - * - mixer capture ID - * - JackControl, JackDev, JackHWMute - * - Jack information for a device. The jack status can be reported via - * a kcontrol and/or via an input device. **JackControl** is the - * kcontrol name of the jack, and **JackDev** is the input device id of - * the jack (if the full input device path is /dev/input/by-id/foo, the - * JackDev value should be "foo"). UCM configuration files should - * contain both JackControl and JackDev when possible, because - * applications are likely to support only one or the other. - * - * If **JackHWMute** is set, it indicates that when the jack is plugged - * in, the hardware automatically mutes some other device(s). The - * JackHWMute value is a space-separated list of device names (this - * isn't compatible with device names with spaces in them, so don't use - * such device names!). Note that JackHWMute should be used only when - * the hardware enforces the automatic muting. If the hardware doesn't - * enforce any muting, it may still be tempting to set JackHWMute to - * trick upper software layers to e.g. automatically mute speakers when - * headphones are plugged in, but that's application policy - * configuration that doesn't belong to UCM configuration files. - */ -int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, - const char *identifier, - const char **value); - -/** - * \brief Get current - integer - * \param uc_mgr Use case manager - * \param identifier - * \param value result - * \return Zero if success, otherwise a negative error code - * - * Known identifiers: - * - _devstatus/{device} - return status for given device - * - _modstatus/{modifier} - return status for given modifier - */ -int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, - const char *identifier, - long *value); - -/** - * \brief Set new - * \param uc_mgr Use case manager - * \param identifier - * \param value Value - * \return Zero if success, otherwise a negative error code - * - * Known identifiers: - * - _verb - set current verb = value - * - _enadev - enable given device = value - * - _disdev - disable given device = value - * - _swdev/{old_device} - new_device = value - * - disable old_device and then enable new_device - * - if old_device is not enabled just return - * - check transmit sequence firstly - * - _enamod - enable given modifier = value - * - _dismod - disable given modifier = value - * - _swmod/{old_modifier} - new_modifier = value - * - disable old_modifier and then enable new_modifier - * - if old_modifier is not enabled just return - * - check transmit sequence firstly - */ -int snd_use_case_set(snd_use_case_mgr_t *uc_mgr, - const char *identifier, - const char *value); - -/** - * \brief Open and initialise use case core for sound card - * \param uc_mgr Returned use case manager pointer - * \param card_name Sound card name. - * \return zero if success, otherwise a negative error code - */ -int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, const char *card_name); - - -/** - * \brief Reload and re-parse use case configuration files for sound card. - * \param uc_mgr Use case manager - * \return zero if success, otherwise a negative error code - */ -int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr); - -/** - * \brief Close use case manager - * \param uc_mgr Use case manager - * \return zero if success, otherwise a negative error code - */ -int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr); - -/** - * \brief Reset use case manager verb, device, modifier to deafult settings. - * \param uc_mgr Use case manager - * \return zero if success, otherwise a negative error code - */ -int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr); - -/* - * helper functions - */ - -/** - * \brief Obtain a list of cards - * \param list Returned allocated list - * \return Number of list entries if success, otherwise a negative error code - */ -static __inline__ int snd_use_case_card_list(const char **list[]) -{ - return snd_use_case_get_list(NULL, NULL, list); -} - -/** - * \brief Obtain a list of verbs - * \param uc_mgr Use case manager - * \param list Returned list of verbs - * \return Number of list entries if success, otherwise a negative error code - */ -static __inline__ int snd_use_case_verb_list(snd_use_case_mgr_t *uc_mgr, - const char **list[]) -{ - return snd_use_case_get_list(uc_mgr, "_verbs", list); -} - -/** - * \} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ALSA_USE_CASE_H */ diff --git a/raylib/external/alsa/version.h b/raylib/external/alsa/version.h deleted file mode 100644 index ad36231..0000000 --- a/raylib/external/alsa/version.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * version.h - */ - -#define SND_LIB_MAJOR 1 /**< major number of library version */ -#define SND_LIB_MINOR 1 /**< minor number of library version */ -#define SND_LIB_SUBMINOR 5 /**< subminor number of library version */ -#define SND_LIB_EXTRAVER 1000000 /**< extra version number, used mainly for betas */ -/** library version */ -#define SND_LIB_VERSION ((SND_LIB_MAJOR<<16)|\ - (SND_LIB_MINOR<<8)|\ - SND_LIB_SUBMINOR) -/** library version (string) */ -#define SND_LIB_VERSION_STR "1.1.5" - diff --git a/raylib/external/cgltf.h b/raylib/external/cgltf.h new file mode 100644 index 0000000..ed04d54 --- /dev/null +++ b/raylib/external/cgltf.h @@ -0,0 +1,2023 @@ +#ifndef CGLTF_H_INCLUDED__ +#define CGLTF_H_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef unsigned long cgltf_size; +typedef float cgltf_float; +typedef int cgltf_bool; + +typedef enum cgltf_file_type +{ + cgltf_file_type_invalid, + cgltf_file_type_gltf, + cgltf_file_type_glb, +} cgltf_file_type; + +typedef struct cgltf_options +{ + cgltf_file_type type; + cgltf_size json_token_count; /* 0 == auto */ + void* (*memory_alloc)(void* user, cgltf_size size); + void (*memory_free) (void* user, void* ptr); + void* memory_user_data; +} cgltf_options; + +typedef enum cgltf_result +{ + cgltf_result_success, + cgltf_result_data_too_short, + cgltf_result_unknown_format, + cgltf_result_invalid_json, + cgltf_result_invalid_options, +} cgltf_result; + +typedef enum cgltf_buffer_view_type +{ + cgltf_buffer_view_type_invalid, + cgltf_buffer_view_type_indices, + cgltf_buffer_view_type_vertices, +} cgltf_buffer_view_type; + +typedef enum cgltf_attribute_type +{ + cgltf_attribute_type_invalid, + cgltf_attribute_type_position, + cgltf_attribute_type_normal, + cgltf_attribute_type_tangent, + cgltf_attribute_type_texcoord_0, + cgltf_attribute_type_texcoord_1, + cgltf_attribute_type_color_0, + cgltf_attribute_type_joints_0, + cgltf_attribute_type_weights_0, +} cgltf_attribute_type; + +typedef enum cgltf_component_type +{ + cgltf_component_type_invalid, + cgltf_component_type_rgb_32f, + cgltf_component_type_rgba_32f, + cgltf_component_type_rg_32f, + cgltf_component_type_rg_8, + cgltf_component_type_rg_16, + cgltf_component_type_rgba_8, + cgltf_component_type_rgba_16, + cgltf_component_type_r_8, + cgltf_component_type_r_8u, + cgltf_component_type_r_16, + cgltf_component_type_r_16u, + cgltf_component_type_r_32u, + cgltf_component_type_r_32f, +} cgltf_component_type; + +typedef enum cgltf_type +{ + cgltf_type_invalid, + cgltf_type_scalar, + cgltf_type_vec2, + cgltf_type_vec3, + cgltf_type_vec4, + cgltf_type_mat2, + cgltf_type_mat3, + cgltf_type_mat4, +} cgltf_type; + +typedef enum cgltf_primitive_type +{ + cgltf_type_points, + cgltf_type_lines, + cgltf_type_line_loop, + cgltf_type_line_strip, + cgltf_type_triangles, + cgltf_type_triangle_strip, + cgltf_type_triangle_fan, +} cgltf_primitive_type; + +typedef struct cgltf_buffer +{ + cgltf_size size; + char* uri; +} cgltf_buffer; + +typedef struct cgltf_buffer_view +{ + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; /* 0 == automatically determined by accessor */ + cgltf_buffer_view_type type; +} cgltf_buffer_view; + +typedef struct cgltf_accessor +{ + cgltf_component_type component_type; + cgltf_type type; + cgltf_size offset; + cgltf_size count; + cgltf_size stride; + cgltf_buffer_view* buffer_view; +} cgltf_accessor; + +typedef struct cgltf_attribute +{ + cgltf_attribute_type name; + cgltf_accessor* data; +} cgltf_attribute; + + +typedef struct cgltf_rgba +{ + cgltf_float r; + cgltf_float g; + cgltf_float b; + cgltf_float a; +} cgltf_rgba; + +typedef struct cgltf_image +{ + char* uri; + cgltf_buffer_view* buffer_view; + char* mime_type; +} cgltf_image; + +typedef struct cgltf_sampler +{ + cgltf_float mag_filter; + cgltf_float min_filter; + cgltf_float wrap_s; + cgltf_float wrap_t; +} cgltf_sampler; + +typedef struct cgltf_texture +{ + cgltf_image* image; + cgltf_sampler* sampler; +} cgltf_texture; + +typedef struct cgltf_texture_view +{ + cgltf_texture* texture; + cgltf_size texcoord; + cgltf_float scale; +} cgltf_texture_view; + +typedef struct cgltf_pbr +{ + cgltf_texture_view base_color_texture; + cgltf_texture_view metallic_roughness_texture; + + cgltf_rgba base_color; + cgltf_float metallic_factor; + cgltf_float roughness_factor; +} cgltf_pbr; + +typedef struct cgltf_material +{ + char* name; + cgltf_pbr pbr; + cgltf_rgba emissive_color; + cgltf_texture_view normal_texture; + cgltf_texture_view emissive_texture; + cgltf_texture_view occlusion_texture; + cgltf_bool double_sided; +} cgltf_material; + +typedef struct cgltf_primitive { + cgltf_primitive_type type; + cgltf_accessor* indices; + cgltf_material* material; + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_primitive; + +typedef struct cgltf_mesh { + char* name; + cgltf_primitive* primitives; + cgltf_size primitives_count; +} cgltf_mesh; + +typedef struct cgltf_data +{ + unsigned version; + cgltf_file_type file_type; + + cgltf_mesh* meshes; + cgltf_size meshes_count; + + cgltf_material* materials; + cgltf_size materials_count; + + cgltf_accessor* accessors; + cgltf_size accessors_count; + + cgltf_buffer_view* buffer_views; + cgltf_size buffer_views_count; + + cgltf_buffer* buffers; + cgltf_size buffers_count; + + cgltf_image* images; + cgltf_size images_count; + + cgltf_texture* textures; + cgltf_size textures_count; + + cgltf_sampler* samplers; + cgltf_size samplers_count; + + const void* bin; + cgltf_size bin_size; + + void (*memory_free) (void* user, void* ptr); + void* memory_user_data; +} cgltf_data; + +cgltf_result cgltf_parse( + const cgltf_options* options, + const void* data, + cgltf_size size, + cgltf_data* out_data); + +void cgltf_free(cgltf_data* data); + +#endif /* #ifndef CGLTF_H_INCLUDED__ */ + +/* + * + * Stop now, if you are only interested in the API. + * Below, you find the implementation. + * + */ + +#ifdef __INTELLISENSE__ +/* This makes MSVC intellisense work. */ +#define CGLTF_IMPLEMENTATION +#endif + +#ifdef CGLTF_IMPLEMENTATION + +#include /* For uint8_t, uint32_t */ +#include /* For strncpy */ +#include /* For malloc, free */ + + +/* + * -- jsmn.h start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; +void jsmn_init(jsmn_parser *parser); +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens); +/* + * -- jsmn.h end -- + */ + + +static const cgltf_size GltfHeaderSize = 12; +static const cgltf_size GltfChunkHeaderSize = 8; +static const uint32_t GltfMagic = 0x46546C67; +static const uint32_t GltfMagicJsonChunk = 0x4E4F534A; +static const uint32_t GltfMagicBinChunk = 0x004E4942; + +static void* cgltf_mem_alloc(void* user, cgltf_size size) +{ + return malloc(size); +} + +static void cgltf_mem_free(void* user, void* ptr) +{ + free(ptr); +} + +static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data* out_data); + +cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data* out_data) +{ + if (size < GltfHeaderSize) + { + return cgltf_result_data_too_short; + } + + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + cgltf_options fixed_options = *options; + if (fixed_options.memory_alloc == NULL) + { + fixed_options.memory_alloc = &cgltf_mem_alloc; + } + if (fixed_options.memory_free == NULL) + { + fixed_options.memory_free = &cgltf_mem_free; + } + + uint32_t tmp; + // Magic + memcpy(&tmp, data, 4); + if (tmp != GltfMagic) + { + if (fixed_options.type == cgltf_file_type_invalid) + { + fixed_options.type = cgltf_file_type_gltf; + } + else + { + return cgltf_result_unknown_format; + } + } + + memset(out_data, 0, sizeof(cgltf_data)); + out_data->memory_free = fixed_options.memory_free; + out_data->memory_user_data = fixed_options.memory_user_data; + + if (fixed_options.type == cgltf_file_type_gltf) + { + out_data->file_type = cgltf_file_type_gltf; + return cgltf_parse_json(&fixed_options, data, size, out_data); + } + + const uint8_t* ptr = (const uint8_t*)data; + // Version + memcpy(&tmp, ptr + 4, 4); + out_data->version = tmp; + + // Total length + memcpy(&tmp, ptr + 8, 4); + if (tmp > size) + { + return cgltf_result_data_too_short; + } + + const uint8_t* json_chunk = ptr + GltfHeaderSize; + + // JSON chunk: length + uint32_t json_length; + memcpy(&json_length, json_chunk, 4); + if (GltfHeaderSize + GltfChunkHeaderSize + json_length > size) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: magic + memcpy(&tmp, json_chunk + 4, 4); + if (tmp != GltfMagicJsonChunk) + { + return cgltf_result_unknown_format; + } + + json_chunk += GltfChunkHeaderSize; + cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + out_data->file_type = cgltf_file_type_invalid; + if (GltfHeaderSize + GltfChunkHeaderSize + json_length + GltfChunkHeaderSize <= size) + { + // We can read another chunk + const uint8_t* bin_chunk = json_chunk + json_length; + + // Bin chunk: length + uint32_t bin_length; + memcpy(&bin_length, bin_chunk, 4); + if (GltfHeaderSize + GltfChunkHeaderSize + json_length + GltfChunkHeaderSize + bin_length > size) + { + return cgltf_result_data_too_short; + } + + // Bin chunk: magic + memcpy(&tmp, bin_chunk + 4, 4); + if (tmp != GltfMagicBinChunk) + { + return cgltf_result_unknown_format; + } + + bin_chunk += GltfChunkHeaderSize; + + out_data->file_type = cgltf_file_type_glb; + out_data->bin = bin_chunk; + out_data->bin_size = bin_length; + } + + return cgltf_result_success; +} + +void cgltf_free(cgltf_data* data) +{ + data->memory_free(data->memory_user_data, data->accessors); + data->memory_free(data->memory_user_data, data->buffer_views); + + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + data->memory_free(data->memory_user_data, data->buffers[i].uri); + } + data->memory_free(data->memory_user_data, data->buffers); + + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + data->memory_free(data->memory_user_data, data->meshes[i].name); + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + data->memory_free(data->memory_user_data, data->meshes[i].primitives[j].attributes); + } + data->memory_free(data->memory_user_data, data->meshes[i].primitives); + } + data->memory_free(data->memory_user_data, data->meshes); + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + data->memory_free(data->memory_user_data, data->materials[i].name); + } + + data->memory_free(data->memory_user_data, data->materials); + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + data->memory_free(data->memory_user_data, data->images[i].uri); + data->memory_free(data->memory_user_data, data->images[i].mime_type); + } + + data->memory_free(data->memory_user_data, data->images); + data->memory_free(data->memory_user_data, data->textures); + data->memory_free(data->memory_user_data, data->samplers); +} + +#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return -128; } + +static char cgltf_to_lower(char c) +{ + if (c >= 'A' && c <= 'Z') + { + c = 'a' + (c - 'A'); + } + return c; +} + +static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); + int const str_len = strlen(str); + int const name_length = tok->end - tok->start; + if (name_length == str_len) + { + for (int i = 0; i < str_len; ++i) + { + char const a = cgltf_to_lower(*((const char*)json_chunk + tok->start + i)); + char const b = cgltf_to_lower(*(str + i)); + if (a < b) + { + return -1; + } + else if (a > b) + { + return 1; + } + } + return 0; + } + return 128; +} + +static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + char tmp[128]; + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + int size = tok->end - tok->start; + strncpy(tmp, + (const char*)json_chunk + tok->start, + size); + tmp[size] = 0; + return atoi(tmp); +} + +static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) { + char tmp[128]; + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + int size = tok->end - tok->start; + strncpy(tmp, + (const char*)json_chunk + tok->start, + size); + tmp[size] = 0; + return atof(tmp); +} + +static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) { + //TODO: error handling? + if (memcmp(json_chunk + tok->start, "true", 4) == 0) + return 1; + + return 0; +} + +static int cgltf_skip_json(jsmntok_t const* tokens, int i) +{ + if (tokens[i].type == JSMN_ARRAY) + { + int size = tokens[i].size; + ++i; + for (int j = 0; j < size; ++j) + { + i = cgltf_skip_json(tokens, i); + } + } + else if (tokens[i].type == JSMN_OBJECT) + { + int size = tokens[i].size; + ++i; + for (int j = 0; j < size; ++j) + { + i = cgltf_skip_json(tokens, i); + i = cgltf_skip_json(tokens, i); + } + } + else if (tokens[i].type == JSMN_PRIMITIVE + || tokens[i].type == JSMN_STRING) + { + return i + 1; + } + return i; +} + + +static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, + cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + out_prim->indices = (void* )-1; + out_prim->material = NULL; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + out_prim->type + = (cgltf_primitive_type) + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + out_prim->indices = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0) + { + ++i; + out_prim->material = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0) + { + ++i; + if (tokens[i].type != JSMN_OBJECT) + { + return -1; + } + out_prim->attributes_count = tokens[i].size; + out_prim->attributes + = options->memory_alloc(options->memory_user_data, sizeof(cgltf_attribute) * tokens[i].size); + ++i; + for (cgltf_size iattr = 0; iattr < out_prim->attributes_count; ++iattr) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + out_prim->attributes[iattr].name = cgltf_attribute_type_invalid; + if (cgltf_json_strcmp(tokens+i, json_chunk, "POSITION") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_position; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "NORMAL") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_normal; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TANGENT") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_tangent; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TEXCOORD_0") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_texcoord_0; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TEXCOORD_1") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_texcoord_1; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "COLOR_0") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_color_0; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "JOINTS_0") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_joints_0; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "WEIGHTS_0") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_weights_0; + } + ++i; + out_prim->attributes[iattr].data = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size mesh_index, + cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_data->meshes[mesh_index].name = NULL; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + out_data->meshes[mesh_index].name = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(out_data->meshes[mesh_index].name, + (const char*)json_chunk + tokens[i].start, + strsize); + out_data->meshes[mesh_index].name[strsize] = 0; + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0) + { + ++i; + if (tokens[i].type != JSMN_ARRAY) + { + return -1; + } + out_data->meshes[mesh_index].primitives_count = tokens[i].size; + out_data->meshes[mesh_index].primitives = options->memory_alloc(options->memory_user_data, sizeof(cgltf_primitive) * tokens[i].size); + ++i; + + for (cgltf_size prim_index = 0; + prim_index < out_data->meshes[mesh_index].primitives_count; + ++prim_index) + { + i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, + &out_data->meshes[mesh_index].primitives[prim_index]); + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->meshes_count = tokens[i].size; + out_data->meshes = options->memory_alloc(options->memory_user_data, sizeof(cgltf_mesh) * out_data->meshes_count); + ++i; + for (cgltf_size j = 0 ; j < out_data->meshes_count; ++j) + { + i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_accessor(jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size accessor_index, + cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + memset(&out_data->accessors[accessor_index], 0, sizeof(cgltf_accessor)); + out_data->accessors[accessor_index].buffer_view = (void*)-1; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_data->accessors[accessor_index].buffer_view = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_data->accessors[accessor_index].offset = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + int type = cgltf_json_to_int(tokens+i, json_chunk); + switch (type) + { + case 5120: + type = cgltf_component_type_r_8; + break; + case 5121: + type = cgltf_component_type_r_8u; + break; + case 5122: + type = cgltf_component_type_r_16; + break; + case 5123: + type = cgltf_component_type_r_16u; + break; + case 5125: + type = cgltf_component_type_r_32u; + break; + case 5126: + type = cgltf_component_type_r_32f; + break; + default: + type = cgltf_component_type_invalid; + break; + } + out_data->accessors[accessor_index].component_type = type; + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_data->accessors[accessor_index].count = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_scalar; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_vec2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_vec3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_vec4; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_mat2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_mat3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_mat4; + } + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_rgba(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_rgba* out) +{ + int components = tokens[i].size; + if (components >= 2) { + out->r = cgltf_json_to_float(tokens + ++i, json_chunk); + out->g = cgltf_json_to_float(tokens + ++i, json_chunk); + + if (components > 2) + out->b = cgltf_json_to_float(tokens + ++i, json_chunk); + + if (components > 3) + out->a = cgltf_json_to_float(tokens + ++i, json_chunk); + } + else { + out->r = cgltf_json_to_float(tokens + ++i, json_chunk); + out->g = out->r; + out->b = out->r; + out->a = out->r; + } + + return ++i; +} + +static int cgltf_parse_json_texture_view(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out) { + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0) + { + ++i; + out->texture = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + ++i; + out->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + } + + return i; +} + +static int cgltf_parse_json_pbr(jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size mat_index, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) + { + ++i; + out_data->materials[mat_index].pbr.metallic_factor = + cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) + { + ++i; + out_data->materials[mat_index].pbr.roughness_factor = + cgltf_json_to_float(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0) + { + i = cgltf_parse_json_rgba(tokens, i + 1, json_chunk, + &(out_data->materials[mat_index].pbr.base_color)); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(out_data->materials[mat_index].pbr.base_color_texture)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(out_data->materials[mat_index].pbr.metallic_roughness_texture)); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size img_index, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + memset(&out_data->images[img_index], 0, sizeof(cgltf_image)); + int size = tokens[i].size; + ++i; + + out_data->images[img_index].buffer_view = (void*)-1; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + out_data->images[img_index].uri = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(out_data->images[img_index].uri, + (const char*)json_chunk + tokens[i].start, + strsize); + out_data->images[img_index].uri[strsize] = 0; + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_data->images[img_index].buffer_view = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + out_data->images[img_index].mime_type = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(out_data->images[img_index].mime_type, + (const char*)json_chunk + tokens[i].start, + strsize); + out_data->images[img_index].mime_type[strsize] = 0; + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + } + + return i; +} + +static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size smp_index, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + memset(&out_data->samplers[smp_index], 0, sizeof(cgltf_sampler)); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) + { + ++i; + out_data->samplers[smp_index].mag_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) + { + ++i; + out_data->samplers[smp_index].min_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) + { + ++i; + out_data->samplers[smp_index].wrap_s + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) + { + ++i; + out_data->samplers[smp_index].wrap_t + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + } + + return i; +} + + +static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size tex_index, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + memset(&out_data->textures[tex_index], 0, sizeof(cgltf_texture)); + out_data->textures[tex_index].image = (void*)-1; + out_data->textures[tex_index].sampler = (void*)-1; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0) + { + ++i; + out_data->textures[tex_index].sampler + = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_data->textures[tex_index].image + = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + } + + return i; +} + +static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size mat_index, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + cgltf_material* material = &out_data->materials[mat_index]; + + memset(material, 0, sizeof(cgltf_material)); + material->emissive_texture.texture = (void*)-1; + material->emissive_texture.scale = 1.0f; + + material->normal_texture.texture = (void*)-1; + material->normal_texture.scale = 1.0f; + + material->occlusion_texture.texture = (void*)-1; + material->occlusion_texture.scale = 1.0f; + + material->pbr.base_color_texture.texture = (void*)-1; + material->pbr.base_color_texture.scale = 1.0f; + + material->pbr.metallic_roughness_texture.texture = (void*)-1; + material->pbr.metallic_roughness_texture.scale = 1.0f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + material->name = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(material->name, + (const char*)json_chunk + tokens[i].start, + strsize); + material->name[strsize] = 0; + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) + { + i = cgltf_parse_json_pbr(tokens, i+1, json_chunk, mat_index, out_data); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) + { + i = cgltf_parse_json_rgba(tokens, i + 1, json_chunk, + &(material->emissive_color)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(material->normal_texture)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(material->emissive_texture)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(material->occlusion_texture)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) + { + ++i; + material->double_sided = + cgltf_json_to_bool(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->accessors_count = tokens[i].size; + out_data->accessors = options->memory_alloc(options->memory_user_data, sizeof(cgltf_accessor) * out_data->accessors_count); + ++i; + for (cgltf_size j = 0 ; j < out_data->accessors_count; ++j) + { + i = cgltf_parse_json_accessor(tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->materials_count = tokens[i].size; + out_data->materials = options->memory_alloc(options->memory_user_data, sizeof(cgltf_material) * out_data->materials_count); + ++i; + for (cgltf_size j = 0; j < out_data->materials_count; ++j) + { + i = cgltf_parse_json_material(options, tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->images_count = tokens[i].size; + out_data->images = options->memory_alloc(options->memory_user_data, sizeof(cgltf_image) * out_data->images_count); + ++i; + + for (cgltf_size j = 0; j < out_data->images_count; ++j) { + i = cgltf_parse_json_image(options, tokens, i, json_chunk, j, out_data); + if (i < 0) { + return i; + } + } + return i; +} + +static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->textures_count = tokens[i].size; + out_data->textures = options->memory_alloc(options->memory_user_data, sizeof(cgltf_texture) * out_data->textures_count); + ++i; + + for (cgltf_size j = 0; j < out_data->textures_count; ++j) { + i = cgltf_parse_json_texture(options, tokens, i, json_chunk, j, out_data); + if (i < 0) { + return i; + } + } + return i; +} + +static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->samplers_count = tokens[i].size; + out_data->samplers = options->memory_alloc(options->memory_user_data, sizeof(cgltf_sampler) * out_data->samplers_count); + ++i; + + for (cgltf_size j = 0; j < out_data->samplers_count; ++j) { + i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, j, out_data); + if (i < 0) { + return i; + } + } + return i; +} + +static int cgltf_parse_json_buffer_view(jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size buffer_view_index, + cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + memset(&out_data->buffer_views[buffer_view_index], 0, sizeof(cgltf_buffer_view)); + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_data->buffer_views[buffer_view_index].buffer = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_data->buffer_views[buffer_view_index].offset = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_data->buffer_views[buffer_view_index].size = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_data->buffer_views[buffer_view_index].stride = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + int type = cgltf_json_to_int(tokens+i, json_chunk); + switch (type) + { + case 34962: + type = cgltf_buffer_view_type_vertices; + break; + case 34963: + type = cgltf_buffer_view_type_indices; + break; + default: + type = cgltf_buffer_view_type_invalid; + break; + } + out_data->buffer_views[buffer_view_index].type = type; + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->buffer_views_count = tokens[i].size; + out_data->buffer_views = options->memory_alloc(options->memory_user_data, sizeof(cgltf_buffer_view) * out_data->buffer_views_count); + ++i; + for (cgltf_size j = 0 ; j < out_data->buffer_views_count; ++j) + { + i = cgltf_parse_json_buffer_view(tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size buffer_index, + cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_data->buffers[buffer_index].uri = NULL; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_data->buffers[buffer_index].size = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + out_data->buffers[buffer_index].uri = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(out_data->buffers[buffer_index].uri, + (const char*)json_chunk + tokens[i].start, + strsize); + out_data->buffers[buffer_index].uri[strsize] = 0; + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->buffers_count = tokens[i].size; + out_data->buffers = options->memory_alloc(options->memory_user_data, sizeof(cgltf_buffer) * out_data->buffers_count); + ++i; + for (cgltf_size j = 0 ; j < out_data->buffers_count; ++j) + { + i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) +{ + cgltf_type size = 0; + + switch (component_type) + { + case cgltf_component_type_rgb_32f: + size = 12; + break; + case cgltf_component_type_rgba_32f: + size = 16; + break; + case cgltf_component_type_rg_32f: + size = 8; + break; + case cgltf_component_type_rg_8: + size = 2; + break; + case cgltf_component_type_rg_16: + size = 4; + break; + case cgltf_component_type_rgba_8: + size = 4; + break; + case cgltf_component_type_rgba_16: + size = 8; + break; + case cgltf_component_type_r_8: + case cgltf_component_type_r_8u: + size = 1; + break; + case cgltf_component_type_r_16: + case cgltf_component_type_r_16u: + size = 2; + break; + case cgltf_component_type_r_32u: + case cgltf_component_type_r_32f: + size = 4; + break; + case cgltf_component_type_invalid: + default: + size = 0; + break; + } + + switch (type) + { + case cgltf_type_vec2: + size *= 2; + break; + case cgltf_type_vec3: + size *= 3; + break; + case cgltf_type_vec4: + size *= 4; + break; + case cgltf_type_mat2: + size *= 4; + break; + case cgltf_type_mat3: + size *= 9; + break; + case cgltf_type_mat4: + size *= 16; + break; + case cgltf_type_invalid: + case cgltf_type_scalar: + default: + size *= 1; + break; + } + + return size; +} + +cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data* out_data) +{ + jsmn_parser parser = {0}; + + if (options->json_token_count == 0) + { + options->json_token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0); + } + + jsmntok_t* tokens = options->memory_alloc(options->memory_user_data, sizeof(jsmntok_t) * options->json_token_count); + + jsmn_init(&parser); + + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count); + + if (token_count < 0 + || tokens[0].type != JSMN_OBJECT) + { + return cgltf_result_invalid_json; + } + + // The root is an object. + + for (int i = 1; i < token_count; ) + { + jsmntok_t const* tok = &tokens[i]; + if (tok->type == JSMN_STRING + && i + 1 < token_count) + { + int const name_length = tok->end - tok->start; + if (name_length == 6 + && strncmp((const char*)json_chunk + tok->start, "meshes", 6) == 0) + { + i = cgltf_parse_json_meshes(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 9 + && strncmp((const char*)json_chunk + tok->start, "accessors", 9) == 0) + { + i = cgltf_parse_json_accessors(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 11 + && strncmp((const char*)json_chunk + tok->start, "bufferViews", 11) == 0) + { + i = cgltf_parse_json_buffer_views(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 7 + && strncmp((const char*)json_chunk + tok->start, "buffers", 7) == 0) + { + i = cgltf_parse_json_buffers(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 9 + && strncmp((const char*)json_chunk + tok->start, "materials", 9) == 0) + { + i = cgltf_parse_json_materials(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 6 + && strncmp((const char*)json_chunk + tok->start, "images", 6) == 0) + { + i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data); + } + else if (name_length == 8 + && strncmp((const char*)json_chunk + tok->start, "textures", 8) == 0) + { + i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data); + } + else if (name_length == 8 + && strncmp((const char*)json_chunk + tok->start, "samplers", 8) == 0) + { + i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return cgltf_result_invalid_json; + } + } + } + + options->memory_free(options->memory_user_data, tokens); + + /* Fix up pointers */ + for (cgltf_size i = 0; i < out_data->meshes_count; ++i) + { + for (cgltf_size j = 0; j < out_data->meshes[i].primitives_count; ++j) + { + if (out_data->meshes[i].primitives[j].indices ==(void*)-1) + { + out_data->meshes[i].primitives[j].indices = NULL; + } + else + { + out_data->meshes[i].primitives[j].indices + = &out_data->accessors[(cgltf_size)out_data->meshes[i].primitives[j].indices]; + } + + for (cgltf_size k = 0; k < out_data->meshes[i].primitives[j].attributes_count; ++k) + { + out_data->meshes[i].primitives[j].attributes[k].data + = &out_data->accessors[(cgltf_size)out_data->meshes[i].primitives[j].attributes[k].data]; + } + } + } + + for (cgltf_size i = 0; i < out_data->accessors_count; ++i) + { + if (out_data->accessors[i].buffer_view == (void*)-1) + { + out_data->accessors[i].buffer_view = NULL; + } + else + { + out_data->accessors[i].buffer_view + = &out_data->buffer_views[(cgltf_size)out_data->accessors[i].buffer_view]; + out_data->accessors[i].stride = 0; + if (out_data->accessors[i].buffer_view) + { + out_data->accessors[i].stride = out_data->accessors[i].buffer_view->stride; + } + } + if (out_data->accessors[i].stride == 0) + { + out_data->accessors[i].stride = cgltf_calc_size(out_data->accessors[i].type, out_data->accessors[i].component_type); + } + } + + for (cgltf_size i = 0; i < out_data->textures_count; ++i) + { + if (out_data->textures[i].image == (void*)-1) + { + out_data->textures[i].image = NULL; + } + else + { + out_data->textures[i].image = + &out_data->images[(cgltf_size)out_data->textures[i].image]; + } + + if (out_data->textures[i].sampler == (void*)-1) + { + out_data->textures[i].sampler = NULL; + } + else + { + out_data->textures[i].sampler = + &out_data->samplers[(cgltf_size)out_data->textures[i].sampler]; + } + } + + for (cgltf_size i = 0; i < out_data->images_count; ++i) + { + if (out_data->images[i].buffer_view == (void*)-1) + { + out_data->images[i].buffer_view = NULL; + } + else + { + out_data->images[i].buffer_view + = &out_data->buffer_views[(cgltf_size)out_data->images[i].buffer_view]; + } + } + + for (cgltf_size i = 0; i < out_data->materials_count; ++i) + { + if (out_data->materials[i].emissive_texture.texture == (void*)-1) + { + out_data->materials[i].emissive_texture.texture = NULL; + } + else + { + out_data->materials[i].emissive_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].emissive_texture.texture]; + } + + if (out_data->materials[i].normal_texture.texture == (void*)-1) + { + out_data->materials[i].normal_texture.texture = NULL; + } + else + { + out_data->materials[i].normal_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].normal_texture.texture]; + } + + if (out_data->materials[i].occlusion_texture.texture == (void*)-1) + { + out_data->materials[i].occlusion_texture.texture = NULL; + } + else + { + out_data->materials[i].occlusion_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].occlusion_texture.texture]; + } + + if (out_data->materials[i].pbr.base_color_texture.texture == (void*)-1) + { + out_data->materials[i].pbr.base_color_texture.texture = NULL; + } + else + { + out_data->materials[i].pbr.base_color_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].pbr.base_color_texture.texture]; + } + + if (out_data->materials[i].pbr.metallic_roughness_texture.texture == (void*)-1) + { + out_data->materials[i].pbr.metallic_roughness_texture.texture = NULL; + } + else + { + out_data->materials[i].pbr.metallic_roughness_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].pbr.metallic_roughness_texture.texture]; + } + } + + for (cgltf_size i = 0; i < out_data->buffer_views_count; ++i) + { + out_data->buffer_views[i].buffer + = &out_data->buffers[(cgltf_size)out_data->buffer_views[i].buffer]; + } + + return cgltf_result_success; +} + +/* + * -- jsmn.c start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + */ +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} +/* + * -- jsmn.c end -- + */ + +#endif /* #ifdef CGLTF_IMPLEMENTATION */ + +#ifdef __cplusplus +} +#endif diff --git a/raylib/external/dr_flac.h b/raylib/external/dr_flac.h index 60f57ec..c836847 100644 --- a/raylib/external/dr_flac.h +++ b/raylib/external/dr_flac.h @@ -1,5 +1,5 @@ // FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file. -// dr_flac - v0.8d - 2017-09-22 +// dr_flac - v0.9.7 - 2018-07-05 // // David Reid - mackron@gmail.com @@ -106,11 +106,12 @@ // // // QUICK NOTES +// - dr_flac does not currently support changing the sample rate nor channel count mid stream. // - Audio data is output as signed 32-bit PCM, regardless of the bits per sample the FLAC stream is encoded as. // - This has not been tested on big-endian architectures. // - Rice codes in unencoded binary form (see https://xiph.org/flac/format.html#rice_partition) has not been tested. If anybody // knows where I can find some test files for this, let me know. -// - dr_flac is not thread-safe, but it's APIs can be called from any thread so long as you do your own synchronization. +// - dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization. // - When using Ogg encapsulation, a corrupted metadata block will result in drflac_open_with_metadata() and drflac_open() // returning inconsistent samples. @@ -466,7 +467,7 @@ typedef struct // value specified in the STREAMINFO block. drflac_uint8 channels; - // The bits per sample. Will be set to somthing like 16, 24, etc. + // The bits per sample. Will be set to something like 16, 24, etc. drflac_uint8 bitsPerSample; // The maximum block size, in samples. This number represents the number of samples in each channel (not combined). @@ -481,17 +482,16 @@ typedef struct // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. drflac_container container; - - // The position of the seektable in the file. - drflac_uint64 seektablePos; - - // The size of the seektable. - drflac_uint32 seektableSize; + // The number of seekpoints in the seektable. + drflac_uint32 seekpointCount; // Information about the frame the decoder is currently sitting on. drflac_frame currentFrame; + // The index of the sample the decoder is currently sitting on. This is only used for seeking. + drflac_uint64 currentSample; + // The position of the first frame in the stream. This is only ever used for seeking. drflac_uint64 firstFramePos; @@ -503,6 +503,9 @@ typedef struct // A pointer to the decoded sample data. This is an offset of pExtraData. drflac_int32* pDecodedSamples; + // A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table. + drflac_seekpoint* pSeekpoints; + // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. void* _oggbs; @@ -576,7 +579,7 @@ drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, dr // See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close() drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData); -// The same as drflac_open_with_metadata(), except attemps to open the stream even when a header block is not present. +// The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present. // // See also: drflac_open_with_metadata(), drflac_open_relaxed() drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData); @@ -759,11 +762,13 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr #define DRFLAC_X64 #elif defined(__i386) || defined(_M_IX86) #define DRFLAC_X86 +#elif defined(__arm__) || defined(_M_ARM) +#define DRFLAC_ARM #endif // Compile-time CPU feature support. #if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) - #ifdef _MSC_VER + #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER >= 1400 #include static void drflac__cpuid(int info[4], int fid) @@ -777,19 +782,8 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr #if defined(__GNUC__) || defined(__clang__) static void drflac__cpuid(int info[4], int fid) { - asm ( - "movl %[fid], %%eax\n\t" - "cpuid\n\t" - "movl %%eax, %[info0]\n\t" - "movl %%ebx, %[info1]\n\t" - "movl %%ecx, %[info2]\n\t" - "movl %%edx, %[info3]\n\t" - : [info0] "=rm"(info[0]), - [info1] "=rm"(info[1]), - [info2] "=rm"(info[2]), - [info3] "=rm"(info[3]) - : [fid] "rm"(fid) - : "eax", "ebx", "ecx", "edx" + __asm__ __volatile__ ( + "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) ); } #else @@ -806,7 +800,7 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr #include #endif -#if defined(_MSC_VER) && _MSC_VER >= 1500 +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) #define DRFLAC_HAS_LZCNT_INTRINSIC #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) #define DRFLAC_HAS_LZCNT_INTRINSIC @@ -1444,7 +1438,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; } else { @@ -1457,13 +1451,13 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); drflac_uint32 bitCountLo = bitCount - bitCountHi; - drflac_uint32 resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + drflac_uint32 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } - *pResultOut = (resultHi << bitCountLo) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; return DRFLAC_TRUE; @@ -1489,6 +1483,7 @@ static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, dr return DRFLAC_TRUE; } +#ifdef DRFLAC_64BIT static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) { drflac_assert(bitCount <= 64); @@ -1507,6 +1502,7 @@ static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, d *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); return DRFLAC_TRUE; } +#endif // Function below is unused, but leaving it here in case I need to quickly add it again. #if 0 @@ -1694,7 +1690,7 @@ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) #if !defined(DR_FLAC_NO_SIMD) && defined(DRFLAC_HAS_LZCNT_INTRINSIC) #define DRFLAC_IMPLEMENT_CLZ_LZCNT #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) #define DRFLAC_IMPLEMENT_CLZ_MSVC #endif @@ -1739,7 +1735,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported() static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) #ifdef DRFLAC_64BIT return (drflac_uint32)__lzcnt64(x); #else @@ -1802,7 +1798,6 @@ static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsign } drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); - zeroCounter += setBitOffsetPlus1; setBitOffsetPlus1 += 1; bs->consumedBits += setBitOffsetPlus1; @@ -2296,31 +2291,74 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_b drflac_assert(count > 0); drflac_assert(pSamplesOut != NULL); - drflac_uint32 zeroCountPart; - drflac_uint32 riceParamPart; + static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - drflac_uint32 i = 0; + drflac_uint32 zeroCountPart0; + drflac_uint32 zeroCountPart1; + drflac_uint32 zeroCountPart2; + drflac_uint32 zeroCountPart3; + drflac_uint32 riceParamPart0; + drflac_uint32 riceParamPart1; + drflac_uint32 riceParamPart2; + drflac_uint32 riceParamPart3; + drflac_uint32 i4 = 0; + drflac_uint32 count4 = count >> 2; + while (i4 < count4) { + // Rice extraction. + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !drflac__read_rice_parts(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !drflac__read_rice_parts(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !drflac__read_rice_parts(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return DRFLAC_FALSE; + } + + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + + if (bitsPerSample > 16) { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3); + } else { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3); + } + + i4 += 1; + pSamplesOut += 4; + } + + drflac_uint32 i = i4 << 2; while (i < count) { // Rice extraction. - if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { return DRFLAC_FALSE; } // Rice reconstruction. - static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - - riceParamPart |= (zeroCountPart << riceParam); - riceParamPart = (riceParamPart >> 1) ^ t[riceParamPart & 0x01]; - //riceParamPart = (riceParamPart >> 1) ^ (~(riceParamPart & 0x01) + 1); + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + //riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1); // Sample reconstruction. if (bitsPerSample > 16) { - pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); } else { - pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); } i += 1; + pSamplesOut += 1; } return DRFLAC_TRUE; @@ -2402,6 +2440,16 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ return DRFLAC_FALSE; } + // From the FLAC spec: + // The Rice partition order in a Rice-coded residual section must be less than or equal to 8. + if (partitionOrder > 8) { + return DRFLAC_FALSE; + } + + // Validation check. + if ((blockSize / (1 << partitionOrder)) <= order) { + return DRFLAC_FALSE; + } drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; drflac_uint32 partitionsRemaining = (1 << partitionOrder); @@ -2446,7 +2494,10 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ } partitionsRemaining -= 1; - samplesInPartition = blockSize / (1 << partitionOrder); + + if (partitionOrder != 0) { + samplesInPartition = blockSize / (1 << partitionOrder); + } } return DRFLAC_TRUE; @@ -2982,7 +3033,17 @@ static drflac_result drflac__decode_frame(drflac* pFlac) // This function should be called while the stream is sitting on the first byte after the frame header. drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes)); + // The frame block size must never be larger than the maximum block size defined by the FLAC stream. + if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) { + return DRFLAC_ERROR; + } + + // The number of channels in the frame must match the channel count from the STREAMINFO block. int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + if (channelCount != (int)pFlac->channels) { + return DRFLAC_ERROR; + } + for (int i = 0; i < channelCount; ++i) { if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) { return DRFLAC_ERROR; @@ -3098,6 +3159,8 @@ static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame)); + pFlac->currentSample = 0; + return result; } @@ -3110,18 +3173,42 @@ static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac) static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex) { - // We need to find the frame that contains the sample. To do this, we iterate over each frame and inspect it's header. If based on the - // header we can determine that the frame contains the sample, we do a full decode of that frame. - if (!drflac__seek_to_first_frame(pFlac)) { - return DRFLAC_FALSE; - } + drflac_assert(pFlac != NULL); - drflac_uint64 runningSampleCount = 0; - for (;;) { - if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + drflac_bool32 isMidFrame = DRFLAC_FALSE; + + // If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file. + drflac_uint64 runningSampleCount; + if (sampleIndex >= pFlac->currentSample) { + // Seeking forward. Need to seek from the current position. + runningSampleCount = pFlac->currentSample; + + // The frame header for the first frame may not yet have been read. We need to do that if necessary. + if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; + } + } else { + // Seeking backwards. Need to seek from the start of the file. + runningSampleCount = 0; + + // Move back to the start. + if (!drflac__seek_to_first_frame(pFlac)) { return DRFLAC_FALSE; } + // Decode the first frame in preparation for sample-exact seeking below. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } + + // We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its + // header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame. + for (;;) { drflac_uint64 firstSampleInFrame = 0; drflac_uint64 lastSampleInFrame = 0; drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); @@ -3130,35 +3217,52 @@ static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_u if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend // it never existed and keep iterating. - drflac_result result = drflac__decode_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. - drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. - if (samplesToDecode == 0) { - return DRFLAC_TRUE; - } - return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. + drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail). } else { - return DRFLAC_FALSE; + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } } + } else { + // We started seeking mid-frame which means we need to skip the frame decoding part. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; } } else { // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this // frame never existed and leave the running sample count untouched. - drflac_result result = drflac__seek_to_next_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningSampleCount += sampleCountInThisFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; } else { - return DRFLAC_FALSE; + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } } + } else { + // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header. + runningSampleCount += pFlac->currentFrame.samplesRemaining; + pFlac->currentFrame.samplesRemaining = 0; + isMidFrame = DRFLAC_FALSE; } } + + next_iteration: + // Grab the next frame in preparation for the next iteration. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } } } @@ -3167,95 +3271,107 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui { drflac_assert(pFlac != NULL); - if (pFlac->seektablePos == 0) { + if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { return DRFLAC_FALSE; } - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->seektablePos)) { - return DRFLAC_FALSE; - } - // The number of seek points is derived from the size of the SEEKTABLE block. - drflac_uint32 seekpointCount = pFlac->seektableSize / 18; // 18 = the size of each seek point. - if (seekpointCount == 0) { - return DRFLAC_FALSE; // Would this ever happen? - } - - - drflac_seekpoint closestSeekpoint = {0, 0, 0}; - - drflac_uint32 seekpointsRemaining = seekpointCount; - while (seekpointsRemaining > 0) { - drflac_seekpoint seekpoint; - if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) { - break; - } - if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) { - break; - } - if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) { + drflac_uint32 iClosestSeekpoint = 0; + for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + if (pFlac->pSeekpoints[iSeekpoint].firstSample*pFlac->channels >= sampleIndex) { break; } - // Note that the seekpoint sample is based on a single channel. The input sample (sampleIndex) is based on interleaving, thus - // we need to multiple the seekpoint's sample by the channel count. - if (seekpoint.firstSample*pFlac->channels > sampleIndex) { - break; + iClosestSeekpoint = iSeekpoint; + } + + + drflac_bool32 isMidFrame = DRFLAC_FALSE; + + // At this point we should have found the seekpoint closest to our sample. If we are seeking forward and the closest seekpoint is _before_ the current sample, we + // just seek forward from where we are. Otherwise we start seeking from the seekpoint's first sample. + drflac_uint64 runningSampleCount; + if ((sampleIndex >= pFlac->currentSample) && (pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels <= pFlac->currentSample)) { + // Optimized case. Just seek forward from where we are. + runningSampleCount = pFlac->currentSample; + + // The frame header for the first frame may not yet have been read. We need to do that if necessary. + if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; } + } else { + // Slower case. Seek to the start of the seekpoint and then seek forward from there. + runningSampleCount = pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels; - closestSeekpoint = seekpoint; - seekpointsRemaining -= 1; - } - - // At this point we should have found the seekpoint closest to our sample. We need to seek to it using basically the same - // technique as we use with the brute force method. - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + closestSeekpoint.frameOffset)) { - return DRFLAC_FALSE; - } - - drflac_uint64 runningSampleCount = closestSeekpoint.firstSample*pFlac->channels; - for (;;) { - if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + pFlac->pSeekpoints[iClosestSeekpoint].frameOffset)) { return DRFLAC_FALSE; } + // Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } + + for (;;) { drflac_uint64 firstSampleInFrame = 0; drflac_uint64 lastSampleInFrame = 0; drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { - // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend // it never existed and keep iterating. - drflac_result result = drflac__decode_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. - drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. - if (samplesToDecode == 0) { - return DRFLAC_TRUE; - } - return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. + drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail). } else { - return DRFLAC_FALSE; + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } } + } else { + // We started seeking mid-frame which means we need to skip the frame decoding part. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; } } else { // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this // frame never existed and leave the running sample count untouched. - drflac_result result = drflac__seek_to_next_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningSampleCount += sampleCountInThisFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; } else { - return DRFLAC_FALSE; + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } } + } else { + // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header. + runningSampleCount += pFlac->currentFrame.samplesRemaining; + pFlac->currentFrame.samplesRemaining = 0; + isMidFrame = DRFLAC_FALSE; } } + + next_iteration: + // Grab the next frame in preparation for the next iteration. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } } } @@ -3363,10 +3479,8 @@ drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, return DRFLAC_TRUE; } -drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) +drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize) { - drflac_assert(pFlac != NULL); - // We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that // we'll be sitting on byte 42. drflac_uint64 runningFilePos = 42; @@ -3377,7 +3491,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) drflac_uint8 isLastBlock = 0; drflac_uint8 blockType; drflac_uint32 blockSize; - if (!drflac__read_and_decode_block_header(pFlac->bs.onRead, pFlac->bs.pUserData, &isLastBlock, &blockType, &blockSize)) { + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { return DRFLAC_FALSE; } runningFilePos += 4; @@ -3392,13 +3506,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) { case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: { - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3408,7 +3522,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3419,13 +3533,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) seektablePos = runningFilePos; seektableSize = blockSize; - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3443,7 +3557,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount); } - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3451,13 +3565,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: { - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3470,7 +3584,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.vorbis_comment.comments = pRunningData; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3478,13 +3592,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: { - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3498,7 +3612,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3506,13 +3620,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: { - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3532,7 +3646,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.data.picture.indexColorCount = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.picture.pictureDataSize = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3540,14 +3654,14 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_PADDING: { - if (pFlac->onMeta) { + if (onMeta) { metadata.data.padding.unused = 0; // Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. } else { - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); } } } break; @@ -3555,31 +3669,31 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_INVALID: { // Invalid chunk. Just skip over this one. - if (pFlac->onMeta) { - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. + if (onMeta) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. } } - } + } break; default: { // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we // can at the very least report the chunk to the application and let it look at the raw data. - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3587,8 +3701,8 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) } // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. - if (pFlac->onMeta == NULL && blockSize > 0) { - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { + if (onMeta == NULL && blockSize > 0) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { isLastBlock = DRFLAC_TRUE; } } @@ -3599,9 +3713,9 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) } } - pFlac->seektablePos = seektablePos; - pFlac->seektableSize = seektableSize; - pFlac->firstFramePos = runningFilePos; + *pSeektablePos = seektablePos; + *pSeektableSize = seektableSize; + *pFirstFramePos = runningFilePos; return DRFLAC_TRUE; } @@ -3891,7 +4005,7 @@ drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserD // The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works -// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is architecured +// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed // in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type // dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from // the physical Ogg bitstream are converted and delivered in native FLAC format. @@ -3998,6 +4112,8 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og return DRFLAC_FALSE; } } +#else + (void)recoveryMethod; // <-- Silence a warning. #endif oggbs->currentPageHeader = header; @@ -4237,13 +4353,13 @@ drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleInde // // Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg // bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the - // standard drflac__*() APIs because that will read in extra data for it's own internal caching which in turn breaks + // standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks // the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read // using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to // avoid the use of the drflac_bs object. // // Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: - // 1) Seeking is already partially accellerated using Ogg's paging system in the code block above. + // 1) Seeking is already partially accelerated using Ogg's paging system in the code block above. // 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. // 3) Simplicity. if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { @@ -4436,7 +4552,7 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next - // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialiation phase for Ogg is to create the + // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the // Ogg bistream object. pInit->hasMetadataBlocks = DRFLAC_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block. return DRFLAC_TRUE; @@ -4578,43 +4694,116 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p #ifndef DR_FLAC_NO_OGG // There's additional data required for Ogg streams. + drflac_uint32 oggbsAllocationSize = 0; if (init.container == drflac_container_ogg) { - allocationSize += sizeof(drflac_oggbs); + oggbsAllocationSize = sizeof(drflac_oggbs); + allocationSize += oggbsAllocationSize; + } + + drflac_oggbs oggbs; + drflac_zero_memory(&oggbs, sizeof(oggbs)); + if (init.container == drflac_container_ogg) { + oggbs.onRead = onRead; + oggbs.onSeek = onSeek; + oggbs.pUserData = pUserData; + oggbs.currentBytePos = init.oggFirstBytePos; + oggbs.firstBytePos = init.oggFirstBytePos; + oggbs.serialNumber = init.oggSerial; + oggbs.bosPageHeader = init.oggBosHeader; + oggbs.bytesRemainingInPage = 0; } #endif + // This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to + // consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading + // and decoding the metadata. + drflac_uint64 firstFramePos = 42; // <-- We know we are at byte 42 at this point. + drflac_uint64 seektablePos = 0; + drflac_uint32 seektableSize = 0; + if (init.hasMetadataBlocks) { + drflac_read_proc onReadOverride = onRead; + drflac_seek_proc onSeekOverride = onSeek; + void* pUserDataOverride = pUserData; + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + onReadOverride = drflac__on_read_ogg; + onSeekOverride = drflac__on_seek_ogg; + pUserDataOverride = (void*)&oggbs; + } +#endif + + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize)) { + return NULL; + } + + allocationSize += seektableSize; + } + + drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize); drflac__init_from_info(pFlac, &init); pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - oggbs->onRead = onRead; - oggbs->onSeek = onSeek; - oggbs->pUserData = pUserData; - oggbs->currentBytePos = init.oggFirstBytePos; - oggbs->firstBytePos = init.oggFirstBytePos; - oggbs->serialNumber = init.oggSerial; - oggbs->bosPageHeader = init.oggBosHeader; - oggbs->bytesRemainingInPage = 0; + drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize); + *pInternalOggbs = oggbs; // The Ogg bistream needs to be layered on top of the original bitstream. pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; - pFlac->bs.pUserData = (void*)oggbs; - pFlac->_oggbs = (void*)oggbs; + pFlac->bs.pUserData = (void*)pInternalOggbs; + pFlac->_oggbs = (void*)pInternalOggbs; } #endif - // Decode metadata before returning. - if (init.hasMetadataBlocks) { - if (!drflac__read_and_decode_metadata(pFlac)) { - DRFLAC_FREE(pFlac); - return NULL; + pFlac->firstFramePos = firstFramePos; + + // NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now. +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) + { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + else +#endif + { + // If we have a seektable we need to load it now, making sure we move back to where we were previously. + if (seektablePos != 0) { + pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints); + pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + + // Seek to the seektable, then just read directly into our seektable buffer. + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) { + // Endian swap. + for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + pFlac->pSeekpoints[iSeekpoint].firstSample = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstSample); + pFlac->pSeekpoints[iSeekpoint].frameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].frameOffset); + pFlac->pSeekpoints[iSeekpoint].sampleCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].sampleCount); + } + } else { + // Failed to read the seektable. Pretend we don't have one. + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + + // We need to seek back to where we were. If this fails it's a critical error. + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFramePos, drflac_seek_origin_start)) { + return NULL; + } + } else { + // Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one. + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } } } + + // If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode // the first frame. if (!init.hasStreamInfoBlock) { @@ -4782,6 +4971,7 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_ drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; drflac_assert(memoryStream != NULL); drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + drflac_assert(offset <= (drflac_int64)memoryStream->dataSize); if (origin == drflac_seek_origin_current) { if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { @@ -4998,18 +5188,25 @@ drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 sampl break; // Couldn't read the next frame, so just break from the loop and return. } } else { - samplesRead += 1; - pFlac->currentFrame.samplesRemaining -= 1; - samplesToRead -= 1; + if (pFlac->currentFrame.samplesRemaining > samplesToRead) { + samplesRead += samplesToRead; + pFlac->currentFrame.samplesRemaining -= (drflac_uint32)samplesToRead; // <-- Safe cast. Will always be < currentFrame.samplesRemaining < 65536. + samplesToRead = 0; + } else { + samplesRead += pFlac->currentFrame.samplesRemaining; + samplesToRead -= pFlac->currentFrame.samplesRemaining; + pFlac->currentFrame.samplesRemaining = 0; + } } } + pFlac->currentSample += samplesRead; return samplesRead; } drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) { - // Note that is allowed to be null, in which case this will be treated as something like a seek. + // Note that is allowed to be null, in which case this will act like a seek. if (pFlac == NULL || samplesToRead == 0) { return 0; } @@ -5036,10 +5233,11 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; if (misalignedSampleCount > 0) { drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); - samplesRead += misalignedSamplesRead; + samplesRead += misalignedSamplesRead; samplesReadFromFrameSoFar += misalignedSamplesRead; - bufferOut += misalignedSamplesRead; - samplesToRead -= misalignedSamplesRead; + bufferOut += misalignedSamplesRead; + samplesToRead -= misalignedSamplesRead; + pFlac->currentSample += misalignedSamplesRead; } @@ -5124,14 +5322,14 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac } drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount; - samplesRead += alignedSamplesRead; + samplesRead += alignedSamplesRead; samplesReadFromFrameSoFar += alignedSamplesRead; - bufferOut += alignedSamplesRead; - samplesToRead -= alignedSamplesRead; + bufferOut += alignedSamplesRead; + samplesToRead -= alignedSamplesRead; + pFlac->currentSample += alignedSamplesRead; pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead; - // At this point we may still have some excess samples left to read. if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) { drflac_uint64 excessSamplesRead = 0; @@ -5141,10 +5339,11 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut); } - samplesRead += excessSamplesRead; + samplesRead += excessSamplesRead; samplesReadFromFrameSoFar += excessSamplesRead; - bufferOut += excessSamplesRead; - samplesToRead -= excessSamplesRead; + bufferOut += excessSamplesRead; + samplesToRead -= excessSamplesRead; + pFlac->currentSample += excessSamplesRead; } } } @@ -5170,8 +5369,8 @@ drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac } totalSamplesRead += samplesJustRead; - samplesToRead -= samplesJustRead; - pBufferOut += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; } return totalSamplesRead; @@ -5195,8 +5394,8 @@ drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* } totalSamplesRead += samplesJustRead; - samplesToRead -= samplesJustRead; - pBufferOut += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; } return totalSamplesRead; @@ -5215,33 +5414,57 @@ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) } if (sampleIndex == 0) { + pFlac->currentSample = 0; return drflac__seek_to_first_frame(pFlac); - } + } else { + drflac_bool32 wasSuccessful = DRFLAC_FALSE; - // Clamp the sample to the end. - if (sampleIndex >= pFlac->totalSampleCount) { - sampleIndex = pFlac->totalSampleCount - 1; - } - - - // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so - // we'll instead use Ogg's natural seeking facility. -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) - { - return drflac_ogg__seek_to_sample(pFlac, sampleIndex); - } - else -#endif - { - // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. - if (!drflac__seek_to_sample__seek_table(pFlac, sampleIndex)) { - return drflac__seek_to_sample__brute_force(pFlac, sampleIndex); + // Clamp the sample to the end. + if (sampleIndex >= pFlac->totalSampleCount) { + sampleIndex = pFlac->totalSampleCount - 1; } + + // If the target sample and the current sample are in the same frame we just move the position forward. + if (sampleIndex > pFlac->currentSample) { + // Forward. + drflac_uint32 offset = (drflac_uint32)(sampleIndex - pFlac->currentSample); + if (pFlac->currentFrame.samplesRemaining > offset) { + pFlac->currentFrame.samplesRemaining -= offset; + pFlac->currentSample = sampleIndex; + return DRFLAC_TRUE; + } + } else { + // Backward. + drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentSample - sampleIndex); + drflac_uint32 currentFrameSampleCount = pFlac->currentFrame.header.blockSize * drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + drflac_uint32 currentFrameSamplesConsumed = (drflac_uint32)(currentFrameSampleCount - pFlac->currentFrame.samplesRemaining); + if (currentFrameSamplesConsumed > offsetAbs) { + pFlac->currentFrame.samplesRemaining += offsetAbs; + pFlac->currentSample = sampleIndex; + return DRFLAC_TRUE; + } + } + + // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so + // we'll instead use Ogg's natural seeking facility. + #ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + wasSuccessful = drflac_ogg__seek_to_sample(pFlac, sampleIndex); + } + else + #endif + { + // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. + wasSuccessful = drflac__seek_to_sample__seek_table(pFlac, sampleIndex); + if (!wasSuccessful) { + wasSuccessful = drflac__seek_to_sample__brute_force(pFlac, sampleIndex); + } + } + + pFlac->currentSample = sampleIndex; + return wasSuccessful; } - - - return DRFLAC_TRUE; } @@ -5498,6 +5721,42 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr // REVISION HISTORY // +// v0.9.7 - 2018-07-05 +// - Fix a warning. +// +// v0.9.6 - 2018-06-29 +// - Fix some typos. +// +// v0.9.5 - 2018-06-23 +// - Fix some warnings. +// +// v0.9.4 - 2018-06-14 +// - Optimizations to seeking. +// - Clean up. +// +// v0.9.3 - 2018-05-22 +// - Bug fix. +// +// v0.9.2 - 2018-05-12 +// - Fix a compilation error due to a missing break statement. +// +// v0.9.1 - 2018-04-29 +// - Fix compilation error with Clang. +// +// v0.9 - 2018-04-24 +// - Fix Clang build. +// - Start using major.minor.revision versioning. +// +// v0.8g - 2018-04-19 +// - Fix build on non-x86/x64 architectures. +// +// v0.8f - 2018-02-02 +// - Stop pretending to support changing rate/channels mid stream. +// +// v0.8e - 2018-02-01 +// - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream. +// - Fix a crash the the Rice partition order is invalid. +// // v0.8d - 2017-09-22 // - Add support for decoding streams with ID3 tags. ID3 tags are just skipped. // diff --git a/raylib/external/dr_mp3.h b/raylib/external/dr_mp3.h index 564bf37..467d319 100644 --- a/raylib/external/dr_mp3.h +++ b/raylib/external/dr_mp3.h @@ -1,5 +1,5 @@ // MP3 audio decoder. Public domain. See "unlicense" statement at the end of this file. -// dr_mp3 - v0.2.3 - 2018-04-29 +// dr_mp3 - v0.2.5 - 2018-06-22 // // David Reid - mackron@gmail.com // @@ -339,7 +339,7 @@ void drmp3_free(void* p); #define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) #define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) #define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) -#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) #define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) #define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) @@ -412,10 +412,10 @@ static int drmp3_have_simd() #ifdef MINIMP3_TEST static int g_counter; if (g_counter++ > 100) - goto test_nosimd; + return 0; #endif if (g_have_simd) - return g_have_simd - 1; + goto end; drmp3_cpuid(CPUInfo, 0); if (CPUInfo[0] > 0) { @@ -423,11 +423,9 @@ static int drmp3_have_simd() g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */ return g_have_simd - 1; } -#ifdef MINIMP3_TEST -test_nosimd: -#endif - g_have_simd = 1; - return 0; + +end: + return g_have_simd - 1; #endif } #elif defined(__ARM_NEON) || defined(__aarch64__) @@ -760,8 +758,7 @@ static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) { - static const drmp3_uint8 g_scf_long[9][23] = { - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + static const drmp3_uint8 g_scf_long[8][23] = { { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, @@ -771,8 +768,7 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } }; - static const drmp3_uint8 g_scf_short[9][40] = { - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + static const drmp3_uint8 g_scf_short[8][40] = { { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, @@ -782,8 +778,7 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } }; - static const drmp3_uint8 g_scf_mixed[9][40] = { - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + static const drmp3_uint8 g_scf_mixed[8][40] = { { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, @@ -796,7 +791,7 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm unsigned tables, scfsi = 0; int main_data_begin, part_23_sum = 0; - int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); + int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; if (DRMP3_HDR_TEST_MPEG1(hdr)) @@ -1020,25 +1015,25 @@ static float drmp3_L3_pow_43(int x) static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) { static const float g_pow43_signed[32] = { 0,0,1,-1,2.519842f,-2.519842f,4.326749f,-4.326749f,6.349604f,-6.349604f,8.549880f,-8.549880f,10.902724f,-10.902724f,13.390518f,-13.390518f,16.000000f,-16.000000f,18.720754f,-18.720754f,21.544347f,-21.544347f,24.463781f,-24.463781f,27.473142f,-27.473142f,30.567351f,-30.567351f,33.741992f,-33.741992f,36.993181f,-36.993181f }; - static const drmp3_int16 tab0[32] = { 0, }; - static const drmp3_int16 tab1[] = { 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256 }; - static const drmp3_int16 tab2[] = { -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288 }; - static const drmp3_int16 tab3[] = { -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288 }; - static const drmp3_int16 tab5[] = { -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258 }; - static const drmp3_int16 tab6[] = { -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259 }; - static const drmp3_int16 tab7[] = { -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258 }; - static const drmp3_int16 tab8[] = { -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258 }; - static const drmp3_int16 tab9[] = { -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259 }; - static const drmp3_int16 tab10[] = { -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258 }; - static const drmp3_int16 tab11[] = { -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290 }; - static const drmp3_int16 tab12[] = { -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259 }; - static const drmp3_int16 tab13[] = { -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258 }; - static const drmp3_int16 tab15[] = { -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259 }; - static const drmp3_int16 tab16[] = { -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258 }; - static const drmp3_int16 tab24[] = { -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; + static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, + -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, + -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, + -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, + -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, + -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, + -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, + -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, + -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, + -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, + -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, + -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, + -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, + -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, + -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; - static const drmp3_int16 * const tabindex[2*16] = { tab0,tab1,tab2,tab3,tab0,tab5,tab6,tab7,tab8,tab9,tab10,tab11,tab12,tab13,tab0,tab15,tab16,tab16,tab16,tab16,tab16,tab16,tab16,tab16,tab24,tab24,tab24,tab24,tab24,tab24,tab24,tab24 }; + static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; #define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n)) @@ -1058,7 +1053,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g { int tab_num = gr_info->table_select[ireg]; int sfb_cnt = gr_info->region_count[ireg++]; - const short *codebook = tabindex[tab_num]; + const short *codebook = tabs + tabindex[tab_num]; int linbits = g_linbits[tab_num]; do { @@ -1227,7 +1222,7 @@ static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const d int prev = itop - max_blocks; ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); } - drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress&1); + drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); } static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) @@ -2776,6 +2771,12 @@ void drmp3_free(void* p) // REVISION HISTORY // =============== // +// v0.2.5 - 2018-06-22 +// - Bring up to date with minimp3. +// +// v0.2.4 - 2018-05-12 +// - Bring up to date with minimp3. +// // v0.2.3 - 2018-04-29 // - Fix TCC build. // diff --git a/raylib/external/dr_wav.h b/raylib/external/dr_wav.h index 536df8f..b071d00 100644 --- a/raylib/external/dr_wav.h +++ b/raylib/external/dr_wav.h @@ -1,5 +1,5 @@ // WAV audio loader and writer. Public domain. See "unlicense" statement at the end of this file. -// dr_wav - v0.7a - 2017-11-17 +// dr_wav - v0.8.1 - 2018-06-29 // // David Reid - mackron@gmail.com @@ -50,7 +50,7 @@ // drwav_free(pSampleData); // // The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in -// this case), but you can still output the audio data in it's internal format (see notes below for supported formats): +// this case), but you can still output the audio data in its internal format (see notes below for supported formats): // // size_t samplesRead = drwav_read(&wav, wav.totalSampleCount, pDecodedInterleavedSamples); // @@ -103,8 +103,8 @@ // - Signed 16-bit PCM // - Signed 24-bit PCM // - Signed 32-bit PCM -// - IEEE 32-bit floating point. -// - IEEE 64-bit floating point. +// - IEEE 32-bit floating point +// - IEEE 64-bit floating point // - A-law and u-law // - Microsoft ADPCM // - IMA ADPCM (DVI, format code 0x11) @@ -246,7 +246,7 @@ typedef struct // Block align. This is equal to the number of channels * bytes per sample. drwav_uint16 blockAlign; - // Bit's per sample. + // Bits per sample. drwav_uint16 bitsPerSample; // The size of the extended data. Only used internally for validation, but left here for informational purposes. @@ -292,7 +292,7 @@ typedef struct // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. drwav_uint16 channels; - // The bits per sample. Will be set to somthing like 16, 24, etc. + // The bits per sample. Will be set to something like 16, 24, etc. drwav_uint16 bitsPerSample; // The number of bytes per sample. @@ -316,6 +316,14 @@ typedef struct drwav_uint64 bytesRemaining; + // Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always + // set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation. + drwav_uint64 dataChunkDataSizeTargetWrite; + + // Keeps track of whether or not the wav writer was initialized in sequential mode. + drwav_bool32 isSequentialWrite; + + // A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_open_memory(). drwav__memory_stream memoryStream; drwav__memory_stream_write memoryStreamWrite; @@ -381,11 +389,15 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS // This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory() // to open the stream from a file or from a block of memory respectively. // +// If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform +// a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek. +// // If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate // a drwav object on the heap and return a pointer to it. // // See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit() drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData); +drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData); // Uninitializes the given drwav object. // @@ -403,8 +415,8 @@ void drwav_uninit(drwav* pWav); // // Close the loader with drwav_close(). // -// This is the lowest level function for opening a WAV file. You can also use drwav_open_file() and drwav_open_memory() -// to open the stream from a file or from a block of memory respectively. +// You can also use drwav_open_file() and drwav_open_memory() to open the stream from a file or from a block of +// memory respectively. // // This is different from drwav_init() in that it will allocate the drwav object for you via DRWAV_MALLOC() before // initializing it. @@ -422,14 +434,15 @@ drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserDat // // Close the loader with drwav_close(). // -// This is the lowest level function for opening a WAV file. You can also use drwav_open_file_write() and drwav_open_memory_write() -// to open the stream from a file or from a block of memory respectively. +// You can also use drwav_open_file_write() and drwav_open_memory_write() to open the stream from a file or from a block +// of memory respectively. // // This is different from drwav_init_write() in that it will allocate the drwav object for you via DRWAV_MALLOC() before // initializing it. // // See also: drwav_open_file_write(), drwav_open_memory_write(), drwav_close() drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData); +drwav* drwav_open_write_sequential(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData); // Uninitializes and deletes the the given drwav object. // @@ -478,7 +491,7 @@ drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* p -//// Convertion Utilities //// +//// Conversion Utilities //// #ifndef DR_WAV_NO_CONVERSION_API // Reads a chunk of audio data and converts it to signed 16-bit PCM samples. @@ -587,6 +600,7 @@ drwav_bool32 drwav_init_file(drwav* pWav, const char* filename); // objects because the operating system may restrict the number of file handles an application can have open at // any given time. drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat); +drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); // Helper for opening a wave file using stdio. // @@ -601,6 +615,7 @@ drwav* drwav_open_file(const char* filename); // objects because the operating system may restrict the number of file handles an application can have open at // any given time. drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat); +drwav* drwav_open_file_write_sequential(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); #endif //DR_WAV_NO_STDIO @@ -619,6 +634,7 @@ drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize); // The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be // considered valid until after drwav_uninit() has been called anyway. drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat); +drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); // Helper for opening a loader from a pre-allocated memory buffer. // @@ -635,6 +651,7 @@ drwav* drwav_open_memory(const void* data, size_t dataSize); // The buffer will remain allocated even after drwav_close() is called. Indeed, the buffer should not be // considered valid until after drwav_close() has been called anyway. drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat); +drwav* drwav_open_memory_write_sequential(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); #ifndef DR_WAV_NO_CONVERSION_API @@ -643,13 +660,13 @@ drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onS float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); #ifndef DR_WAV_NO_STDIO -// Opens an decodes a wav file in a single operation. +// Opens and decodes a wav file in a single operation. drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); drwav_int32* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); #endif -// Opens an decodes a wav file from a block of memory in a single operation. +// Opens and decodes a wav file from a block of memory in a single operation. drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); drwav_int32* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); @@ -820,6 +837,11 @@ static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 fo } +drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); +drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); +drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData); +drwav* drwav_open_write__internal(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData); + typedef struct { union @@ -864,7 +886,7 @@ static drwav_bool32 drwav__read_chunk_header(drwav_read_proc onRead, void* pUser pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; // <-- Subtract 24 because w64 includes the size of the header. pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 8); - pRunningBytesReadOut += 24; + *pRunningBytesReadOut += 24; } return DRWAV_TRUE; @@ -899,8 +921,8 @@ static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSe } - // Skip junk chunks. - if ((container == drwav_container_riff && drwav__fourcc_equal(header.id.fourcc, "JUNK")) || (container == drwav_container_w64 && drwav__guid_equal(header.id.guid, drwavGUID_W64_JUNK))) { + // Skip non-fmt chunks. + if ((container == drwav_container_riff && !drwav__fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT))) { if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { return DRWAV_FALSE; } @@ -996,6 +1018,23 @@ static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSe #ifndef DR_WAV_NO_STDIO +FILE* drwav_fopen(const char* filePath, const char* openMode) +{ + FILE* pFile; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (fopen_s(&pFile, filePath, openMode) != 0) { + return DRWAV_FALSE; + } +#else + pFile = fopen(filePath, openMode); + if (pFile == NULL) { + return DRWAV_FALSE; + } +#endif + + return pFile; +} + static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); @@ -1013,51 +1052,41 @@ static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek drwav_bool32 drwav_init_file(drwav* pWav, const char* filename) { - FILE* pFile; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (fopen_s(&pFile, filename, "rb") != 0) { - return DRWAV_FALSE; - } -#else - pFile = fopen(filename, "rb"); + FILE* pFile = drwav_fopen(filename, "rb"); if (pFile == NULL) { return DRWAV_FALSE; } -#endif return drwav_init(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile); } -drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat) + +drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential) { - FILE* pFile; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (fopen_s(&pFile, filename, "wb") != 0) { - return DRWAV_FALSE; - } -#else - pFile = fopen(filename, "wb"); + FILE* pFile = drwav_fopen(filename, "wb"); if (pFile == NULL) { return DRWAV_FALSE; } -#endif - return drwav_init_write(pWav, pFormat, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile); + return drwav_init_write__internal(pWav, pFormat, totalSampleCount, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile); +} + +drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat) +{ + return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE); +} + +drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) +{ + return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE); } drwav* drwav_open_file(const char* filename) { - FILE* pFile; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (fopen_s(&pFile, filename, "rb") != 0) { - return NULL; - } -#else - pFile = fopen(filename, "rb"); + FILE* pFile = drwav_fopen(filename, "rb"); if (pFile == NULL) { - return NULL; + return DRWAV_FALSE; } -#endif drwav* pWav = drwav_open(drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile); if (pWav == NULL) { @@ -1068,21 +1097,15 @@ drwav* drwav_open_file(const char* filename) return pWav; } -drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat) -{ - FILE* pFile; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (fopen_s(&pFile, filename, "wb") != 0) { - return NULL; - } -#else - pFile = fopen(filename, "wb"); - if (pFile == NULL) { - return NULL; - } -#endif - drwav* pWav = drwav_open_write(pFormat, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile); +drwav* drwav_open_file_write__internal(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential) +{ + FILE* pFile = drwav_fopen(filename, "wb"); + if (pFile == NULL) { + return DRWAV_FALSE; + } + + drwav* pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile); if (pWav == NULL) { fclose(pFile); return NULL; @@ -1090,6 +1113,16 @@ drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFor return pWav; } + +drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat) +{ + return drwav_open_file_write__internal(filename, pFormat, 0, DRWAV_FALSE); +} + +drwav* drwav_open_file_write_sequential(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) +{ + return drwav_open_file_write__internal(filename, pFormat, totalSampleCount, DRWAV_TRUE); +} #endif //DR_WAV_NO_STDIO @@ -1229,7 +1262,8 @@ drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize) return DRWAV_TRUE; } -drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat) + +drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential) { if (ppData == NULL) { return DRWAV_FALSE; @@ -1246,7 +1280,7 @@ drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSi memoryStreamWrite.dataCapacity = 0; memoryStreamWrite.currentWritePos = 0; - if (!drwav_init_write(pWav, pFormat, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite)) { + if (!drwav_init_write__internal(pWav, pFormat, totalSampleCount, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite)) { return DRWAV_FALSE; } @@ -1255,6 +1289,17 @@ drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSi return DRWAV_TRUE; } +drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat) +{ + return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE); +} + +drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) +{ + return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE); +} + + drwav* drwav_open_memory(const void* data, size_t dataSize) { if (data == NULL || dataSize == 0) { @@ -1277,7 +1322,8 @@ drwav* drwav_open_memory(const void* data, size_t dataSize) return pWav; } -drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat) + +drwav* drwav_open_memory_write__internal(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential) { if (ppData == NULL) { return NULL; @@ -1294,7 +1340,7 @@ drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_dat memoryStreamWrite.dataCapacity = 0; memoryStreamWrite.currentWritePos = 0; - drwav* pWav = drwav_open_write(pFormat, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite); + drwav* pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite); if (pWav == NULL) { return NULL; } @@ -1304,6 +1350,16 @@ drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_dat return pWav; } +drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat) +{ + return drwav_open_memory_write__internal(ppData, pDataSize, pFormat, 0, DRWAV_FALSE); +} + +drwav* drwav_open_memory_write_sequential(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) +{ + return drwav_open_memory_write__internal(ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE); +} + drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData) { @@ -1389,12 +1445,17 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS } - // The next 24 bytes should be the "fmt " chunk. + // The next bytes should be the "fmt " chunk. drwav_fmt fmt; if (!drwav__read_fmt(onRead, onSeek, pUserData, pWav->container, &pWav->dataChunkDataPos, &fmt)) { return DRWAV_FALSE; // Failed to read the "fmt " chunk. } + // Basic validation. + if (fmt.sampleRate == 0 || fmt.channels == 0 || fmt.bitsPerSample == 0 || fmt.blockAlign == 0) { + return DRWAV_FALSE; // Invalid channel count. Probably an invalid WAV file. + } + // Translate the internal format. unsigned short translatedFormatTag = fmt.formatTag; @@ -1448,7 +1509,7 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS if (onRead(pUserData, &sampleCountFromFactChunk, 8) != 8) { return DRWAV_FALSE; } - pWav->dataChunkDataPos += 4; + pWav->dataChunkDataPos += 8; dataSize -= 8; } } @@ -1470,11 +1531,16 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS pWav->sampleRate = fmt.sampleRate; pWav->channels = fmt.channels; pWav->bitsPerSample = fmt.bitsPerSample; - pWav->bytesPerSample = (unsigned int)(fmt.blockAlign / fmt.channels); + pWav->bytesPerSample = fmt.blockAlign / fmt.channels; pWav->bytesRemaining = dataSize; pWav->translatedFormatTag = translatedFormatTag; pWav->dataChunkDataSize = dataSize; + // The bytes per sample should never be 0 at this point. This would indicate an invalid WAV file. + if (pWav->bytesPerSample == 0) { + return DRWAV_FALSE; + } + if (sampleCountFromFactChunk != 0) { pWav->totalSampleCount = sampleCountFromFactChunk * fmt.channels; } else { @@ -1489,18 +1555,26 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels); } } - - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + + // The way we calculate the bytes per sample does not make sense for compressed formats so we just set it to 0. + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { pWav->bytesPerSample = 0; } + // Some formats only support a certain number of channels. + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + if (pWav->channels > 2) { + return DRWAV_FALSE; + } + } + #ifdef DR_WAV_LIBSNDFILE_COMPAT // I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website), // it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count - // from the number of blocks, however this results in the inclusion of the extra silent samples at the end of the last block. The correct - // way to know the total sample count is to inspect the "fact" chunk which should always be present for compressed formats, and should + // from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct + // way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should // always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my - // correctness tests against libsndfile and is disabled by default. + // correctness tests against libsndfile, and is disabled by default. if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { drwav_uint64 blockCount = dataSize / fmt.blockAlign; pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2; // x2 because two samples per byte. @@ -1514,12 +1588,51 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS return DRWAV_TRUE; } -drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData) + +drwav_uint32 drwav_riff_chunk_size_riff(drwav_uint64 dataChunkSize) { - if (onWrite == NULL || onSeek == NULL) { + if (dataChunkSize <= (0xFFFFFFFF - 36)) { + return 36 + (drwav_uint32)dataChunkSize; + } else { + return 0xFFFFFFFF; + } +} + +drwav_uint32 drwav_data_chunk_size_riff(drwav_uint64 dataChunkSize) +{ + if (dataChunkSize <= 0xFFFFFFFF) { + return (drwav_uint32)dataChunkSize; + } else { + return 0xFFFFFFFF; + } +} + +drwav_uint64 drwav_riff_chunk_size_w64(drwav_uint64 dataChunkSize) +{ + return 80 + 24 + dataChunkSize; // +24 because W64 includes the size of the GUID and size fields. +} + +drwav_uint64 drwav_data_chunk_size_w64(drwav_uint64 dataChunkSize) +{ + return 24 + dataChunkSize; // +24 because W64 includes the size of the GUID and size fields. +} + + +drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData) +{ + if (pWav == NULL) { return DRWAV_FALSE; } + if (onWrite == NULL) { + return DRWAV_FALSE; + } + + if (!isSequential && onSeek == NULL) { + return DRWAV_FALSE; // <-- onSeek is required when in non-sequential mode. + } + + // Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { return DRWAV_FALSE; @@ -1536,20 +1649,42 @@ drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drw pWav->fmt.formatTag = (drwav_uint16)pFormat->format; pWav->fmt.channels = (drwav_uint16)pFormat->channels; pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) >> 3); - pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) >> 3); + pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); + pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; pWav->fmt.extendedSize = 0; + pWav->isSequentialWrite = isSequential; + size_t runningPos = 0; + // The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In + // sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non- + // sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek. + drwav_uint64 initialDataChunkSize = 0; + if (isSequential) { + initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; + + // The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64 + // so for the sake of simplicity I'm not doing any validation for that. + if (pFormat->container == drwav_container_riff) { + if (initialDataChunkSize > (0xFFFFFFFF - 36)) { + return DRWAV_FALSE; // Not enough room to store every sample. + } + } + } + + pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; + + // "RIFF" chunk. - drwav_uint64 chunkSizeRIFF = 0; if (pFormat->container == drwav_container_riff) { + drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; // +36 = "RIFF"+[RIFF Chunk Size]+"WAVE" + [sizeof "fmt " chunk] runningPos += pWav->onWrite(pUserData, "RIFF", 4); runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 4); runningPos += pWav->onWrite(pUserData, "WAVE", 4); } else { + drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; // +24 because W64 includes the size of the GUID and size fields. runningPos += pWav->onWrite(pUserData, drwavGUID_W64_RIFF, 16); runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 8); runningPos += pWav->onWrite(pUserData, drwavGUID_W64_WAVE, 16); @@ -1575,14 +1710,14 @@ drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drw runningPos += pWav->onWrite(pUserData, &pWav->fmt.bitsPerSample, 2); pWav->dataChunkDataPos = runningPos; - pWav->dataChunkDataSize = 0; // "data" chunk. - drwav_uint64 chunkSizeDATA = 0; if (pFormat->container == drwav_container_riff) { + drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; runningPos += pWav->onWrite(pUserData, "data", 4); runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 4); } else { + drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; // +24 because W64 includes the size of the GUID and size fields. runningPos += pWav->onWrite(pUserData, drwavGUID_W64_DATA, 16); runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 8); } @@ -1612,16 +1747,32 @@ drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drw return DRWAV_TRUE; } + +drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData) +{ + return drwav_init_write__internal(pWav, pFormat, 0, DRWAV_FALSE, onWrite, onSeek, pUserData); // DRWAV_FALSE = Not Sequential +} + +drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData) +{ + return drwav_init_write__internal(pWav, pFormat, totalSampleCount, DRWAV_TRUE, onWrite, NULL, pUserData); // DRWAV_TRUE = Sequential +} + void drwav_uninit(drwav* pWav) { if (pWav == NULL) { return; } - // If the drwav object was opened in write mode we'll need to finialize a few things: - // - Make sure the "data" chunk is aligned to 16-bits + // If the drwav object was opened in write mode we'll need to finalize a few things: + // - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers. // - Set the size of the "data" chunk. if (pWav->onWrite != NULL) { + // Validation for sequential mode. + if (pWav->isSequentialWrite) { + drwav_assert(pWav->dataChunkDataSize == pWav->dataChunkDataSizeTargetWrite); + } + // Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. drwav_uint32 paddingSize = 0; if (pWav->container == drwav_container_riff) { @@ -1636,42 +1787,31 @@ void drwav_uninit(drwav* pWav) } - // Chunk sizes. - if (pWav->onSeek) { + // Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need + // to do this when using non-sequential mode. + if (pWav->onSeek && !pWav->isSequentialWrite) { if (pWav->container == drwav_container_riff) { // The "RIFF" chunk size. if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { - drwav_uint32 riffChunkSize = 36; - if (pWav->dataChunkDataSize <= (0xFFFFFFFF - 36)) { - riffChunkSize = 36 + (drwav_uint32)pWav->dataChunkDataSize; - } else { - riffChunkSize = 0xFFFFFFFF; - } - + drwav_uint32 riffChunkSize = drwav_riff_chunk_size_riff(pWav->dataChunkDataSize); pWav->onWrite(pWav->pUserData, &riffChunkSize, 4); } // the "data" chunk size. if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) { - drwav_uint32 dataChunkSize = 0; - if (pWav->dataChunkDataSize <= 0xFFFFFFFF) { - dataChunkSize = (drwav_uint32)pWav->dataChunkDataSize; - } else { - dataChunkSize = 0xFFFFFFFF; - } - + drwav_uint32 dataChunkSize = drwav_data_chunk_size_riff(pWav->dataChunkDataSize); pWav->onWrite(pWav->pUserData, &dataChunkSize, 4); } } else { // The "RIFF" chunk size. if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { - drwav_uint64 riffChunkSize = 80 + 24 + pWav->dataChunkDataSize; + drwav_uint64 riffChunkSize = drwav_riff_chunk_size_w64(pWav->dataChunkDataSize); pWav->onWrite(pWav->pUserData, &riffChunkSize, 8); } // The "data" chunk size. if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) { - drwav_uint64 dataChunkSize = 24 + pWav->dataChunkDataSize; // +24 because W64 includes the size of the GUID and size fields. + drwav_uint64 dataChunkSize = drwav_data_chunk_size_w64(pWav->dataChunkDataSize); pWav->onWrite(pWav->pUserData, &dataChunkSize, 8); } } @@ -1703,14 +1843,15 @@ drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserDat return pWav; } -drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData) + +drwav* drwav_open_write__internal(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData) { drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav)); if (pWav == NULL) { return NULL; } - if (!drwav_init_write(pWav, pFormat, onWrite, onSeek, pUserData)) { + if (!drwav_init_write__internal(pWav, pFormat, totalSampleCount, isSequential, onWrite, onSeek, pUserData)) { DRWAV_FREE(pWav); return NULL; } @@ -1718,6 +1859,16 @@ drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWri return pWav; } +drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData) +{ + return drwav_open_write__internal(pFormat, 0, DRWAV_FALSE, onWrite, onSeek, pUserData); +} + +drwav* drwav_open_write_sequential(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData) +{ + return drwav_open_write__internal(pFormat, totalSampleCount, DRWAV_TRUE, onWrite, NULL, pUserData); +} + void drwav_close(drwav* pWav) { drwav_uninit(pWav); @@ -1763,6 +1914,10 @@ drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOu drwav_bool32 drwav_seek_to_first_sample(drwav* pWav) { + if (pWav->onWrite != NULL) { + return DRWAV_FALSE; // No seeking in write mode. + } + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { return DRWAV_FALSE; } @@ -1779,6 +1934,10 @@ drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample) { // Seeking should be compatible with wave files > 2GB. + if (pWav->onWrite != NULL) { + return DRWAV_FALSE; // No seeking in write mode. + } + if (pWav == NULL || pWav->onSeek == NULL) { return DRWAV_FALSE; } @@ -1798,27 +1957,40 @@ drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample) // to seek back to the start. if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { // TODO: This can be optimized. + + // If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards, + // we first need to seek back to the start and then just do the same thing as a forward seek. + if (sample < pWav->compressed.iCurrentSample) { + if (!drwav_seek_to_first_sample(pWav)) { + return DRWAV_FALSE; + } + } + if (sample > pWav->compressed.iCurrentSample) { - // Seeking forward - just move from the current position. drwav_uint64 offset = sample - pWav->compressed.iCurrentSample; drwav_int16 devnull[2048]; while (offset > 0) { - drwav_uint64 samplesToRead = sample; + drwav_uint64 samplesToRead = offset; if (samplesToRead > 2048) { samplesToRead = 2048; } - drwav_uint64 samplesRead = drwav_read_s16(pWav, samplesToRead, devnull); + drwav_uint64 samplesRead = 0; + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + samplesRead = drwav_read_s16__msadpcm(pWav, samplesToRead, devnull); + } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + samplesRead = drwav_read_s16__ima(pWav, samplesToRead, devnull); + } else { + assert(DRWAV_FALSE); // If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. + } + if (samplesRead != samplesToRead) { return DRWAV_FALSE; } offset -= samplesRead; } - } else { - // Seeking backwards. Just use the fallback. - goto fallback; } } else { drwav_uint64 totalSizeInBytes = pWav->totalSampleCount * pWav->bytesPerSample; @@ -1850,30 +2022,6 @@ drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample) } } - return DRWAV_TRUE; - -fallback: - // This is a generic seek implementation that just continuously reads samples into a temporary buffer. This should work for all supported - // formats, but it is not efficient. This should be used as a fall back. - if (!drwav_seek_to_first_sample(pWav)) { - return DRWAV_FALSE; - } - - drwav_int16 devnull[2048]; - while (sample > 0) { - drwav_uint64 samplesToRead = sample; - if (samplesToRead > 2048) { - samplesToRead = 2048; - } - - drwav_uint64 samplesRead = drwav_read_s16(pWav, samplesToRead, devnull); - if (samplesRead != samplesToRead) { - return DRWAV_FALSE; - } - - sample -= samplesRead; - } - return DRWAV_TRUE; } @@ -1901,138 +2049,29 @@ drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* p return 0; } - size_t bytesWritten = drwav_write_raw(pWav, (size_t)bytesToWrite, pData); - return ((drwav_uint64)bytesWritten * 8) / pWav->bitsPerSample; -} - - -#ifndef DR_WAV_NO_CONVERSION_API -static unsigned short g_drwavAlawTable[256] = { - 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, - 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, - 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, - 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, - 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, - 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, - 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, - 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, - 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, - 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, - 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, - 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, - 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, - 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, - 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, - 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 -}; - -static unsigned short g_drwavMulawTable[256] = { - 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, - 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, - 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, - 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, - 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, - 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, - 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, - 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, - 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, - 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, - 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, - 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, - 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, - 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, - 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, - 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 -}; - -static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) -{ - return (short)g_drwavAlawTable[sampleIn]; -} - -static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) -{ - return (short)g_drwavMulawTable[sampleIn]; -} - - - -static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) -{ - // Special case for 8-bit sample data because it's treated as unsigned. - if (bytesPerSample == 1) { - drwav_u8_to_s16(pOut, pIn, totalSampleCount); - return; - } - - - // Slightly more optimal implementation for common formats. - if (bytesPerSample == 2) { - for (unsigned int i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((drwav_int16*)pIn)[i]; - } - return; - } - if (bytesPerSample == 3) { - drwav_s24_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); - return; - } - - - // Generic, slow converter. - for (unsigned int i = 0; i < totalSampleCount; ++i) { - unsigned short sample = 0; - unsigned short shift = (8 - bytesPerSample) * 8; - for (unsigned short j = 0; j < bytesPerSample && j < 2; ++j) { - sample |= (unsigned short)(pIn[j]) << shift; - shift += 8; + drwav_uint64 bytesWritten = 0; + const drwav_uint8* pRunningData = (const drwav_uint8*)pData; + while (bytesToWrite > 0) { + drwav_uint64 bytesToWriteThisIteration = bytesToWrite; + if (bytesToWriteThisIteration > SIZE_MAX) { + bytesToWriteThisIteration = SIZE_MAX; } - pIn += bytesPerSample; - *pOut++ = sample; - } -} - -static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) -{ - if (bytesPerSample == 4) { - drwav_f32_to_s16(pOut, (float*)pIn, totalSampleCount); - return; - } else { - drwav_f64_to_s16(pOut, (double*)pIn, totalSampleCount); - return; - } -} - -drwav_uint64 drwav_read_s16__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) -{ - // Fast path. - if (pWav->bytesPerSample == 2) { - return drwav_read(pWav, samplesToRead, pBufferOut); - } - - drwav_uint64 totalSamplesRead = 0; - unsigned char sampleData[4096]; - while (samplesToRead > 0) { - drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); - if (samplesRead == 0) { + size_t bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); + if (bytesJustWritten == 0) { break; } - drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); - - pBufferOut += samplesRead; - samplesToRead -= samplesRead; - totalSamplesRead += samplesRead; + bytesToWrite -= bytesJustWritten; + bytesWritten += bytesJustWritten; + pRunningData += bytesJustWritten; } - return totalSamplesRead; + return (bytesWritten * 8) / pWav->bitsPerSample; } + + drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) { drwav_assert(pWav != NULL); @@ -2334,6 +2373,147 @@ drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_ return totalSamplesRead; } + +#ifndef DR_WAV_NO_CONVERSION_API +static unsigned short g_drwavAlawTable[256] = { + 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, + 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, + 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, + 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, + 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, + 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, + 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, + 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, + 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, + 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, + 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, + 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, + 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, + 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, + 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, + 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 +}; + +static unsigned short g_drwavMulawTable[256] = { + 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, + 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, + 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, + 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, + 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, + 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, + 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, + 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, + 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, + 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, + 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, + 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, + 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, + 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, + 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 +}; + +static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) +{ + return (short)g_drwavAlawTable[sampleIn]; +} + +static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) +{ + return (short)g_drwavMulawTable[sampleIn]; +} + + + +static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) +{ + // Special case for 8-bit sample data because it's treated as unsigned. + if (bytesPerSample == 1) { + drwav_u8_to_s16(pOut, pIn, totalSampleCount); + return; + } + + + // Slightly more optimal implementation for common formats. + if (bytesPerSample == 2) { + for (unsigned int i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((drwav_int16*)pIn)[i]; + } + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_s16(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); + return; + } + + + // Anything more than 64 bits per sample is not supported. + if (bytesPerSample > 8) { + drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + + + // Generic, slow converter. + for (unsigned int i = 0; i < totalSampleCount; ++i) { + drwav_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + + unsigned int j; + for (j = 0; j < bytesPerSample && j < 8; j += 1) { + sample |= (drwav_uint64)(pIn[j]) << shift; + shift += 8; + } + + pIn += j; + *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); + } +} + +static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) +{ + if (bytesPerSample == 4) { + drwav_f32_to_s16(pOut, (float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + drwav_f64_to_s16(pOut, (double*)pIn, totalSampleCount); + return; + } else { + // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. + drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} + +drwav_uint64 drwav_read_s16__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + // Fast path. + if (pWav->bytesPerSample == 2) { + return drwav_read(pWav, samplesToRead, pBufferOut); + } + + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + drwav_uint64 drwav_read_s16__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalSamplesRead = 0; @@ -2529,17 +2709,27 @@ static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t samp return; } + + // Anything more than 64 bits per sample is not supported. + if (bytesPerSample > 8) { + drwav_zero_memory(pOut, sampleCount * sizeof(*pOut)); + return; + } + + // Generic, slow converter. for (unsigned int i = 0; i < sampleCount; ++i) { - unsigned int sample = 0; + drwav_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; - for (unsigned short j = 0; j < bytesPerSample && j < 4; ++j) { - sample |= (unsigned int)(pIn[j]) << shift; + + unsigned int j; + for (j = 0; j < bytesPerSample && j < 8; j += 1) { + sample |= (drwav_uint64)(pIn[j]) << shift; shift += 8; } - pIn += bytesPerSample; - *pOut++ = (float)((int)sample / 2147483648.0); + pIn += j; + *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0); } } @@ -2550,15 +2740,23 @@ static void drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sam *pOut++ = ((float*)pIn)[i]; } return; - } else { + } else if (bytesPerSample == 8) { drwav_f64_to_f32(pOut, (double*)pIn, sampleCount); return; + } else { + // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. + drwav_zero_memory(pOut, sampleCount * sizeof(*pOut)); + return; } } drwav_uint64 drwav_read_f32__pcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) { + if (pWav->bytesPerSample == 0) { + return 0; + } + drwav_uint64 totalSamplesRead = 0; unsigned char sampleData[4096]; while (samplesToRead > 0) { @@ -2628,6 +2826,10 @@ drwav_uint64 drwav_read_f32__ieee(drwav* pWav, drwav_uint64 samplesToRead, float return drwav_read(pWav, samplesToRead, pBufferOut); } + if (pWav->bytesPerSample == 0) { + return 0; + } + drwav_uint64 totalSamplesRead = 0; unsigned char sampleData[4096]; while (samplesToRead > 0) { @@ -2648,6 +2850,10 @@ drwav_uint64 drwav_read_f32__ieee(drwav* pWav, drwav_uint64 samplesToRead, float drwav_uint64 drwav_read_f32__alaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) { + if (pWav->bytesPerSample == 0) { + return 0; + } + drwav_uint64 totalSamplesRead = 0; unsigned char sampleData[4096]; while (samplesToRead > 0) { @@ -2668,6 +2874,10 @@ drwav_uint64 drwav_read_f32__alaw(drwav* pWav, drwav_uint64 samplesToRead, float drwav_uint64 drwav_read_f32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) { + if (pWav->bytesPerSample == 0) { + return 0; + } + drwav_uint64 totalSamplesRead = 0; unsigned char sampleData[4096]; while (samplesToRead > 0) { @@ -2842,17 +3052,27 @@ static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_ return; } + + // Anything more than 64 bits per sample is not supported. + if (bytesPerSample > 8) { + drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + + // Generic, slow converter. for (unsigned int i = 0; i < totalSampleCount; ++i) { - unsigned int sample = 0; + drwav_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; - for (unsigned short j = 0; j < bytesPerSample && j < 4; ++j) { - sample |= (unsigned int)(pIn[j]) << shift; + + unsigned int j; + for (j = 0; j < bytesPerSample && j < 8; j += 1) { + sample |= (drwav_uint64)(pIn[j]) << shift; shift += 8; } - pIn += bytesPerSample; - *pOut++ = sample; + pIn += j; + *pOut++ = (drwav_int32)((drwav_int64)sample >> 32); } } @@ -2861,9 +3081,13 @@ static void drwav__ieee_to_s32(drwav_int32* pOut, const unsigned char* pIn, size if (bytesPerSample == 4) { drwav_f32_to_s32(pOut, (float*)pIn, totalSampleCount); return; - } else { + } else if (bytesPerSample == 8) { drwav_f64_to_s32(pOut, (double*)pIn, totalSampleCount); return; + } else { + // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. + drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut)); + return; } } @@ -2875,6 +3099,10 @@ drwav_uint64 drwav_read_s32__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_ return drwav_read(pWav, samplesToRead, pBufferOut); } + if (pWav->bytesPerSample == 0) { + return 0; + } + drwav_uint64 totalSamplesRead = 0; unsigned char sampleData[4096]; while (samplesToRead > 0) { @@ -2939,6 +3167,10 @@ drwav_uint64 drwav_read_s32__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_ drwav_uint64 drwav_read_s32__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) { + if (pWav->bytesPerSample == 0) { + return 0; + } + drwav_uint64 totalSamplesRead = 0; unsigned char sampleData[4096]; while (samplesToRead > 0) { @@ -2959,6 +3191,10 @@ drwav_uint64 drwav_read_s32__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav drwav_uint64 drwav_read_s32__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) { + if (pWav->bytesPerSample == 0) { + return 0; + } + drwav_uint64 totalSamplesRead = 0; unsigned char sampleData[4096]; while (samplesToRead > 0) { @@ -2979,6 +3215,10 @@ drwav_uint64 drwav_read_s32__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav drwav_uint64 drwav_read_s32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) { + if (pWav->bytesPerSample == 0) { + return 0; + } + drwav_uint64 totalSamplesRead = 0; unsigned char sampleData[4096]; while (samplesToRead > 0) { @@ -3354,6 +3594,35 @@ void drwav_free(void* pDataReturnedByOpenAndRead) // REVISION HISTORY // +// v0.8.1 - 2018-06-29 +// - Add support for sequential writing APIs. +// - Disable seeking in write mode. +// - Fix bugs with Wave64. +// - Fix typos. +// +// v0.8 - 2018-04-27 +// - Bug fix. +// - Start using major.minor.revision versioning. +// +// v0.7f - 2018-02-05 +// - Restrict ADPCM formats to a maximum of 2 channels. +// +// v0.7e - 2018-02-02 +// - Fix a crash. +// +// v0.7d - 2018-02-01 +// - Fix a crash. +// +// v0.7c - 2018-02-01 +// - Set drwav.bytesPerSample to 0 for all compressed formats. +// - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for +// all format conversion reading APIs (*_s16, *_s32, *_f32 APIs). +// - Fix some divide-by-zero errors. +// +// v0.7b - 2018-01-22 +// - Fix errors with seeking of compressed formats. +// - Fix compilation error when DR_WAV_NO_CONVERSION_API +// // v0.7a - 2017-11-17 // - Fix some GCC warnings. // @@ -3393,7 +3662,7 @@ void drwav_free(void* pDataReturnedByOpenAndRead) // // v0.5 - 2016-09-29 // - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to -// keep it consistent with dr_audio and drwav_flac. +// keep it consistent with dr_audio and dr_flac. // // v0.4b - 2016-09-18 // - Fixed a typo in documentation. @@ -3403,8 +3672,8 @@ void drwav_free(void* pDataReturnedByOpenAndRead) // - Change date format to ISO 8601 (YYYY-MM-DD) // // v0.4 - 2016-07-13 -// - API CHANGE. Make onSeek consistent with drwav_flac. -// - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with drwav_flac. +// - API CHANGE. Make onSeek consistent with dr_flac. +// - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac. // - Added support for Sony Wave64. // // v0.3a - 2016-05-28 @@ -3418,7 +3687,7 @@ void drwav_free(void* pDataReturnedByOpenAndRead) // - Fixed Linux/GCC build. // // v0.2 - 2016-05-11 -// - Added support for reading data as signed 32-bit PCM for consistency with drwav_flac. +// - Added support for reading data as signed 32-bit PCM for consistency with dr_flac. // // v0.1a - 2016-05-07 // - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize. diff --git a/raylib/external/mini_al.h b/raylib/external/mini_al.h index d69abe4..759662c 100644 --- a/raylib/external/mini_al.h +++ b/raylib/external/mini_al.h @@ -1,5 +1,5 @@ // Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. -// mini_al - v0.6b - 2018-02-03 +// mini_al - v0.8.8 - 2018-09-14 // // David Reid - davidreidsoftware@gmail.com @@ -16,14 +16,17 @@ // - WASAPI // - DirectSound // - WinMM +// - Core Audio (Apple) // - ALSA -// - OSS -// - OpenSL|ES / Android +// - PulseAudio +// - JACK +// - sndio (OpenBSD) +// - audio(4) (NetBSD and OpenBSD) +// - OSS (FreeBSD) +// - OpenSL|ES (Android only) // - OpenAL // - SDL // - Null (Silence) -// - ... and more in the future. -// - Core Audio (OSX, iOS) // // Supported Formats: // - Unsigned 8-bit PCM @@ -36,36 +39,46 @@ // USAGE // ===== // mini_al is a single-file library. To use it, do something like the following in one .c file. -// #define MAL_IMPLEMENTATION +// #define MINI_AL_IMPLEMENTATION // #include "mini_al.h" // // You can then #include this file in other parts of the program as you would with any other header file. // -// The implementation of this library will try #include-ing necessary headers for each backend. If you do not have -// the development packages for any particular backend you can disable it by #define-ing the appropriate MAL_NO_* -// option before the implementation. +// If you want to disable a specific backend, #define the appropriate MAL_NO_* option before the implementation. +// +// Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations. // // -// Building (Windows) +// Building for Windows +// -------------------- +// The Windows build should compile clean on all popular compilers without the need to configure any include paths +// nor link to any libraries. +// +// Building for macOS and iOS +// -------------------------- +// The macOS build should compile clean without the need to download any dependencies or link to any libraries or +// frameworks. The iOS build needs to be compiled as Objective-C (sorry) and will need to link the relevant frameworks +// but should Just Work with Xcode. +// +// Building for Linux // ------------------ -// The Windows build should compile clean on all modern versions of MSVC without the need to configure any include -// paths nor link to any libraries. The same applies to MinGW/GCC and Clang. +// The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages for any +// of the supported backends. // -// Building (Linux) +// Building for BSD // ---------------- -// The Linux build uses ALSA for it's backend so you will need to install the relevant ALSA development packages -// for your preferred distro. It also uses pthreads. Dependencies are dynamically linked at runtime so you do not -// need to link to -lasound nor -lpthread. You will need to link to -ldl. +// The BSD build only requires linking to -ldl, -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and +// FreeBSD uses OSS. // -// Building (BSD) -// -------------- -// BSD build uses OSS. Requires linking to -lossaudio on {Open,Net}BSD, but not FreeBSD. +// Building for Android +// -------------------- +// The Android build uses OpenSL|ES, and will require an appropriate API level that supports OpenSL|ES. mini_al has +// been tested against API levels 16 and 21. // -// Building (Emscripten) -// --------------------- +// Building for Emscripten +// ----------------------- // The Emscripten build currently uses SDL 1.2 for it's backend which means specifying "-s USE_SDL=2" is unecessary -// as of this version. However, if in the future there is legitimate benefit or enough demand for SDL 2 to be used -// instead, you will need to specify this when compiling. +// as of this version. // // // Playback Example @@ -77,21 +90,15 @@ // // then return the number of frames you wrote. // // // // The user data (pDevice->pUserData) is set by mal_device_init(). -// return (mal_uint32)drwav_read_f32((drwav*)pDevice->pUserData, frameCount * pDevice->channels, (float*)pSamples) / pDevice->channels; +// return (mal_uint32)mal_decoder_read((mal_decoder*)pDevice->pUserData, frameCount, pSamples); // } // // ... // -// mal_context context; -// if (mal_context_init(NULL, 0, NULL, &context) != MAL_SUCCESS) { -// printf("Failed to initialize context."); -// return -3; -// } -// -// mal_device_config config = mal_device_config_init_playback(mal_format_s16, wav.channels, wav.sampleRate, on_send_frames_to_device); +// mal_device_config config = mal_device_config_init_playback(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, on_send_frames_to_device); // // mal_device device; -// mal_result result = mal_device_init(&context, mal_device_type_playback, NULL, &config, pMyData, &device); +// mal_result result = mal_device_init(NULL, mal_device_type_playback, NULL, &config, &decoder /*pUserData*/, &device); // if (result != MAL_SUCCESS) { // return -1; // } @@ -111,19 +118,27 @@ // - If mal_device_init() is called with a device that's not aligned to the platform's natural alignment // boundary (4 bytes on 32-bit, 8 bytes on 64-bit), it will _not_ be thread-safe. The reason for this // is that it depends on members of mal_device being correctly aligned for atomic assignments. -// - Sample data is always little-endian and interleaved. For example, mal_format_s16 means signed 16-bit +// - Sample data is always native-endian and interleaved. For example, mal_format_s16 means signed 16-bit // integer samples, interleaved. Let me know if you need non-interleaved and I'll look into it. +// - The sndio backend is currently only enabled on OpenBSD builds. +// - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it. +// - If you are using the platform's default device, mini_al will try automatically switching the internal +// device when the device is unplugged. This feature is disabled when the device is opened in exclusive +// mode. // // // // BACKEND NUANCES // =============== -// - The absolute best latency I am able to get on DirectSound is about 10 milliseconds. This seems very -// consistent so I'm suspecting there's some kind of hard coded limit there or something. -// - DirectSound currently supports a maximum of 4 periods. +// +// Android +// ------- // - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: // -// - UWP is only supported when compiling as C++. +// - Only a single mal_context can be active at any given time. This is due to a limitation with OpenSL|ES. +// +// UWP +// --- // - UWP only supports default playback and capture devices. // - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): // @@ -150,6 +165,21 @@ // #define MAL_NO_ALSA // Disables the ALSA backend. // +// #define MAL_NO_PULSEAUDIO +// Disables the PulseAudio backend. +// +// #define MAL_NO_JACK +// Disables the JACK backend. +// +// #define MAL_NO_COREAUDIO +// Disables the Core Audio backend. +// +// #define MAL_NO_SNDIO +// Disables the sndio backend. +// +// #define MAL_NO_AUDIO4 +// Disables the audio(4) backend. +// // #define MAL_NO_OSS // Disables the OSS backend. // @@ -165,13 +195,48 @@ // #define MAL_NO_NULL // Disables the null backend. // -// #define MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS -// When a buffer size of 0 is specified when a device is initialized, it will default to a size with -// this number of milliseconds worth of data. Note that some backends may adjust this setting if that -// particular backend has unusual latency characteristics. -// // #define MAL_DEFAULT_PERIODS // When a period count of 0 is specified when a device is initialized, it will default to this. +// +// #define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY +// #define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE +// When a buffer size of 0 is specified when a device is initialized it will default to a buffer of this size, depending +// on the chosen performance profile. These can be increased or decreased depending on your specific requirements. +// +// #define MAL_NO_DECODING +// Disables the decoding APIs. +// +// #define MAL_NO_DEVICE_IO +// Disables playback and recording. This will disable mal_context and mal_device APIs. This is useful if you only want to +// use mini_al's data conversion and/or decoding APIs. +// +// #define MAL_NO_STDIO +// Disables file IO APIs. +// +// #define MAL_NO_SSE2 +// Disables SSE2 optimizations. +// +// #define MAL_NO_AVX2 +// Disables AVX2 optimizations. +// +// #define MAL_NO_AVX512 +// Disables AVX-512 optimizations. +// +// #define MAL_NO_NEON +// Disables NEON optimizations. +// +// #define MAL_LOG_LEVEL +// Sets the logging level. Set level to one of the following: +// MAL_LOG_LEVEL_VERBOSE +// MAL_LOG_LEVEL_INFO +// MAL_LOG_LEVEL_WARNING +// MAL_LOG_LEVEL_ERROR +// +// #define MAL_DEBUT_OUTPUT +// Enable printf() debug output. +// +// #ifndef MAL_COINIT_VALUE +// Windows only. The value to pass to internal calls to CoInitializeEx(). Defaults to COINIT_MULTITHREADED. #ifndef mini_al_h #define mini_al_h @@ -183,19 +248,27 @@ extern "C" { #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4201) // nonstandard extension used: nameless struct/union + #pragma warning(disable:4324) // structure was padded due to alignment specifier #endif // Platform/backend detection. #ifdef _WIN32 #define MAL_WIN32 - #if (!defined(WINAPI_FAMILY) || WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + #define MAL_WIN32_UWP + #else #define MAL_WIN32_DESKTOP #endif #else #define MAL_POSIX #include // Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. - #define MAL_UNIX + #ifdef __unix__ + #define MAL_UNIX + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define MAL_BSD + #endif + #endif #ifdef __linux__ #define MAL_LINUX #endif @@ -210,21 +283,857 @@ extern "C" { #endif #endif +#include // For size_t. + +#ifndef MAL_HAS_STDINT + #if defined(_MSC_VER) + #if _MSC_VER >= 1600 + #define MAL_HAS_STDINT + #endif + #else + #if defined(__has_include) + #if __has_include() + #define MAL_HAS_STDINT + #endif + #endif + #endif +#endif + +#if !defined(MAL_HAS_STDINT) && (defined(__GNUC__) || defined(__clang__)) // Assume support for stdint.h on GCC and Clang. + #define MAL_HAS_STDINT +#endif + +#ifndef MAL_HAS_STDINT +typedef signed char mal_int8; +typedef unsigned char mal_uint8; +typedef signed short mal_int16; +typedef unsigned short mal_uint16; +typedef signed int mal_int32; +typedef unsigned int mal_uint32; + #if defined(_MSC_VER) + typedef signed __int64 mal_int64; + typedef unsigned __int64 mal_uint64; + #else + typedef signed long long int mal_int64; + typedef unsigned long long int mal_uint64; + #endif + #if defined(_WIN32) + #if defined(_WIN64) + typedef mal_uint64 mal_uintptr; + #else + typedef mal_uint32 mal_uintptr; + #endif + #elif defined(__GNUC__) + #if defined(__LP64__) + typedef mal_uint64 mal_uintptr; + #else + typedef mal_uint32 mal_uintptr; + #endif + #else + typedef mal_uint64 mal_uintptr; // Fallback. + #endif +#else +#include +typedef int8_t mal_int8; +typedef uint8_t mal_uint8; +typedef int16_t mal_int16; +typedef uint16_t mal_uint16; +typedef int32_t mal_int32; +typedef uint32_t mal_uint32; +typedef int64_t mal_int64; +typedef uint64_t mal_uint64; +typedef uintptr_t mal_uintptr; +#endif +typedef mal_uint8 mal_bool8; +typedef mal_uint32 mal_bool32; +#define MAL_TRUE 1 +#define MAL_FALSE 0 + +typedef void* mal_handle; +typedef void* mal_ptr; +typedef void (* mal_proc)(void); + +#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) +typedef mal_uint16 wchar_t; +#endif + +// Define NULL for some compilers. +#ifndef NULL +#define NULL 0 +#endif + +#if defined(SIZE_MAX) + #define MAL_SIZE_MAX SIZE_MAX +#else + #define MAL_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ +#endif + + +#ifdef _MSC_VER +#define MAL_INLINE __forceinline +#else +#ifdef __GNUC__ +#define MAL_INLINE inline __attribute__((always_inline)) +#else +#define MAL_INLINE inline +#endif +#endif + +#ifdef _MSC_VER +#define MAL_ALIGN(alignment) __declspec(align(alignment)) +#elif !defined(__DMC__) +#define MAL_ALIGN(alignment) __attribute__((aligned(alignment))) +#else +#define MAL_ALIGN(alignment) +#endif + +#ifdef _MSC_VER +#define MAL_ALIGNED_STRUCT(alignment) MAL_ALIGN(alignment) struct +#else +#define MAL_ALIGNED_STRUCT(alignment) struct MAL_ALIGN(alignment) +#endif + +// SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. +#define MAL_SIMD_ALIGNMENT 64 + + +// Logging levels +#define MAL_LOG_LEVEL_VERBOSE 4 +#define MAL_LOG_LEVEL_INFO 3 +#define MAL_LOG_LEVEL_WARNING 2 +#define MAL_LOG_LEVEL_ERROR 1 + +#ifndef MAL_LOG_LEVEL +#define MAL_LOG_LEVEL MAL_LOG_LEVEL_ERROR +#endif + +typedef struct mal_context mal_context; +typedef struct mal_device mal_device; + +typedef mal_uint8 mal_channel; +#define MAL_CHANNEL_NONE 0 +#define MAL_CHANNEL_MONO 1 +#define MAL_CHANNEL_FRONT_LEFT 2 +#define MAL_CHANNEL_FRONT_RIGHT 3 +#define MAL_CHANNEL_FRONT_CENTER 4 +#define MAL_CHANNEL_LFE 5 +#define MAL_CHANNEL_BACK_LEFT 6 +#define MAL_CHANNEL_BACK_RIGHT 7 +#define MAL_CHANNEL_FRONT_LEFT_CENTER 8 +#define MAL_CHANNEL_FRONT_RIGHT_CENTER 9 +#define MAL_CHANNEL_BACK_CENTER 10 +#define MAL_CHANNEL_SIDE_LEFT 11 +#define MAL_CHANNEL_SIDE_RIGHT 12 +#define MAL_CHANNEL_TOP_CENTER 13 +#define MAL_CHANNEL_TOP_FRONT_LEFT 14 +#define MAL_CHANNEL_TOP_FRONT_CENTER 15 +#define MAL_CHANNEL_TOP_FRONT_RIGHT 16 +#define MAL_CHANNEL_TOP_BACK_LEFT 17 +#define MAL_CHANNEL_TOP_BACK_CENTER 18 +#define MAL_CHANNEL_TOP_BACK_RIGHT 19 +#define MAL_CHANNEL_AUX_0 20 +#define MAL_CHANNEL_AUX_1 21 +#define MAL_CHANNEL_AUX_2 22 +#define MAL_CHANNEL_AUX_3 23 +#define MAL_CHANNEL_AUX_4 24 +#define MAL_CHANNEL_AUX_5 25 +#define MAL_CHANNEL_AUX_6 26 +#define MAL_CHANNEL_AUX_7 27 +#define MAL_CHANNEL_AUX_8 28 +#define MAL_CHANNEL_AUX_9 29 +#define MAL_CHANNEL_AUX_10 30 +#define MAL_CHANNEL_AUX_11 31 +#define MAL_CHANNEL_AUX_12 32 +#define MAL_CHANNEL_AUX_13 33 +#define MAL_CHANNEL_AUX_14 34 +#define MAL_CHANNEL_AUX_15 35 +#define MAL_CHANNEL_AUX_16 36 +#define MAL_CHANNEL_AUX_17 37 +#define MAL_CHANNEL_AUX_18 38 +#define MAL_CHANNEL_AUX_19 39 +#define MAL_CHANNEL_AUX_20 40 +#define MAL_CHANNEL_AUX_21 41 +#define MAL_CHANNEL_AUX_22 42 +#define MAL_CHANNEL_AUX_23 43 +#define MAL_CHANNEL_AUX_24 44 +#define MAL_CHANNEL_AUX_25 45 +#define MAL_CHANNEL_AUX_26 46 +#define MAL_CHANNEL_AUX_27 47 +#define MAL_CHANNEL_AUX_28 48 +#define MAL_CHANNEL_AUX_29 49 +#define MAL_CHANNEL_AUX_30 50 +#define MAL_CHANNEL_AUX_31 51 +#define MAL_CHANNEL_LEFT MAL_CHANNEL_FRONT_LEFT +#define MAL_CHANNEL_RIGHT MAL_CHANNEL_FRONT_RIGHT +#define MAL_CHANNEL_POSITION_COUNT MAL_CHANNEL_AUX_31 + 1 + +typedef int mal_result; +#define MAL_SUCCESS 0 +#define MAL_ERROR -1 // A generic error. +#define MAL_INVALID_ARGS -2 +#define MAL_INVALID_OPERATION -3 +#define MAL_OUT_OF_MEMORY -4 +#define MAL_FORMAT_NOT_SUPPORTED -5 +#define MAL_NO_BACKEND -6 +#define MAL_NO_DEVICE -7 +#define MAL_API_NOT_FOUND -8 +#define MAL_DEVICE_BUSY -9 +#define MAL_DEVICE_NOT_INITIALIZED -10 +#define MAL_DEVICE_NOT_STARTED -11 +#define MAL_DEVICE_NOT_STOPPED -12 +#define MAL_DEVICE_ALREADY_STARTED -13 +#define MAL_DEVICE_ALREADY_STARTING -14 +#define MAL_DEVICE_ALREADY_STOPPED -15 +#define MAL_DEVICE_ALREADY_STOPPING -16 +#define MAL_FAILED_TO_MAP_DEVICE_BUFFER -17 +#define MAL_FAILED_TO_UNMAP_DEVICE_BUFFER -18 +#define MAL_FAILED_TO_INIT_BACKEND -19 +#define MAL_FAILED_TO_READ_DATA_FROM_CLIENT -20 +#define MAL_FAILED_TO_READ_DATA_FROM_DEVICE -21 +#define MAL_FAILED_TO_SEND_DATA_TO_CLIENT -22 +#define MAL_FAILED_TO_SEND_DATA_TO_DEVICE -23 +#define MAL_FAILED_TO_OPEN_BACKEND_DEVICE -24 +#define MAL_FAILED_TO_START_BACKEND_DEVICE -25 +#define MAL_FAILED_TO_STOP_BACKEND_DEVICE -26 +#define MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE -27 +#define MAL_FAILED_TO_CREATE_MUTEX -28 +#define MAL_FAILED_TO_CREATE_EVENT -29 +#define MAL_FAILED_TO_CREATE_THREAD -30 +#define MAL_INVALID_DEVICE_CONFIG -31 +#define MAL_ACCESS_DENIED -32 +#define MAL_TOO_LARGE -33 +#define MAL_DEVICE_UNAVAILABLE -34 +#define MAL_TIMEOUT -35 + +// Standard sample rates. +#define MAL_SAMPLE_RATE_8000 8000 +#define MAL_SAMPLE_RATE_11025 11025 +#define MAL_SAMPLE_RATE_16000 16000 +#define MAL_SAMPLE_RATE_22050 22050 +#define MAL_SAMPLE_RATE_24000 24000 +#define MAL_SAMPLE_RATE_32000 32000 +#define MAL_SAMPLE_RATE_44100 44100 +#define MAL_SAMPLE_RATE_48000 48000 +#define MAL_SAMPLE_RATE_88200 88200 +#define MAL_SAMPLE_RATE_96000 96000 +#define MAL_SAMPLE_RATE_176400 176400 +#define MAL_SAMPLE_RATE_192000 192000 +#define MAL_SAMPLE_RATE_352800 352800 +#define MAL_SAMPLE_RATE_384000 384000 + +#define MAL_MIN_PCM_SAMPLE_SIZE_IN_BYTES 1 // For simplicity, mini_al does not support PCM samples that are not byte aligned. +#define MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES 8 +#define MAL_MIN_CHANNELS 1 +#define MAL_MAX_CHANNELS 32 +#define MAL_MIN_SAMPLE_RATE MAL_SAMPLE_RATE_8000 +#define MAL_MAX_SAMPLE_RATE MAL_SAMPLE_RATE_384000 +#define MAL_SRC_SINC_MIN_WINDOW_WIDTH 2 +#define MAL_SRC_SINC_MAX_WINDOW_WIDTH 32 +#define MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH 32 +#define MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION 8 +#define MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES 256 + +typedef enum +{ + mal_stream_format_pcm = 0, +} mal_stream_format; + +typedef enum +{ + mal_stream_layout_interleaved = 0, + mal_stream_layout_deinterleaved +} mal_stream_layout; + +typedef enum +{ + mal_dither_mode_none = 0, + mal_dither_mode_rectangle, + mal_dither_mode_triangle +} mal_dither_mode; + +typedef enum +{ + // I like to keep these explicitly defined because they're used as a key into a lookup table. When items are + // added to this, make sure there are no gaps and that they're added to the lookup table in mal_get_bytes_per_sample(). + mal_format_unknown = 0, // Mainly used for indicating an error, but also used as the default for the output format for decoders. + mal_format_u8 = 1, + mal_format_s16 = 2, // Seems to be the most widely supported format. + mal_format_s24 = 3, // Tightly packed. 3 bytes per sample. + mal_format_s32 = 4, + mal_format_f32 = 5, + mal_format_count +} mal_format; + +typedef enum +{ + mal_channel_mix_mode_planar_blend = 0, // Simple averaging based on the plane(s) the channel is sitting on. + mal_channel_mix_mode_simple, // Drop excess channels; zeroed out extra channels. + mal_channel_mix_mode_default = mal_channel_mix_mode_planar_blend +} mal_channel_mix_mode; + +typedef enum +{ + mal_standard_channel_map_microsoft, + mal_standard_channel_map_alsa, + mal_standard_channel_map_rfc3551, // Based off AIFF. + mal_standard_channel_map_flac, + mal_standard_channel_map_vorbis, + mal_standard_channel_map_sound4, // FreeBSD's sound(4). + mal_standard_channel_map_sndio, // www.sndio.org/tips.html + mal_standard_channel_map_default = mal_standard_channel_map_microsoft +} mal_standard_channel_map; + +typedef enum +{ + mal_performance_profile_low_latency = 0, + mal_performance_profile_conservative +} mal_performance_profile; + + +typedef struct mal_format_converter mal_format_converter; +typedef mal_uint32 (* mal_format_converter_read_proc) (mal_format_converter* pConverter, mal_uint32 frameCount, void* pFramesOut, void* pUserData); +typedef mal_uint32 (* mal_format_converter_read_deinterleaved_proc)(mal_format_converter* pConverter, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData); + +typedef struct +{ + mal_format formatIn; + mal_format formatOut; + mal_uint32 channels; + mal_stream_format streamFormatIn; + mal_stream_format streamFormatOut; + mal_dither_mode ditherMode; + mal_bool32 noSSE2 : 1; + mal_bool32 noAVX2 : 1; + mal_bool32 noAVX512 : 1; + mal_bool32 noNEON : 1; + mal_format_converter_read_proc onRead; + mal_format_converter_read_deinterleaved_proc onReadDeinterleaved; + void* pUserData; +} mal_format_converter_config; + +struct mal_format_converter +{ + mal_format_converter_config config; + mal_bool32 useSSE2 : 1; + mal_bool32 useAVX2 : 1; + mal_bool32 useAVX512 : 1; + mal_bool32 useNEON : 1; + void (* onConvertPCM)(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode); + void (* onInterleavePCM)(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels); + void (* onDeinterleavePCM)(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels); +}; + + + +typedef struct mal_channel_router mal_channel_router; +typedef mal_uint32 (* mal_channel_router_read_deinterleaved_proc)(mal_channel_router* pRouter, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData); + +typedef struct +{ + mal_uint32 channelsIn; + mal_uint32 channelsOut; + mal_channel channelMapIn[MAL_MAX_CHANNELS]; + mal_channel channelMapOut[MAL_MAX_CHANNELS]; + mal_channel_mix_mode mixingMode; + mal_bool32 noSSE2 : 1; + mal_bool32 noAVX2 : 1; + mal_bool32 noAVX512 : 1; + mal_bool32 noNEON : 1; + mal_channel_router_read_deinterleaved_proc onReadDeinterleaved; + void* pUserData; +} mal_channel_router_config; + +struct mal_channel_router +{ + mal_channel_router_config config; + mal_bool32 isPassthrough : 1; + mal_bool32 isSimpleShuffle : 1; + mal_bool32 useSSE2 : 1; + mal_bool32 useAVX2 : 1; + mal_bool32 useAVX512 : 1; + mal_bool32 useNEON : 1; + mal_uint8 shuffleTable[MAL_MAX_CHANNELS]; + float weights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS]; +}; + + + +typedef struct mal_src mal_src; +//typedef mal_uint32 (* mal_src_read_proc)(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read. +typedef mal_uint32 (* mal_src_read_deinterleaved_proc)(mal_src* pSRC, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData); // Returns the number of frames that were read. + +typedef enum +{ + mal_src_algorithm_sinc = 0, + mal_src_algorithm_linear, + mal_src_algorithm_none, + mal_src_algorithm_default = mal_src_algorithm_sinc +} mal_src_algorithm; + +typedef enum +{ + mal_src_sinc_window_function_hann = 0, + mal_src_sinc_window_function_rectangular, + mal_src_sinc_window_function_default = mal_src_sinc_window_function_hann +} mal_src_sinc_window_function; + +typedef struct +{ + mal_src_sinc_window_function windowFunction; + mal_uint32 windowWidth; +} mal_src_config_sinc; + +typedef struct +{ + mal_uint32 sampleRateIn; + mal_uint32 sampleRateOut; + mal_uint32 channels; + mal_src_algorithm algorithm; + mal_bool32 neverConsumeEndOfInput : 1; + mal_bool32 noSSE2 : 1; + mal_bool32 noAVX2 : 1; + mal_bool32 noAVX512 : 1; + mal_bool32 noNEON : 1; + mal_src_read_deinterleaved_proc onReadDeinterleaved; + void* pUserData; + union + { + mal_src_config_sinc sinc; + }; +} mal_src_config; + +MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_src +{ + union + { + struct + { + MAL_ALIGN(MAL_SIMD_ALIGNMENT) float input[MAL_MAX_CHANNELS][MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES]; + float timeIn; + mal_uint32 leftoverFrames; + } linear; + + struct + { + MAL_ALIGN(MAL_SIMD_ALIGNMENT) float input[MAL_MAX_CHANNELS][MAL_SRC_SINC_MAX_WINDOW_WIDTH*2 + MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES]; + float timeIn; + mal_uint32 inputFrameCount; // The number of frames sitting in the input buffer, not including the first half of the window. + mal_uint32 windowPosInSamples; // An offset of . + float table[MAL_SRC_SINC_MAX_WINDOW_WIDTH*1 * MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION]; // Precomputed lookup table. The +1 is used to avoid the need for an overflow check. + } sinc; + }; + + mal_src_config config; + mal_bool32 isEndOfInputLoaded : 1; + mal_bool32 useSSE2 : 1; + mal_bool32 useAVX2 : 1; + mal_bool32 useAVX512 : 1; + mal_bool32 useNEON : 1; +}; + +typedef struct mal_dsp mal_dsp; +typedef mal_uint32 (* mal_dsp_read_proc)(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData); + +typedef struct +{ + mal_format formatIn; + mal_uint32 channelsIn; + mal_uint32 sampleRateIn; + mal_channel channelMapIn[MAL_MAX_CHANNELS]; + mal_format formatOut; + mal_uint32 channelsOut; + mal_uint32 sampleRateOut; + mal_channel channelMapOut[MAL_MAX_CHANNELS]; + mal_channel_mix_mode channelMixMode; + mal_dither_mode ditherMode; + mal_src_algorithm srcAlgorithm; + mal_bool32 allowDynamicSampleRate; + mal_bool32 neverConsumeEndOfInput : 1; // <-- For SRC. + mal_bool32 noSSE2 : 1; + mal_bool32 noAVX2 : 1; + mal_bool32 noAVX512 : 1; + mal_bool32 noNEON : 1; + mal_dsp_read_proc onRead; + void* pUserData; + union + { + mal_src_config_sinc sinc; + }; +} mal_dsp_config; + +MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_dsp +{ + mal_dsp_read_proc onRead; + void* pUserData; + mal_format_converter formatConverterIn; // For converting data to f32 in preparation for further processing. + mal_format_converter formatConverterOut; // For converting data to the requested output format. Used as the final step in the processing pipeline. + mal_channel_router channelRouter; // For channel conversion. + mal_src src; // For sample rate conversion. + mal_bool32 isDynamicSampleRateAllowed : 1; // mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() will fail if this is set to false. + mal_bool32 isPreFormatConversionRequired : 1; + mal_bool32 isPostFormatConversionRequired : 1; + mal_bool32 isChannelRoutingRequired : 1; + mal_bool32 isSRCRequired : 1; + mal_bool32 isChannelRoutingAtStart : 1; + mal_bool32 isPassthrough : 1; // <-- Will be set to true when the DSP pipeline is an optimized passthrough. +}; + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// DATA CONVERSION +// =============== +// +// This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Channel Maps +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Helper for retrieving a standard channel map. +void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]); + +// Copies a channel map. +void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels); + + +// Determines whether or not a channel map is valid. +// +// A blank channel map is valid (all channels set to MAL_CHANNEL_NONE). The way a blank channel map is handled is context specific, but +// is usually treated as a passthrough. +// +// Invalid channel maps: +// - A channel map with no channels +// - A channel map with more than one channel and a mono channel +mal_bool32 mal_channel_map_valid(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]); + +// Helper for comparing two channel maps for equality. +// +// This assumes the channel count is the same between the two. +mal_bool32 mal_channel_map_equal(mal_uint32 channels, const mal_channel channelMapA[MAL_MAX_CHANNELS], const mal_channel channelMapB[MAL_MAX_CHANNELS]); + +// Helper for determining if a channel map is blank (all channels set to MAL_CHANNEL_NONE). +mal_bool32 mal_channel_map_blank(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]); + +// Helper for determining whether or not a channel is present in the given channel map. +mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS], mal_channel channelPosition); + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Format Conversion +// ================= +// The format converter serves two purposes: +// 1) Conversion between data formats (u8 to f32, etc.) +// 2) Interleaving and deinterleaving +// +// When initializing a converter, you specify the input and output formats (u8, s16, etc.) and read callbacks. There are two read callbacks - one for +// interleaved input data (onRead) and another for deinterleaved input data (onReadDeinterleaved). You implement whichever is most convenient for you. You +// can implement both, but it's not recommended as it just introduces unnecessary complexity. +// +// To read data as interleaved samples, use mal_format_converter_read(). Otherwise use mal_format_converter_read_deinterleaved(). +// +// Dithering +// --------- +// The format converter also supports dithering. Dithering can be set using ditherMode variable in the config, like so. +// +// pConfig->ditherMode = mal_dither_mode_rectangle; +// +// The different dithering modes include the following, in order of efficiency: +// - None: mal_dither_mode_none +// - Rectangle: mal_dither_mode_rectangle +// - Triangle: mal_dither_mode_triangle +// +// Note that even if the dither mode is set to something other than mal_dither_mode_none, it will be ignored for conversions where dithering is not needed. +// Dithering is available for the following conversions: +// - s16 -> u8 +// - s24 -> u8 +// - s32 -> u8 +// - f32 -> u8 +// - s24 -> s16 +// - s32 -> s16 +// - f32 -> s16 +// +// Note that it is not an error to pass something other than mal_dither_mode_none for conversions where dither is not used. It will just be ignored. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Initializes a format converter. +mal_result mal_format_converter_init(const mal_format_converter_config* pConfig, mal_format_converter* pConverter); + +// Reads data from the format converter as interleaved channels. +mal_uint64 mal_format_converter_read(mal_format_converter* pConverter, mal_uint64 frameCount, void* pFramesOut, void* pUserData); + +// Reads data from the format converter as deinterleaved channels. +mal_uint64 mal_format_converter_read_deinterleaved(mal_format_converter* pConverter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); + + +// Helper for initializing a format converter config. +mal_format_converter_config mal_format_converter_config_init_new(void); +mal_format_converter_config mal_format_converter_config_init(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_proc onRead, void* pUserData); +mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_deinterleaved_proc onReadDeinterleaved, void* pUserData); + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Channel Routing +// =============== +// There are two main things you can do with the channel router: +// 1) Rearrange channels +// 2) Convert from one channel count to another +// +// Channel Rearrangement +// --------------------- +// A simple example of channel rearrangement may be swapping the left and right channels in a stereo stream. To do this you just pass in the same channel +// count for both the input and output with channel maps that contain the same channels (in a different order). +// +// Channel Conversion +// ------------------ +// The channel router can also convert from one channel count to another, such as converting a 5.1 stream to stero. When changing the channel count, the +// router will first perform a 1:1 mapping of channel positions that are present in both the input and output channel maps. The second thing it will do +// is distribute the input mono channel (if any) across all output channels, excluding any None and LFE channels. If there is an output mono channel, all +// input channels will be averaged, excluding any None and LFE channels. +// +// The last case to consider is when a channel position in the input channel map is not present in the output channel map, and vice versa. In this case the +// channel router will perform a blend of other related channels to produce an audible channel. There are several blending modes. +// 1) Simple +// Unmatched channels are silenced. +// 2) Planar Blending +// Channels are blended based on a set of planes that each speaker emits audio from. +// +// Planar Blending +// --------------- +// In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker. +// This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left +// of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map +// contains only the front/left channel position, but the output channel map contains both the front/left and front/center channel. When deciding on the audio +// data to send to the front/center speaker (which has no 1:1 mapping with an input channel) we need to use some logic based on our available input channel +// positions. +// +// As mentioned earlier, our front/left speaker is, conceptually speaking, emitting audio from the front _and_ the left planes. Similarly, the front/center +// speaker is emitting audio from _only_ the front plane. What these two channels have in common is that they are both emitting audio from the front plane. +// Thus, it makes sense that the front/center speaker should receive some contribution from the front/left channel. How much contribution depends on their +// planar relationship (thus the name of this blending technique). +// +// Because the front/left channel is emitting audio from two planes (front and left), you can think of it as though it's willing to dedicate 50% of it's total +// volume to each of it's planes (a channel position emitting from 1 plane would be willing to given 100% of it's total volume to that plane, and a channel +// position emitting from 3 planes would be willing to given 33% of it's total volume to each plane). Similarly, the front/center speaker is emitting audio +// from only one plane so you can think of it as though it's willing to _take_ 100% of it's volume from front plane emissions. Now, since the front/left +// channel is willing to _give_ 50% of it's total volume to the front plane, and the front/center speaker is willing to _take_ 100% of it's total volume +// from the front, you can imagine that 50% of the front/left speaker will be given to the front/center speaker. +// +// Usage +// ----- +// To use the channel router you need to specify three things: +// 1) The input channel count and channel map +// 2) The output channel count and channel map +// 3) The mixing mode to use in the case where a 1:1 mapping is unavailable +// +// Note that input and output data is always deinterleaved 32-bit floating point. +// +// Initialize the channel router with mal_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration, +// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing. +// +// Read data from the channel router with mal_channel_router_read_deinterleaved(). Output data is always 32-bit floating point. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Initializes a channel router where it is assumed that the input data is non-interleaved. +mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal_channel_router* pRouter); + +// Reads data from the channel router as deinterleaved channels. +mal_uint64 mal_channel_router_read_deinterleaved(mal_channel_router* pRouter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); + +// Helper for initializing a channel router config. +mal_channel_router_config mal_channel_router_config_init(mal_uint32 channelsIn, const mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint32 channelsOut, const mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_channel_mix_mode mixingMode, mal_channel_router_read_deinterleaved_proc onRead, void* pUserData); + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Sample Rate Conversion +// ====================== +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Initializes a sample rate conversion object. +mal_result mal_src_init(const mal_src_config* pConfig, mal_src* pSRC); + +// Dynamically adjusts the input sample rate. +// +// DEPRECATED. Use mal_src_set_sample_rate() instead. +mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn); + +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +// +// DEPRECATED. Use mal_src_set_sample_rate() instead. +mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut); + +// Dynamically adjusts the sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +mal_result mal_src_set_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut); + +// Reads a number of frames. +// +// Returns the number of frames actually read. +mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); + + +// Helper for creating a sample rate conversion config. +mal_src_config mal_src_config_init_new(void); +mal_src_config mal_src_config_init(mal_uint32 sampleRateIn, mal_uint32 sampleRateOut, mal_uint32 channels, mal_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData); + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// DSP +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Initializes a DSP object. +mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP); + +// Dynamically adjusts the input sample rate. +// +// This will fail is the DSP was not initialized with allowDynamicSampleRate. +// +// DEPRECATED. Use mal_dsp_set_sample_rate() instead. +mal_result mal_dsp_set_input_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut); + +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +// +// This will fail is the DSP was not initialized with allowDynamicSampleRate. +// +// DEPRECATED. Use mal_dsp_set_sample_rate() instead. +mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut); + +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +// +// This will fail is the DSP was not initialized with allowDynamicSampleRate. +mal_result mal_dsp_set_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut); + + +// Reads a number of frames and runs them through the DSP processor. +mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData); + +// Helper for initializing a mal_dsp_config object. +mal_dsp_config mal_dsp_config_init_new(void); +mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_dsp_read_proc onRead, void* pUserData); +mal_dsp_config mal_dsp_config_init_ex(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_dsp_read_proc onRead, void* pUserData); + + +// High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to +// determine the required size of the output buffer. +// +// A return value of 0 indicates an error. +// +// This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead. +mal_uint64 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint64 frameCountIn); +mal_uint64 mal_convert_frames_ex(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint64 frameCountIn); + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Miscellaneous Helpers +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// malloc(). Calls MAL_MALLOC(). +void* mal_malloc(size_t sz); + +// realloc(). Calls MAL_REALLOC(). +void* mal_realloc(void* p, size_t sz); + +// free(). Calls MAL_FREE(). +void mal_free(void* p); + +// Performs an aligned malloc, with the assumption that the alignment is a power of 2. +void* mal_aligned_malloc(size_t sz, size_t alignment); + +// Free's an aligned malloc'd buffer. +void mal_aligned_free(void* p); + +// Retrieves a friendly name for a format. +const char* mal_get_format_name(mal_format format); + +// Blends two frames in floating point format. +void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels); + +// Retrieves the size of a sample in bytes for the given format. +// +// This API is efficient and is implemented using a lookup table. +// +// Thread Safety: SAFE +// This is API is pure. +mal_uint32 mal_get_bytes_per_sample(mal_format format); +static MAL_INLINE mal_uint32 mal_get_bytes_per_frame(mal_format format, mal_uint32 channels) { return mal_get_bytes_per_sample(format) * channels; } + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Format Conversion +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void mal_pcm_u8_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_u8_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_u8_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_u8_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode); + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// DEVICE I/O +// ========== +// +// This section contains the APIs for device playback and capture. Here is where you'll find mal_device_init(), etc. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef MAL_NO_DEVICE_IO // Some backends are only supported on certain platforms. #if defined(MAL_WIN32) #define MAL_SUPPORT_WASAPI #if defined(MAL_WIN32_DESKTOP) // DirectSound and WinMM backends are only supported on desktop's. #define MAL_SUPPORT_DSOUND #define MAL_SUPPORT_WINMM - #endif - - // Don't support WASAPI on older versions of MSVC for now. - #if defined(_MSC_VER) - #if _MSC_VER < 1600 - #if !defined(__audioclient_h__) - #undef MAL_SUPPORT_WASAPI - #endif - #endif + #define MAL_SUPPORT_JACK // JACK is technically supported on Windows, but I don't know how many people use it in practice... #endif #endif #if defined(MAL_UNIX) @@ -233,15 +1142,25 @@ extern "C" { #define MAL_SUPPORT_ALSA #endif #endif - #if defined(MAL_APPLE) - #define MAL_SUPPORT_COREAUDIO + #if !defined(MAL_BSD) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN) + #define MAL_SUPPORT_PULSEAUDIO + #define MAL_SUPPORT_JACK #endif #if defined(MAL_ANDROID) #define MAL_SUPPORT_OPENSL #endif - #if !defined(MAL_LINUX) && !defined(MAL_APPLE) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN) - #define MAL_SUPPORT_OSS + #if defined(__OpenBSD__) // <-- Change this to "#if defined(MAL_BSD)" to enable sndio on all BSD flavors. + #define MAL_SUPPORT_SNDIO // sndio is only supported on OpenBSD for now. May be expanded later if there's demand. #endif + #if defined(__NetBSD__) || defined(__OpenBSD__) + #define MAL_SUPPORT_AUDIO4 // Only support audio(4) on platforms with known support. + #endif + #if defined(__FreeBSD__) || defined(__DragonFly__) + #define MAL_SUPPORT_OSS // Only support OSS on specific platforms with known support. + #endif +#endif +#if defined(MAL_APPLE) + #define MAL_SUPPORT_COREAUDIO #endif #define MAL_SUPPORT_SDL // All platforms support SDL. @@ -265,9 +1184,21 @@ extern "C" { #if !defined(MAL_NO_ALSA) && defined(MAL_SUPPORT_ALSA) #define MAL_ENABLE_ALSA #endif +#if !defined(MAL_NO_PULSEAUDIO) && defined(MAL_SUPPORT_PULSEAUDIO) + #define MAL_ENABLE_PULSEAUDIO +#endif +#if !defined(MAL_NO_JACK) && defined(MAL_SUPPORT_JACK) + #define MAL_ENABLE_JACK +#endif #if !defined(MAL_NO_COREAUDIO) && defined(MAL_SUPPORT_COREAUDIO) #define MAL_ENABLE_COREAUDIO #endif +#if !defined(MAL_NO_SNDIO) && defined(MAL_SUPPORT_SNDIO) + #define MAL_ENABLE_SNDIO +#endif +#if !defined(MAL_NO_AUDIO4) && defined(MAL_SUPPORT_AUDIO4) + #define MAL_ENABLE_AUDIO4 +#endif #if !defined(MAL_NO_OSS) && defined(MAL_SUPPORT_OSS) #define MAL_ENABLE_OSS #endif @@ -284,38 +1215,47 @@ extern "C" { #define MAL_ENABLE_NULL #endif - -#if defined(_MSC_VER) && _MSC_VER < 1600 -typedef signed char mal_int8; -typedef unsigned char mal_uint8; -typedef signed short mal_int16; -typedef unsigned short mal_uint16; -typedef signed int mal_int32; -typedef unsigned int mal_uint32; -typedef signed __int64 mal_int64; -typedef unsigned __int64 mal_uint64; -#else -#include -typedef int8_t mal_int8; -typedef uint8_t mal_uint8; -typedef int16_t mal_int16; -typedef uint16_t mal_uint16; -typedef int32_t mal_int32; -typedef uint32_t mal_uint32; -typedef int64_t mal_int64; -typedef uint64_t mal_uint64; +#ifdef MAL_SUPPORT_WASAPI +// We need a IMMNotificationClient object for WASAPI. +typedef struct +{ + void* lpVtbl; + mal_uint32 counter; + mal_device* pDevice; +} mal_IMMNotificationClient; #endif -typedef mal_uint8 mal_bool8; -typedef mal_uint32 mal_bool32; -#define MAL_TRUE 1 -#define MAL_FALSE 0 -typedef void* mal_handle; -typedef void* mal_ptr; -typedef void (* mal_proc)(); -typedef struct mal_context mal_context; -typedef struct mal_device mal_device; +typedef enum +{ + mal_backend_null, + mal_backend_wasapi, + mal_backend_dsound, + mal_backend_winmm, + mal_backend_alsa, + mal_backend_pulseaudio, + mal_backend_jack, + mal_backend_coreaudio, + mal_backend_sndio, + mal_backend_audio4, + mal_backend_oss, + mal_backend_opensl, + mal_backend_openal, + mal_backend_sdl +} mal_backend; + +// Thread priorties should be ordered such that the default priority of the worker thread is 0. +typedef enum +{ + mal_thread_priority_idle = -5, + mal_thread_priority_lowest = -4, + mal_thread_priority_low = -3, + mal_thread_priority_normal = -2, + mal_thread_priority_high = -1, + mal_thread_priority_highest = 0, + mal_thread_priority_realtime = 1, + mal_thread_priority_default = 0 +} mal_thread_priority; typedef struct { @@ -388,110 +1328,15 @@ typedef struct }; } mal_event; -#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) -typedef mal_uint16 wchar_t; -#endif - -// Define NULL for some compilers. -#ifndef NULL -#define NULL 0 -#endif #define MAL_MAX_PERIODS_DSOUND 4 #define MAL_MAX_PERIODS_OPENAL 4 -typedef mal_uint8 mal_channel; -#define MAL_CHANNEL_NONE 0 -#define MAL_CHANNEL_FRONT_LEFT 1 -#define MAL_CHANNEL_FRONT_RIGHT 2 -#define MAL_CHANNEL_FRONT_CENTER 3 -#define MAL_CHANNEL_LFE 4 -#define MAL_CHANNEL_BACK_LEFT 5 -#define MAL_CHANNEL_BACK_RIGHT 6 -#define MAL_CHANNEL_FRONT_LEFT_CENTER 7 -#define MAL_CHANNEL_FRONT_RIGHT_CENTER 8 -#define MAL_CHANNEL_BACK_CENTER 9 -#define MAL_CHANNEL_SIDE_LEFT 10 -#define MAL_CHANNEL_SIDE_RIGHT 11 -#define MAL_CHANNEL_TOP_CENTER 12 -#define MAL_CHANNEL_TOP_FRONT_LEFT 13 -#define MAL_CHANNEL_TOP_FRONT_CENTER 14 -#define MAL_CHANNEL_TOP_FRONT_RIGHT 15 -#define MAL_CHANNEL_TOP_BACK_LEFT 16 -#define MAL_CHANNEL_TOP_BACK_CENTER 17 -#define MAL_CHANNEL_TOP_BACK_RIGHT 18 -#define MAL_CHANNEL_MONO MAL_CHANNEL_FRONT_CENTER -#define MAL_MAX_CHANNELS 18 - -#define MAL_MAX_SAMPLE_SIZE_IN_BYTES 8 - -typedef int mal_result; -#define MAL_SUCCESS 0 -#define MAL_ERROR -1 // A generic error. -#define MAL_INVALID_ARGS -2 -#define MAL_OUT_OF_MEMORY -3 -#define MAL_FORMAT_NOT_SUPPORTED -4 -#define MAL_NO_BACKEND -5 -#define MAL_NO_DEVICE -6 -#define MAL_API_NOT_FOUND -7 -#define MAL_DEVICE_BUSY -8 -#define MAL_DEVICE_NOT_INITIALIZED -9 -#define MAL_DEVICE_ALREADY_STARTED -10 -#define MAL_DEVICE_ALREADY_STARTING -11 -#define MAL_DEVICE_ALREADY_STOPPED -12 -#define MAL_DEVICE_ALREADY_STOPPING -13 -#define MAL_FAILED_TO_MAP_DEVICE_BUFFER -14 -#define MAL_FAILED_TO_INIT_BACKEND -15 -#define MAL_FAILED_TO_READ_DATA_FROM_CLIENT -16 -#define MAL_FAILED_TO_READ_DATA_FROM_DEVICE -17 -#define MAL_FAILED_TO_SEND_DATA_TO_CLIENT -18 -#define MAL_FAILED_TO_SEND_DATA_TO_DEVICE -19 -#define MAL_FAILED_TO_OPEN_BACKEND_DEVICE -20 -#define MAL_FAILED_TO_START_BACKEND_DEVICE -21 -#define MAL_FAILED_TO_STOP_BACKEND_DEVICE -22 -#define MAL_FAILED_TO_CREATE_MUTEX -23 -#define MAL_FAILED_TO_CREATE_EVENT -24 -#define MAL_FAILED_TO_CREATE_THREAD -25 -#define MAL_INVALID_DEVICE_CONFIG -26 -#define MAL_ACCESS_DENIED -27 -#define MAL_DSOUND_FAILED_TO_CREATE_DEVICE -1024 -#define MAL_DSOUND_FAILED_TO_SET_COOP_LEVEL -1025 -#define MAL_DSOUND_FAILED_TO_CREATE_BUFFER -1026 -#define MAL_DSOUND_FAILED_TO_QUERY_INTERFACE -1027 -#define MAL_DSOUND_FAILED_TO_SET_NOTIFICATIONS -1028 -#define MAL_ALSA_FAILED_TO_OPEN_DEVICE -2048 -#define MAL_ALSA_FAILED_TO_SET_HW_PARAMS -2049 -#define MAL_ALSA_FAILED_TO_SET_SW_PARAMS -2050 -#define MAL_ALSA_FAILED_TO_PREPARE_DEVICE -2051 -#define MAL_ALSA_FAILED_TO_RECOVER_DEVICE -2052 -#define MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR -3072 -#define MAL_WASAPI_FAILED_TO_CREATE_DEVICE -3073 -#define MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE -3074 -#define MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE -3075 -#define MAL_WASAPI_FAILED_TO_FIND_BEST_FORMAT -3076 -#define MAL_WASAPI_FAILED_TO_GET_INTERNAL_BUFFER -3077 -#define MAL_WASAPI_FAILED_TO_RELEASE_INTERNAL_BUFFER -3078 -#define MAL_WINMM_FAILED_TO_GET_DEVICE_CAPS -4096 -#define MAL_WINMM_FAILED_TO_GET_SUPPORTED_FORMATS -4097 - typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message); typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples); typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples); typedef void (* mal_stop_proc)(mal_device* pDevice); -typedef enum -{ - mal_backend_null, - mal_backend_wasapi, - mal_backend_dsound, - mal_backend_winmm, - mal_backend_alsa, - mal_backend_oss, - mal_backend_opensl, - mal_backend_openal, - mal_backend_sdl -} mal_backend; - typedef enum { mal_device_type_playback, @@ -500,21 +1345,9 @@ typedef enum typedef enum { - // I like to keep these explicitly defined because they're used as a key into a lookup table. When items are - // added to this, make sure there are no gaps and that they're added to the lookup table in mal_get_sample_size_in_bytes(). - mal_format_unknown = 0, // Mainly used for indicating an error. - mal_format_u8 = 1, - mal_format_s16 = 2, // Seems to be the most widely supported format. - mal_format_s24 = 3, // Tightly packed. 3 bytes per sample. - mal_format_s32 = 4, - mal_format_f32 = 5, -} mal_format; - -typedef enum -{ - mal_channel_mix_mode_basic, // Drop excess channels; zeroed out extra channels. - mal_channel_mix_mode_blend, // Blend channels based on locality. -} mal_channel_mix_mode; + mal_share_mode_shared = 0, + mal_share_mode_exclusive, +} mal_share_mode; typedef union { @@ -530,8 +1363,20 @@ typedef union #ifdef MAL_SUPPORT_ALSA char alsa[256]; // ALSA uses a name string for identification. #endif +#ifdef MAL_SUPPORT_PULSEAUDIO + char pulse[256]; // PulseAudio uses a name string for identification. +#endif +#ifdef MAL_SUPPORT_JACK + int jack; // JACK always uses default devices. +#endif #ifdef MAL_SUPPORT_COREAUDIO - // TODO: Implement me. + char coreaudio[256]; // Core Audio uses a string for identification. +#endif +#ifdef MAL_SUPPORT_SNDIO + char sndio[256]; // "snd/0", etc. +#endif +#ifdef MAL_SUPPORT_AUDIO4 + char audio4[256]; // "/dev/audio", etc. #endif #ifdef MAL_SUPPORT_OSS char oss[64]; // "dev/dsp0", etc. "dev/dsp" for the default device. @@ -546,14 +1391,28 @@ typedef union int sdl; // SDL devices are identified with an index. #endif #ifdef MAL_SUPPORT_NULL - int nullbackend; // Always 0. + int nullbackend; // The null backend uses an integer for device IDs. #endif } mal_device_id; typedef struct { + // Basic info. This is the only information guaranteed to be filled in during device enumeration. mal_device_id id; char name[256]; + + // Detailed info. As much of this is filled as possible with mal_context_get_device_info(). Note that you are allowed to initialize + // a device with settings outside of this range, but it just means the data will be converted using mini_al's data conversion + // pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided + // here mainly for informational purposes or in the rare case that someone might find it useful. + // + // These will be set to 0 when returned by mal_context_enumerate_devices() or mal_context_get_devices(). + mal_uint32 formatCount; + mal_format formats[mal_format_count]; + mal_uint32 minChannels; + mal_uint32 maxChannels; + mal_uint32 minSampleRate; + mal_uint32 maxSampleRate; } mal_device_info; typedef struct @@ -561,85 +1420,6 @@ typedef struct mal_int64 counter; } mal_timer; - -typedef struct mal_src mal_src; -typedef mal_uint32 (* mal_src_read_proc)(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read. - -typedef enum -{ - mal_src_algorithm_none, - mal_src_algorithm_linear -} mal_src_algorithm; - -#define MAL_SRC_CACHE_SIZE_IN_FRAMES 512 -typedef struct -{ - mal_src* pSRC; - float pCachedFrames[MAL_MAX_CHANNELS * MAL_SRC_CACHE_SIZE_IN_FRAMES]; - mal_uint32 cachedFrameCount; - mal_uint32 iNextFrame; -} mal_src_cache; - -typedef struct -{ - mal_uint32 sampleRateIn; - mal_uint32 sampleRateOut; - mal_format formatIn; - mal_format formatOut; - mal_uint32 channels; - mal_src_algorithm algorithm; - mal_uint32 cacheSizeInFrames; // The number of frames to read from the client at a time. -} mal_src_config; - -struct mal_src -{ - mal_src_config config; - mal_src_read_proc onRead; - void* pUserData; - float bin[256]; - mal_src_cache cache; // <-- For simplifying and optimizing client -> memory reading. - - union - { - struct - { - float alpha; - mal_bool32 isPrevFramesLoaded : 1; - mal_bool32 isNextFramesLoaded : 1; - } linear; - }; -}; - -typedef struct mal_dsp mal_dsp; -typedef mal_uint32 (* mal_dsp_read_proc)(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData); - -typedef struct -{ - mal_format formatIn; - mal_uint32 channelsIn; - mal_uint32 sampleRateIn; - mal_channel channelMapIn[MAL_MAX_CHANNELS]; - mal_format formatOut; - mal_uint32 channelsOut; - mal_uint32 sampleRateOut; - mal_channel channelMapOut[MAL_MAX_CHANNELS]; - mal_uint32 cacheSizeInFrames; // Applications should set this to 0 for now. -} mal_dsp_config; - -struct mal_dsp -{ - mal_dsp_config config; - mal_dsp_read_proc onRead; - void* pUserDataForOnRead; - mal_src src; // For sample rate conversion. - mal_channel channelMapInPostMix[MAL_MAX_CHANNELS]; // <-- When mixing, new channels may need to be created. This represents the channel map after mixing. - mal_channel channelShuffleTable[MAL_MAX_CHANNELS]; - mal_bool32 isChannelMappingRequired : 1; - mal_bool32 isSRCRequired : 1; - mal_bool32 isPassthrough : 1; // <-- Will be set to true when the DSP pipeline is an optimized passthrough. -}; - - typedef struct { mal_format format; @@ -647,8 +1427,10 @@ typedef struct mal_uint32 sampleRate; mal_channel channelMap[MAL_MAX_CHANNELS]; mal_uint32 bufferSizeInFrames; + mal_uint32 bufferSizeInMilliseconds; mal_uint32 periods; - mal_bool32 preferExclusiveMode; + mal_share_mode shareMode; + mal_performance_profile performanceProfile; mal_recv_proc onRecvCallback; mal_send_proc onSendCallback; mal_stop_proc onStopCallback; @@ -657,23 +1439,62 @@ typedef struct { mal_bool32 noMMap; // Disables MMap mode. } alsa; + + struct + { + const char* pStreamName; + } pulse; } mal_device_config; typedef struct { mal_log_proc onLog; + mal_thread_priority threadPriority; struct { mal_bool32 useVerboseDeviceEnumeration; - mal_bool32 excludeNullDevice; } alsa; + + struct + { + const char* pApplicationName; + const char* pServerName; + mal_bool32 tryAutoSpawn; // Enables autospawning of the PulseAudio daemon if necessary. + } pulse; + + struct + { + const char* pClientName; + mal_bool32 tryStartServer; + } jack; } mal_context_config; +typedef mal_bool32 (* mal_enum_devices_callback_proc)(mal_context* pContext, mal_device_type type, const mal_device_info* pInfo, void* pUserData); + struct mal_context { - mal_backend backend; // DirectSound, ALSA, etc. + mal_backend backend; // DirectSound, ALSA, etc. mal_context_config config; + mal_mutex deviceEnumLock; // Used to make mal_context_get_devices() thread safe. + mal_mutex deviceInfoLock; // Used to make mal_context_get_device_info() thread safe. + mal_uint32 deviceInfoCapacity; // Total capacity of pDeviceInfos. + mal_uint32 playbackDeviceInfoCount; + mal_uint32 captureDeviceInfoCount; + mal_device_info* pDeviceInfos; // Playback devices first, then capture. + mal_bool32 isBackendAsynchronous : 1; // Set when the context is initialized. Set to 1 for asynchronous backends such as Core Audio and JACK. Do not modify. + + mal_result (* onUninit )(mal_context* pContext); + mal_bool32 (* onDeviceIDEqual )(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1); + mal_result (* onEnumDevices )(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData); // Return false from the callback to stop enumeration. + mal_result (* onGetDeviceInfo )(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo); + mal_result (* onDeviceInit )(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice); + void (* onDeviceUninit )(mal_device* pDevice); + mal_result (* onDeviceReinit )(mal_device* pDevice); + mal_result (* onDeviceStart )(mal_device* pDevice); + mal_result (* onDeviceStop )(mal_device* pDevice); + mal_result (* onDeviceBreakMainLoop)(mal_device* pDevice); + mal_result (* onDeviceMainLoop )(mal_device* pDevice); union { @@ -687,6 +1508,10 @@ struct mal_context struct { /*HMODULE*/ mal_handle hDSoundDLL; + mal_proc DirectSoundCreate; + mal_proc DirectSoundEnumerateA; + mal_proc DirectSoundCaptureCreate; + mal_proc DirectSoundCaptureEnumerateA; } dsound; #endif #ifdef MAL_SUPPORT_WINMM @@ -731,7 +1556,11 @@ struct mal_context mal_proc snd_pcm_hw_params_set_access; mal_proc snd_pcm_hw_params_get_format; mal_proc snd_pcm_hw_params_get_channels; + mal_proc snd_pcm_hw_params_get_channels_min; + mal_proc snd_pcm_hw_params_get_channels_max; mal_proc snd_pcm_hw_params_get_rate; + mal_proc snd_pcm_hw_params_get_rate_min; + mal_proc snd_pcm_hw_params_get_rate_max; mal_proc snd_pcm_hw_params_get_buffer_size; mal_proc snd_pcm_hw_params_get_periods; mal_proc snd_pcm_hw_params_get_access; @@ -762,14 +1591,133 @@ struct mal_context mal_proc snd_pcm_info; mal_proc snd_pcm_info_sizeof; mal_proc snd_pcm_info_get_name; + mal_proc snd_config_update_free_global; + + mal_mutex internalDeviceEnumLock; } alsa; #endif +#ifdef MAL_SUPPORT_PULSEAUDIO + struct + { + mal_handle pulseSO; + mal_proc pa_mainloop_new; + mal_proc pa_mainloop_free; + mal_proc pa_mainloop_get_api; + mal_proc pa_mainloop_iterate; + mal_proc pa_mainloop_wakeup; + mal_proc pa_context_new; + mal_proc pa_context_unref; + mal_proc pa_context_connect; + mal_proc pa_context_disconnect; + mal_proc pa_context_set_state_callback; + mal_proc pa_context_get_state; + mal_proc pa_context_get_sink_info_list; + mal_proc pa_context_get_source_info_list; + mal_proc pa_context_get_sink_info_by_name; + mal_proc pa_context_get_source_info_by_name; + mal_proc pa_operation_unref; + mal_proc pa_operation_get_state; + mal_proc pa_channel_map_init_extend; + mal_proc pa_channel_map_valid; + mal_proc pa_channel_map_compatible; + mal_proc pa_stream_new; + mal_proc pa_stream_unref; + mal_proc pa_stream_connect_playback; + mal_proc pa_stream_connect_record; + mal_proc pa_stream_disconnect; + mal_proc pa_stream_get_state; + mal_proc pa_stream_get_sample_spec; + mal_proc pa_stream_get_channel_map; + mal_proc pa_stream_get_buffer_attr; + mal_proc pa_stream_get_device_name; + mal_proc pa_stream_set_write_callback; + mal_proc pa_stream_set_read_callback; + mal_proc pa_stream_flush; + mal_proc pa_stream_drain; + mal_proc pa_stream_cork; + mal_proc pa_stream_trigger; + mal_proc pa_stream_begin_write; + mal_proc pa_stream_write; + mal_proc pa_stream_peek; + mal_proc pa_stream_drop; + } pulse; +#endif +#ifdef MAL_SUPPORT_JACK + struct + { + mal_handle jackSO; + mal_proc jack_client_open; + mal_proc jack_client_close; + mal_proc jack_client_name_size; + mal_proc jack_set_process_callback; + mal_proc jack_set_buffer_size_callback; + mal_proc jack_on_shutdown; + mal_proc jack_get_sample_rate; + mal_proc jack_get_buffer_size; + mal_proc jack_get_ports; + mal_proc jack_activate; + mal_proc jack_deactivate; + mal_proc jack_connect; + mal_proc jack_port_register; + mal_proc jack_port_name; + mal_proc jack_port_get_buffer; + mal_proc jack_free; + } jack; +#endif #ifdef MAL_SUPPORT_COREAUDIO struct { - int _unused; + mal_handle hCoreFoundation; + mal_proc CFStringGetCString; + + mal_handle hCoreAudio; + mal_proc AudioObjectGetPropertyData; + mal_proc AudioObjectGetPropertyDataSize; + mal_proc AudioObjectSetPropertyData; + mal_proc AudioObjectAddPropertyListener; + + mal_handle hAudioUnit; // Could possibly be set to AudioToolbox on later versions of macOS. + mal_proc AudioComponentFindNext; + mal_proc AudioComponentInstanceDispose; + mal_proc AudioComponentInstanceNew; + mal_proc AudioOutputUnitStart; + mal_proc AudioOutputUnitStop; + mal_proc AudioUnitAddPropertyListener; + mal_proc AudioUnitGetProperty; + mal_proc AudioUnitSetProperty; + mal_proc AudioUnitInitialize; + mal_proc AudioUnitRender; } coreaudio; #endif +#ifdef MAL_SUPPORT_SNDIO + struct + { + mal_handle sndioSO; + mal_proc sio_open; + mal_proc sio_close; + mal_proc sio_setpar; + mal_proc sio_getpar; + mal_proc sio_getcap; + mal_proc sio_start; + mal_proc sio_stop; + mal_proc sio_read; + mal_proc sio_write; + mal_proc sio_onmove; + mal_proc sio_nfds; + mal_proc sio_pollfd; + mal_proc sio_revents; + mal_proc sio_eof; + mal_proc sio_setvol; + mal_proc sio_onvol; + mal_proc sio_initpar; + } sndio; +#endif +#ifdef MAL_SUPPORT_AUDIO4 + struct + { + int _unused; + } audio4; +#endif #ifdef MAL_SUPPORT_OSS struct { @@ -807,7 +1755,6 @@ struct mal_context mal_proc alcCaptureStart; mal_proc alcCaptureStop; mal_proc alcCaptureSamples; - mal_proc alEnable; mal_proc alDisable; mal_proc alIsEnabled; @@ -867,8 +1814,8 @@ struct mal_context mal_proc alGetBufferiv; mal_bool32 isEnumerationSupported : 1; - mal_bool32 isFloat32Supported : 1; - mal_bool32 isMCFormatsSupported : 1; + mal_bool32 isFloat32Supported : 1; + mal_bool32 isMCFormatsSupported : 1; } openal; #endif #ifdef MAL_SUPPORT_SDL @@ -908,10 +1855,16 @@ struct mal_context mal_proc CoCreateInstance; mal_proc CoTaskMemFree; mal_proc PropVariantClear; + mal_proc StringFromGUID2; /*HMODULE*/ mal_handle hUser32DLL; mal_proc GetForegroundWindow; mal_proc GetDesktopWindow; + + /*HMODULE*/ mal_handle hAdvapi32DLL; + mal_proc RegOpenKeyExA; + mal_proc RegCloseKey; + mal_proc RegQueryValueExA; } win32; #endif #ifdef MAL_POSIX @@ -928,41 +1881,54 @@ struct mal_context mal_proc pthread_cond_destroy; mal_proc pthread_cond_wait; mal_proc pthread_cond_signal; + mal_proc pthread_attr_init; + mal_proc pthread_attr_destroy; + mal_proc pthread_attr_setschedpolicy; + mal_proc pthread_attr_getschedparam; + mal_proc pthread_attr_setschedparam; } posix; #endif int _unused; }; }; -struct mal_device +MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device { mal_context* pContext; mal_device_type type; mal_format format; mal_uint32 channels; mal_uint32 sampleRate; - mal_uint8 channelMap[MAL_MAX_CHANNELS]; + mal_channel channelMap[MAL_MAX_CHANNELS]; mal_uint32 bufferSizeInFrames; + mal_uint32 bufferSizeInMilliseconds; mal_uint32 periods; mal_uint32 state; mal_recv_proc onRecv; mal_send_proc onSend; mal_stop_proc onStop; - void* pUserData; // Application defined data. + void* pUserData; // Application defined data. char name[256]; + mal_device_config initConfig; // The configuration passed in to mal_device_init(). Mainly used for reinitializing the backend device. mal_mutex lock; mal_event wakeupEvent; mal_event startEvent; mal_event stopEvent; mal_thread thread; - mal_result workResult; // This is set by the worker thread after it's finished doing a job. + mal_result workResult; // This is set by the worker thread after it's finished doing a job. + mal_bool32 usingDefaultFormat : 1; + mal_bool32 usingDefaultChannels : 1; + mal_bool32 usingDefaultSampleRate : 1; + mal_bool32 usingDefaultChannelMap : 1; mal_bool32 usingDefaultBufferSize : 1; mal_bool32 usingDefaultPeriods : 1; mal_bool32 exclusiveMode : 1; + mal_bool32 isOwnerOfContext : 1; // When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into mal_device_init(). + mal_bool32 isDefaultDevice : 1; // Used to determine if the backend should try reinitializing if the default device is unplugged. mal_format internalFormat; mal_uint32 internalChannels; mal_uint32 internalSampleRate; - mal_uint8 internalChannelMap[MAL_MAX_CHANNELS]; + mal_channel internalChannelMap[MAL_MAX_CHANNELS]; mal_dsp dsp; // Samples run through this to convert samples to a format suitable for use by the backend. mal_uint32 _dspFrameCount; // Internal use only. Used when running the device -> DSP -> client pipeline. See mal_device__on_read_from_device(). const mal_uint8* _dspFrames; // ^^^ AS ABOVE ^^^ @@ -975,15 +1941,17 @@ struct mal_device /*IAudioClient**/ mal_ptr pAudioClient; /*IAudioRenderClient**/ mal_ptr pRenderClient; /*IAudioCaptureClient**/ mal_ptr pCaptureClient; + /*IMMDeviceEnumerator**/ mal_ptr pDeviceEnumerator; /* <-- Used for IMMNotificationClient notifications. Required for detecting default device changes. */ + mal_IMMNotificationClient notificationClient; /*HANDLE*/ mal_handle hEvent; - /*HANDLE*/ mal_handle hStopEvent; + /*HANDLE*/ mal_handle hBreakEvent; /* <-- Used to break from WaitForMultipleObjects() in the main loop. */ mal_bool32 breakFromMainLoop; + mal_bool32 hasDefaultDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ } wasapi; #endif #ifdef MAL_SUPPORT_DSOUND struct { - /*HMODULE*/ mal_handle hDSoundDLL; /*LPDIRECTSOUND*/ mal_ptr pPlayback; /*LPDIRECTSOUNDBUFFER*/ mal_ptr pPlaybackPrimaryBuffer; /*LPDIRECTSOUNDBUFFER*/ mal_ptr pPlaybackBuffer; @@ -1019,12 +1987,54 @@ struct mal_device void* pIntermediaryBuffer; } alsa; #endif +#ifdef MAL_SUPPORT_PULSEAUDIO + struct + { + /*pa_mainloop**/ mal_ptr pMainLoop; + /*pa_mainloop_api**/ mal_ptr pAPI; + /*pa_context**/ mal_ptr pPulseContext; + /*pa_stream**/ mal_ptr pStream; + /*pa_context_state*/ mal_uint32 pulseContextState; + mal_uint32 fragmentSizeInBytes; + mal_bool32 breakFromMainLoop : 1; + } pulse; +#endif +#ifdef MAL_SUPPORT_JACK + struct + { + /*jack_client_t**/ mal_ptr pClient; + /*jack_port_t**/ mal_ptr pPorts[MAL_MAX_CHANNELS]; + float* pIntermediaryBuffer; // Typed as a float because JACK is always floating point. + } jack; +#endif #ifdef MAL_SUPPORT_COREAUDIO struct { - int _unused; + mal_uint32 deviceObjectID; + /*AudioComponent*/ mal_ptr component; // <-- Can this be per-context? + /*AudioUnit*/ mal_ptr audioUnit; + /*AudioBufferList**/ mal_ptr pAudioBufferList; // Only used for input devices. + mal_bool32 isSwitchingDevice; /* <-- Set to true when the default device has changed and mini_al is in the process of switching. */ } coreaudio; #endif +#ifdef MAL_SUPPORT_SNDIO + struct + { + mal_ptr handle; + mal_uint32 fragmentSizeInFrames; + mal_bool32 breakFromMainLoop; + void* pIntermediaryBuffer; + } sndio; +#endif +#ifdef MAL_SUPPORT_AUDIO4 + struct + { + int fd; + mal_uint32 fragmentSizeInFrames; + mal_bool32 breakFromMainLoop; + void* pIntermediaryBuffer; + } audio4; +#endif #ifdef MAL_SUPPORT_OSS struct { @@ -1088,7 +2098,7 @@ struct mal_device // // The context is used for selecting and initializing the relevant backends. // -// Note that the location of the device cannot change throughout it's lifetime. Consider allocating +// Note that the location of the context cannot change throughout it's lifetime. Consider allocating // the mal_context object with malloc() if this is an issue. The reason for this is that a pointer // to the context is stored in the mal_device structure. // @@ -1097,24 +2107,29 @@ struct mal_device // - WASAPI // - DirectSound // - WinMM -// - ALSA +// - Core Audio (Apple) +// - sndio +// - audio(4) // - OSS +// - PulseAudio +// - ALSA +// - JACK // - OpenSL|ES // - OpenAL // - SDL // - Null // -// The onLog callback is used for posting log messages back to the client for diagnostics, debugging, -// etc. You can pass NULL for this if you do not need it. +// is used to configure the context. Use the onLog config to set a callback for whenever a +// log message is posted. The priority of the worker thread can be set with the threadPriority config. +// +// It is recommended that only a single context is active at any given time because it's a bulky data +// structure which performs run-time linking for the relevant backends every time it's initialized. // // Return Value: // MAL_SUCCESS if successful; any other error code otherwise. // // Thread Safety: UNSAFE -// -// Effeciency: LOW -// This will dynamically load backends DLLs/SOs (such as dsound.dll). -mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pConfig, mal_context* pContext); +mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pConfig, mal_context* pContext); // Uninitializes a context. // @@ -1124,48 +2139,119 @@ mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, con // MAL_SUCCESS if successful; any other error code otherwise. // // Thread Safety: UNSAFE -// -// Efficiency: LOW -// This will unload the backend DLLs/SOs. mal_result mal_context_uninit(mal_context* pContext); -// Enumerates over each device of the given type (playback or capture). +// Enumerates over every device (both playback and capture). // -// It is _not_ safe to assume the first enumerated device is the default device. +// This is a lower-level enumeration function to the easier to use mal_context_get_devices(). Use +// mal_context_enumerate_devices() if you would rather not incur an internal heap allocation, or +// it simply suits your code better. +// +// Do _not_ assume the first enumerated device of a given type is the default device. // // Some backends and platforms may only support default playback and capture devices. // +// Note that this only retrieves the ID and name/description of the device. The reason for only +// retrieving basic information is that it would otherwise require opening the backend device in +// order to probe it for more detailed information which can be inefficient. Consider using +// mal_context_get_device_info() for this, but don't call it from within the enumeration callback. +// +// In general, you should not do anything complicated from within the callback. In particular, do +// not try initializing a device from within the callback. +// +// Consider using mal_context_get_devices() for a simpler and safer API, albeit at the expense of +// an internal heap allocation. +// +// Returning false from the callback will stop enumeration. Returning true will continue enumeration. +// // Return Value: // MAL_SUCCESS if successful; any other error code otherwise. // -// Thread Safety: SAFE, SEE NOTES. -// This API uses an application-defined buffer for output. This is thread-safe so long as the -// application ensures mutal exclusion to the output buffer at their level. +// Thread Safety: SAFE +// This is guarded using a simple mutex lock. +mal_result mal_context_enumerate_devices(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData); + +// Retrieves basic information about every active playback and/or capture device. // -// Efficiency: LOW -mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo); +// You can pass in NULL for the playback or capture lists in which case they'll be ignored. +// +// It is _not_ safe to assume the first device in the list is the default device. +// +// The returned pointers will become invalid upon the next call this this function, or when the +// context is uninitialized. Do not free the returned pointers. +// +// This function follows the same enumeration rules as mal_context_enumerate_devices(). See +// documentation for mal_context_enumerate_devices() for more information. +// +// Return Value: +// MAL_SUCCESS if successful; any other error code otherwise. +// +// Thread Safety: SAFE +// Since each call to this function invalidates the pointers from the previous call, you +// should not be calling this simultaneously across multiple threads. Instead, you need to +// make a copy of the returned data with your own higher level synchronization. +mal_result mal_context_get_devices(mal_context* pContext, mal_device_info** ppPlaybackDeviceInfos, mal_uint32* pPlaybackDeviceCount, mal_device_info** ppCaptureDeviceInfos, mal_uint32* pCaptureDeviceCount); + +// Retrieves information about a device with the given ID. +// +// Do _not_ call this from within the mal_context_enumerate_devices() callback. +// +// It's possible for a device to have different information and capabilities depending on wether or +// not it's opened in shared or exclusive mode. For example, in shared mode, WASAPI always uses +// floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this +// function allows you to specify which share mode you want information for. Note that not all +// backends and devices support shared or exclusive mode, in which case this function will fail +// if the requested share mode is unsupported. +// +// This leaves pDeviceInfo unmodified in the result of an error. +// +// Return Value: +// MAL_SUCCESS if successful; any other error code otherwise. +// +// Thread Safety: SAFE +// This is guarded using a simple mutex lock. +mal_result mal_context_get_device_info(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo); // Initializes a device. // +// The context can be null in which case it uses the default. This is equivalent to passing in a +// context that was initialized like so: +// +// mal_context_init(NULL, 0, NULL, &context); +// +// Do not pass in null for the context if you are needing to open multiple devices. You can, +// however, use null when initializing the first device, and then use device.pContext for the +// initialization of other devices. +// // The device ID (pDeviceID) can be null, in which case the default device is used. Otherwise, you -// can retrieve the ID by calling mal_enumerate_devices() and using the ID from the returned data. +// can retrieve the ID by calling mal_context_get_devices() and using the ID from the returned data. // Set pDeviceID to NULL to use the default device. Do _not_ rely on the first device ID returned -// by mal_enumerate_devices() to be the default device. +// by mal_context_enumerate_devices() or mal_context_get_devices() to be the default device. // -// This will try it's hardest to create a valid device, even if it means adjusting input arguments. -// Look at pDevice->internalChannels, pDevice->internalSampleRate, etc. to determine the actual -// properties after initialization. +// The device's configuration is controlled with pConfig. This allows you to configure the sample +// format, channel count, sample rate, etc. Before calling mal_device_init(), you will most likely +// want to initialize a mal_device_config object using mal_device_config_init(), +// mal_device_config_init_playback(), etc. You can also pass in NULL for the device config in +// which case it will use defaults, but will require you to call mal_device_set_recv_callback() or +// mal_device_set_send_callback() before starting the device. // -// If is 0, it will default to MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS. If -// is set to 0 it will default to MAL_DEFAULT_PERIODS. +// Passing in 0 to any property in pConfig will force the use of a default value. In the case of +// sample format, channel count, sample rate and channel map it will default to the values used by +// the backend's internal device. For the size of the buffer you can set bufferSizeInFrames or +// bufferSizeInMilliseconds (if both are set it will prioritize bufferSizeInFrames). If both are +// set to zero, it will default to MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY or +// MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE, depending on whether or not performanceProfile +// is set to mal_performance_profile_low_latency or mal_performance_profile_conservative. +// +// When sending or receiving data to/from a device, mini_al will internally perform a format +// conversion to convert between the format specified by pConfig and the format used internally by +// the backend. If you pass in NULL for pConfig or 0 for the sample format, channel count, +// sample rate _and_ channel map, data transmission will run on an optimized pass-through fast path. // // The property controls how frequently the background thread is woken to check for more // data. It's tied to the buffer size, so as an example, if your buffer size is equivalent to 10 // milliseconds and you have 2 periods, the CPU will wake up approximately every 5 milliseconds. // -// Use mal_device_config_init(), mal_device_config_init_playback(), etc. to initialize a -// mal_device_config object. -// // When compiling for UWP you must ensure you call this function on the main UI thread because the // operating system may need to present the user with a message asking for permissions. Please refer // to the official documentation for ActivateAudioInterfaceAsync() for more information. @@ -1175,15 +2261,16 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma // // Thread Safety: UNSAFE // It is not safe to call this function simultaneously for different devices because some backends -// depend on and mutate global state (such as OpenSL|ES). The same applies to calling this as the +// depend on and mutate global state (such as OpenSL|ES). The same applies to calling this at the // same time as mal_device_uninit(). -// -// Results are undefined if you try using a device before this function has returned. -// -// Efficiency: LOW -// This is just slow due to the nature of it being an initialization API. mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice); +// Initializes a device without a context, with extra parameters for controlling the configuration +// of the internal self-managed context. +// +// See mal_device_init() and mal_context_init(). +mal_result mal_device_init_ex(const mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pContextConfig, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice); + // Uninitializes a device. // // This will explicitly stop the device. You do not need to call mal_device_stop() beforehand, but it's @@ -1195,10 +2282,6 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi // Thread Safety: UNSAFE // As soon as this API is called the device should be considered undefined. All bets are off if you // try using the device at the same time as uninitializing it. -// -// Efficiency: LOW -// This will stop the device with mal_device_stop() which is a slow, synchronized call. It also needs -// to destroy internal objects like the backend-specific objects and the background thread. void mal_device_uninit(mal_device* pDevice); // Sets the callback to use when the application has received data from the device. @@ -1206,8 +2289,7 @@ void mal_device_uninit(mal_device* pDevice); // Thread Safety: SAFE // This API is implemented as a simple atomic assignment. // -// Efficiency: HIGH -// This is just an atomic assignment. +// DEPRECATED. Set this when the device is initialized with mal_device_init*(). void mal_device_set_recv_callback(mal_device* pDevice, mal_recv_proc proc); // Sets the callback to use when the application needs to send data to the device for playback. @@ -1219,17 +2301,13 @@ void mal_device_set_recv_callback(mal_device* pDevice, mal_recv_proc proc); // Thread Safety: SAFE // This API is implemented as a simple atomic assignment. // -// Efficiency: HIGH -// This is just an atomic assignment. +// DEPRECATED. Set this when the device is initialized with mal_device_init*(). void mal_device_set_send_callback(mal_device* pDevice, mal_send_proc proc); // Sets the callback to use when the device has stopped, either explicitly or as a result of an error. // // Thread Safety: SAFE // This API is implemented as a simple atomic assignment. -// -// Efficiency: HIGH -// This is just an atomic assignment. void mal_device_set_stop_callback(mal_device* pDevice, mal_stop_proc proc); // Activates the device. For playback devices this begins playback. For capture devices it begins @@ -1239,6 +2317,9 @@ void mal_device_set_stop_callback(mal_device* pDevice, mal_stop_proc proc); // returning. The reason for this is to ensure there is valid audio data in the buffer, which needs // to be done _before_ the device begins playback. // +// This API waits until the backend device has been started for real by the worker thread. It also +// waits on a mutex for thread-safety. +// // Return Value: // - MAL_SUCCESS if successful; any other error code otherwise. // - MAL_INVALID_ARGS @@ -1262,14 +2343,15 @@ void mal_device_set_stop_callback(mal_device* pDevice, mal_stop_proc proc); // There was a backend-specific error starting the device. // // Thread Safety: SAFE -// -// Efficiency: LOW -// This API waits until the backend device has been started for real by the worker thread. It also -// waits on a mutex for thread-safety. mal_result mal_device_start(mal_device* pDevice); // Puts the device to sleep, but does not uninitialize it. Use mal_device_start() to start it up again. // +// This API needs to wait on the worker thread to stop the backend device properly before returning. It +// also waits on a mutex for thread-safety. In addition, some backends need to wait for the device to +// finish playback/recording of the current fragment which can take some time (usually proportionate to +// the buffer size that was specified at initialization time). +// // Return Value: // - MAL_SUCCESS if successful; any other error code otherwise. // - MAL_INVALID_ARGS @@ -1289,50 +2371,45 @@ mal_result mal_device_start(mal_device* pDevice); // There was a backend-specific error stopping the device. // // Thread Safety: SAFE -// -// Efficiency: LOW -// This API needs to wait on the worker thread to stop the backend device properly before returning. It -// also waits on a mutex for thread-safety. -// -// In addition, some backends need to wait for the device to finish playback/recording of the current -// fragment which can take some time (usually proportionate to the buffer size used when initializing -// the device). mal_result mal_device_stop(mal_device* pDevice); // Determines whether or not the device is started. // +// This is implemented as a simple accessor. +// // Return Value: // True if the device is started, false otherwise. // // Thread Safety: SAFE // If another thread calls mal_device_start() or mal_device_stop() at this same time as this function // is called, there's a very small chance the return value will be out of sync. -// -// Efficiency: HIGH -// This is implemented with a simple accessor. mal_bool32 mal_device_is_started(mal_device* pDevice); // Retrieves the size of the buffer in bytes for the given device. // +// This API is efficient and is implemented with just a few 32-bit integer multiplications. +// // Thread Safety: SAFE // This is calculated from constant values which are set at initialization time and never change. -// -// Efficiency: HIGH -// This is implemented with just a few 32-bit integer multiplications. mal_uint32 mal_device_get_buffer_size_in_bytes(mal_device* pDevice); -// Retrieves the size of a sample in bytes for the given format. -// -// Thread Safety: SAFE -// This is API is pure. -// -// Efficiency: HIGH -// This is implemented with a lookup table. -mal_uint32 mal_get_sample_size_in_bytes(mal_format format); // Helper function for initializing a mal_context_config object. mal_context_config mal_context_config_init(mal_log_proc onLog); +// Initializes a default device config. +// +// A default configuration will configure the device such that the format, channel count, sample rate and channel map are +// the same as the backend's internal configuration. This means the application loses explicit control of these properties, +// but in return gets an optimized fast path for data transmission since mini_al will be releived of all format conversion +// duties. You will not typically want to use default configurations unless you have some specific low-latency requirements. +// +// mal_device_config_init(), mal_device_config_init_playback(), etc. will allow you to explicitly set the sample format, +// channel count, etc. +mal_device_config mal_device_config_init_default(void); +mal_device_config mal_device_config_init_default_capture(mal_recv_proc onRecvCallback); +mal_device_config mal_device_config_init_default_playback(mal_send_proc onSendCallback); + // Helper function for initializing a mal_device_config object. // // This is just a helper API, and as such the returned object can be safely modified as needed. @@ -1343,32 +2420,40 @@ mal_context_config mal_context_config_init(mal_log_proc onLog); // |---------------|------------------------------| // | Channel Count | Mapping | // |---------------|------------------------------| -// | 1 (Mono) | 0: MAL_CHANNEL_FRONT_CENTER | +// | 1 (Mono) | 0: MAL_CHANNEL_MONO | // |---------------|------------------------------| // | 2 (Stereo) | 0: MAL_CHANNEL_FRONT_LEFT | // | | 1: MAL_CHANNEL_FRONT_RIGHT | // |---------------|------------------------------| -// | 3 (2.1) | 0: MAL_CHANNEL_FRONT_LEFT | +// | 3 | 0: MAL_CHANNEL_FRONT_LEFT | // | | 1: MAL_CHANNEL_FRONT_RIGHT | -// | | 2: MAL_CHANNEL_LFE | +// | | 2: MAL_CHANNEL_FRONT_CENTER | // |---------------|------------------------------| -// | 4 (Quad) | 0: MAL_CHANNEL_FRONT_LEFT | +// | 4 (Surround) | 0: MAL_CHANNEL_FRONT_LEFT | // | | 1: MAL_CHANNEL_FRONT_RIGHT | -// | | 2: MAL_CHANNEL_BACK_LEFT | -// | | 3: MAL_CHANNEL_BACK_RIGHT | +// | | 2: MAL_CHANNEL_FRONT_CENTER | +// | | 3: MAL_CHANNEL_BACK_CENTER | // |---------------|------------------------------| -// | 5 (4.1) | 0: MAL_CHANNEL_FRONT_LEFT | +// | 5 | 0: MAL_CHANNEL_FRONT_LEFT | // | | 1: MAL_CHANNEL_FRONT_RIGHT | -// | | 2: MAL_CHANNEL_BACK_LEFT | -// | | 3: MAL_CHANNEL_BACK_RIGHT | -// | | 4: MAL_CHANNEL_LFE | +// | | 2: MAL_CHANNEL_FRONT_CENTER | +// | | 3: MAL_CHANNEL_BACK_LEFT | +// | | 4: MAL_CHANNEL_BACK_RIGHT | // |---------------|------------------------------| // | 6 (5.1) | 0: MAL_CHANNEL_FRONT_LEFT | // | | 1: MAL_CHANNEL_FRONT_RIGHT | // | | 2: MAL_CHANNEL_FRONT_CENTER | // | | 3: MAL_CHANNEL_LFE | -// | | 4: MAL_CHANNEL_BACK_LEFT | -// | | 5: MAL_CHANNEL_BACK_RIGHT | +// | | 4: MAL_CHANNEL_SIDE_LEFT | +// | | 5: MAL_CHANNEL_SIDE_RIGHT | +// |---------------|------------------------------| +// | 7 | 0: MAL_CHANNEL_FRONT_LEFT | +// | | 1: MAL_CHANNEL_FRONT_RIGHT | +// | | 2: MAL_CHANNEL_FRONT_CENTER | +// | | 3: MAL_CHANNEL_LFE | +// | | 4: MAL_CHANNEL_BACK_CENTER | +// | | 4: MAL_CHANNEL_SIDE_LEFT | +// | | 5: MAL_CHANNEL_SIDE_RIGHT | // |---------------|------------------------------| // | 8 (7.1) | 0: MAL_CHANNEL_FRONT_LEFT | // | | 1: MAL_CHANNEL_FRONT_RIGHT | @@ -1388,91 +2473,26 @@ mal_context_config mal_context_config_init(mal_log_proc onLog); // // Efficiency: HIGH // This just returns a stack allocated object and consists of just a few assignments. -mal_device_config mal_device_config_init(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback, mal_send_proc onSendCallback); +mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_recv_proc onRecvCallback, mal_send_proc onSendCallback); + +// A simplified version of mal_device_config_init_ex(). +static MAL_INLINE mal_device_config mal_device_config_init(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback, mal_send_proc onSendCallback) { return mal_device_config_init_ex(format, channels, sampleRate, NULL, onRecvCallback, onSendCallback); } // A simplified version of mal_device_config_init() for capture devices. -static inline mal_device_config mal_device_config_init_capture(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback) { return mal_device_config_init(format, channels, sampleRate, onRecvCallback, NULL); } +static MAL_INLINE mal_device_config mal_device_config_init_capture_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_recv_proc onRecvCallback) { return mal_device_config_init_ex(format, channels, sampleRate, channelMap, onRecvCallback, NULL); } +static MAL_INLINE mal_device_config mal_device_config_init_capture(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback) { return mal_device_config_init_capture_ex(format, channels, sampleRate, NULL, onRecvCallback); } // A simplified version of mal_device_config_init() for playback devices. -static inline mal_device_config mal_device_config_init_playback(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_send_proc onSendCallback) { return mal_device_config_init(format, channels, sampleRate, NULL, onSendCallback); } +static MAL_INLINE mal_device_config mal_device_config_init_playback_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_send_proc onSendCallback) { return mal_device_config_init_ex(format, channels, sampleRate, channelMap, NULL, onSendCallback); } +static MAL_INLINE mal_device_config mal_device_config_init_playback(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_send_proc onSendCallback) { return mal_device_config_init_playback_ex(format, channels, sampleRate, NULL, onSendCallback); } - -/////////////////////////////////////////////////////////////////////////////// -// -// SRC -// -/////////////////////////////////////////////////////////////////////////////// - -// Initializes a sample rate conversion object. -mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC); - -// Dynamically adjusts the output sample rate. -// -// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this -// is not acceptable you will need to use your own algorithm. -mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut); - -// Reads a number of frames. -// -// Returns the number of frames actually read. -mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut); - -// The same mal_src_read_frames() with extra control over whether or not the internal buffers should be flushed at the end. -// -// Internally there exists a buffer that keeps track of the previous and next samples for sample rate conversion. The simple -// version of this function does _not_ flush this buffer because otherwise it causes glitches for streaming based conversion -// pipelines. The problem, however, is that sometimes you need those last few samples (such as if you're doing a bulk conversion -// of a static file). Enabling flushing will fix this for you. -mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); - - - -/////////////////////////////////////////////////////////////////////////////// -// -// DSP -// -/////////////////////////////////////////////////////////////////////////////// - -// Initializes a DSP object. -mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp_read_proc onRead, void* pUserData, mal_dsp* pDSP); - -// Dynamically adjusts the output sample rate. -// -// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this -// is not acceptable you will need to use your own algorithm. -mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut); - -// Reads a number of frames and runs them through the DSP processor. -// -// This this _not_ flush the internal buffers which means you may end up with a few less frames than you may expect. Look at -// mal_dsp_read_frames_ex() if you want to flush the buffers at the end of the read. -mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut); - -// The same mal_dsp_read_frames() with extra control over whether or not the internal buffers should be flushed at the end. -// -// See documentation for mal_src_read_frames_ex() for an explanation on flushing. -mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); - -// High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to -// determine the required size of the output buffer. -// -// A return value of 0 indicates an error. -// -// This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead. -mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint32 frameCountIn); - -// Helper for initializing a mal_dsp_config object. -mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut); - - - -/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Utiltities // -/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Creates a mutex. // @@ -1489,50 +2509,146 @@ void mal_mutex_lock(mal_mutex* pMutex); void mal_mutex_unlock(mal_mutex* pMutex); - -/////////////////////////////////////////////////////////////////////////////// -// -// Miscellaneous Helpers -// -/////////////////////////////////////////////////////////////////////////////// - // Retrieves a friendly name for a backend. const char* mal_get_backend_name(mal_backend backend); -// Retrieves a friendly name for a format. -const char* mal_get_format_name(mal_format format); - -// Blends two frames in floating point format. -void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels); - - - -/////////////////////////////////////////////////////////////////////////////// +// Adjust buffer size based on a scaling factor. // -// Format Conversion +// This just multiplies the base size by the scaling factor, making sure it's a size of at least 1. +mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale); + +// Calculates a buffer size in milliseconds from the specified number of frames and sample rate. +mal_uint32 mal_calculate_buffer_size_in_milliseconds_from_frames(mal_uint32 bufferSizeInFrames, mal_uint32 sampleRate); + +// Calculates a buffer size in frames from the specified number of milliseconds and sample rate. +mal_uint32 mal_calculate_buffer_size_in_frames_from_milliseconds(mal_uint32 bufferSizeInMilliseconds, mal_uint32 sampleRate); + +// Retrieves the default buffer size in milliseconds based on the specified performance profile. +mal_uint32 mal_get_default_buffer_size_in_milliseconds(mal_performance_profile performanceProfile); + +// Calculates a buffer size in frames for the specified performance profile and scale factor. +mal_uint32 mal_get_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate); + +#endif // MAL_NO_DEVICE_IO + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // -/////////////////////////////////////////////////////////////////////////////// -void mal_pcm_u8_to_s16(short* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_s24(void* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_s32(int* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_f32(float* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_s16_to_u8(unsigned char* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_s24(void* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_s32(int* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_f32(float* pOut, const short* pIn, unsigned int count); -void mal_pcm_s24_to_u8(unsigned char* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_s16(short* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_s32(int* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_f32(float* pOut, const void* pIn, unsigned int count); -void mal_pcm_s32_to_u8(unsigned char* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_s16(short* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_s24(void* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_f32(float* pOut, const int* pIn, unsigned int count); -void mal_pcm_f32_to_u8(unsigned char* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s16(short* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s24(void* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count); -void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, unsigned int sampleCount); +// Decoding +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef MAL_NO_DECODING + +typedef struct mal_decoder mal_decoder; + +typedef enum +{ + mal_seek_origin_start, + mal_seek_origin_current +} mal_seek_origin; + +typedef size_t (* mal_decoder_read_proc) (mal_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); // Returns the number of bytes read. +typedef mal_bool32 (* mal_decoder_seek_proc) (mal_decoder* pDecoder, int byteOffset, mal_seek_origin origin); +typedef mal_result (* mal_decoder_seek_to_frame_proc)(mal_decoder* pDecoder, mal_uint64 frameIndex); +typedef mal_result (* mal_decoder_uninit_proc) (mal_decoder* pDecoder); + +typedef struct +{ + mal_format format; // Set to 0 or mal_format_unknown to use the stream's internal format. + mal_uint32 channels; // Set to 0 to use the stream's internal channels. + mal_uint32 sampleRate; // Set to 0 to use the stream's internal sample rate. + mal_channel channelMap[MAL_MAX_CHANNELS]; + mal_channel_mix_mode channelMixMode; + mal_dither_mode ditherMode; + mal_src_algorithm srcAlgorithm; + union + { + mal_src_config_sinc sinc; + } src; +} mal_decoder_config; + +struct mal_decoder +{ + mal_decoder_read_proc onRead; + mal_decoder_seek_proc onSeek; + void* pUserData; + mal_format internalFormat; + mal_uint32 internalChannels; + mal_uint32 internalSampleRate; + mal_channel internalChannelMap[MAL_MAX_CHANNELS]; + mal_format outputFormat; + mal_uint32 outputChannels; + mal_uint32 outputSampleRate; + mal_channel outputChannelMap[MAL_MAX_CHANNELS]; + mal_dsp dsp; // <-- Format conversion is achieved by running frames through this. + mal_decoder_seek_to_frame_proc onSeekToFrame; + mal_decoder_uninit_proc onUninit; + void* pInternalDecoder; // <-- The drwav/drflac/stb_vorbis/etc. objects. + struct + { + const mal_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; // Only used for decoders that were opened against a block of memory. +}; + +mal_decoder_config mal_decoder_config_init(mal_format outputFormat, mal_uint32 outputChannels, mal_uint32 outputSampleRate); + +mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_wav(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_flac(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_vorbis(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_mp3(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_raw(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder); + +mal_result mal_decoder_init_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_memory_wav(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_memory_flac(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_memory_mp3(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_memory_raw(const void* pData, size_t dataSize, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder); + +#ifndef MAL_NO_STDIO +mal_result mal_decoder_init_file(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +mal_result mal_decoder_init_file_wav(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder); +#endif + +mal_result mal_decoder_uninit(mal_decoder* pDecoder); + +mal_uint64 mal_decoder_read(mal_decoder* pDecoder, mal_uint64 frameCount, void* pFramesOut); +mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameIndex); + + +// Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with mal_free(). On input, +// pConfig should be set to what you want. On output it will be set to what you got. +#ifndef MAL_NO_STDIO +mal_result mal_decode_file(const char* pFilePath, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut); +#endif +mal_result mal_decode_memory(const void* pData, size_t dataSize, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut); + +#endif // MAL_NO_DECODING + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Generation +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + double amplitude; + double periodsPerSecond; + double delta; + double time; +} mal_sine_wave; + +mal_result mal_sine_wave_init(double amplitude, double period, mal_uint32 sampleRate, mal_sine_wave* pSineWave); +mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float* pSamples); +mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount, mal_uint32 channels, mal_stream_layout layout, float** ppFrames); + #ifdef __cplusplus } @@ -1540,15 +2656,21 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form #endif //mini_al_h -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // IMPLEMENTATION // -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -#ifdef MAL_IMPLEMENTATION +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if defined(MINI_AL_IMPLEMENTATION) || defined(MAL_IMPLEMENTATION) #include +#include // For INT_MAX +#include // sin(), etc. + +#if defined(MAL_DEBUG_OUTPUT) +#include // for printf() for debug output +#endif #ifdef MAL_WIN32 #include @@ -1595,203 +2717,483 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form #endif #endif +// Architecture Detection +#if defined(__x86_64__) || defined(_M_X64) +#define MAL_X64 +#elif defined(__i386) || defined(_M_IX86) +#define MAL_X86 +#elif defined(__arm__) || defined(_M_ARM) +#define MAL_ARM +#endif -// Disable run-time linking on certain backends. -#ifndef MAL_NO_RUNTIME_LINKING - #if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTEN) - #define MAL_NO_RUNTIME_LINKING +// Cannot currently support AVX-512 if AVX is disabled. +#if !defined(MAL_NO_AVX512) && defined(MAL_NO_AVX2) +#define MAL_NO_AVX512 +#endif + +// Intrinsics Support +#if defined(MAL_X64) || defined(MAL_X86) + #if defined(_MSC_VER) && !defined(__clang__) + // MSVC. + #if !defined(MAL_NO_SSE2) // Assume all MSVC compilers support SSE2 intrinsics. + #define MAL_SUPPORT_SSE2 + #endif + //#if _MSC_VER >= 1600 && !defined(MAL_NO_AVX) // 2010 + // #define MAL_SUPPORT_AVX + //#endif + #if _MSC_VER >= 1700 && !defined(MAL_NO_AVX2) // 2012 + #define MAL_SUPPORT_AVX2 + #endif + #if _MSC_VER >= 1910 && !defined(MAL_NO_AVX512) // 2017 + #define MAL_SUPPORT_AVX512 + #endif + #else + // Assume GNUC-style. + #if defined(__SSE2__) && !defined(MAL_NO_SSE2) + #define MAL_SUPPORT_SSE2 + #endif + //#if defined(__AVX__) && !defined(MAL_NO_AVX) + // #define MAL_SUPPORT_AVX + //#endif + #if defined(__AVX2__) && !defined(MAL_NO_AVX2) + #define MAL_SUPPORT_AVX2 + #endif + #if defined(__AVX512F__) && !defined(MAL_NO_AVX512) + #define MAL_SUPPORT_AVX512 + #endif + #endif + + // If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. + #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) + #if !defined(MAL_SUPPORT_SSE2) && !defined(MAL_NO_SSE2) && __has_include() + #define MAL_SUPPORT_SSE2 + #endif + //#if !defined(MAL_SUPPORT_AVX) && !defined(MAL_NO_AVX) && __has_include() + // #define MAL_SUPPORT_AVX + //#endif + #if !defined(MAL_SUPPORT_AVX2) && !defined(MAL_NO_AVX2) && __has_include() + #define MAL_SUPPORT_AVX2 + #endif + #if !defined(MAL_SUPPORT_AVX512) && !defined(MAL_NO_AVX512) && __has_include() + #define MAL_SUPPORT_AVX512 + #endif + #endif + + #if defined(MAL_SUPPORT_AVX512) + #include // Not a mistake. Intentionally including instead of because otherwise the compiler will complain. + #elif defined(MAL_SUPPORT_AVX2) || defined(MAL_SUPPORT_AVX) + #include + #elif defined(MAL_SUPPORT_SSE2) + #include #endif #endif -// Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not -// certain unused functions and variables can be excluded from the build to avoid warnings. -#ifdef MAL_ENABLE_WASAPI - #define MAL_HAS_WASAPI - #ifdef __has_include - #if !__has_include() - #undef MAL_HAS_WASAPI - #endif +#if defined(MAL_ARM) + #if !defined(MAL_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define MAL_SUPPORT_NEON #endif -#endif -#ifdef MAL_ENABLE_DSOUND - #define MAL_HAS_DSOUND - #ifdef __has_include - #if !__has_include() - #undef MAL_HAS_DSOUND - #endif - #endif -#endif -#ifdef MAL_ENABLE_WINMM - #define MAL_HAS_WINMM // Every compiler I'm aware of supports WinMM. -#endif -#ifdef MAL_ENABLE_ALSA - #define MAL_HAS_ALSA - #ifdef __has_include - #if !__has_include() - #undef MAL_HAS_ALSA - #endif - #endif -#endif -#ifdef MAL_ENABLE_COREAUDIO - #define MAL_HAS_COREAUDIO -#endif -#ifdef MAL_ENABLE_OSS - #define MAL_HAS_OSS // OSS is the only supported backend for Unix and BSD, so it must be present else this library is useless. -#endif -#ifdef MAL_ENABLE_OPENSL - #define MAL_HAS_OPENSL // Like OSS, OpenSL is the only supported backend for Android. It must be present. -#endif -#ifdef MAL_ENABLE_OPENAL - #define MAL_HAS_OPENAL - #ifdef MAL_NO_RUNTIME_LINKING - #ifdef __has_include - #if !__has_include() - #undef MAL_HAS_OPENAL - #endif - #endif - #endif -#endif -#ifdef MAL_ENABLE_SDL - #define MAL_HAS_SDL - // SDL headers are necessary if using compile-time linking. - #ifdef MAL_NO_RUNTIME_LINKING - #ifdef __has_include - #ifdef MAL_EMSCRIPTEN - #if !__has_include() - #undef MAL_HAS_SDL - #endif + // Fall back to looking for the #include file. + #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) + #if !defined(MAL_SUPPORT_NEON) && !defined(MAL_NO_NEON) && __has_include() + #define MAL_SUPPORT_NEON + #endif + #endif + + #if defined(MAL_SUPPORT_NEON) + #include + #endif +#endif + + +#if defined(MAL_X64) || defined(MAL_X86) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 + #include + static MAL_INLINE void mal_cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define MAL_NO_CPUID + #endif + + #if _MSC_VER >= 1600 + static MAL_INLINE unsigned __int64 mal_xgetbv(int reg) + { + return _xgetbv(reg); + } + #else + #define MAL_NO_XGETBV + #endif + #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MAL_ANDROID) + static MAL_INLINE void mal_cpuid(int info[4], int fid) + { + // It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the + // specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for + // supporting different assembly dialects. + // + // What's basically happening is that we're saving and restoring the ebx register manually. + #if defined(DRFLAC_X86) && defined(__PIC__) + __asm__ __volatile__ ( + "xchg{l} {%%}ebx, %k1;" + "cpuid;" + "xchg{l} {%%}ebx, %k1;" + : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) + ); #else - #if !__has_include() - #undef MAL_HAS_SDL - #endif + __asm__ __volatile__ ( + "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) + ); + #endif + } + + static MAL_INLINE unsigned long long mal_xgetbv(int reg) + { + unsigned int hi; + unsigned int lo; + + __asm__ __volatile__ ( + "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) + ); + + return ((unsigned long long)hi << 32ULL) | (unsigned long long)lo; + } + #else + #define MAL_NO_CPUID + #define MAL_NO_XGETBV + #endif +#else + #define MAL_NO_CPUID + #define MAL_NO_XGETBV +#endif + +static MAL_INLINE mal_bool32 mal_has_sse2() +{ +#if defined(MAL_SUPPORT_SSE2) + #if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_SSE2) + #if defined(MAL_X64) + return MAL_TRUE; // 64-bit targets always support SSE2. + #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) + return MAL_TRUE; // If the compiler is allowed to freely generate SSE2 code we can assume support. + #else + #if defined(MAL_NO_CPUID) + return MAL_FALSE; + #else + int info[4]; + mal_cpuid(info, 1); + return (info[3] & (1 << 26)) != 0; #endif #endif + #else + return MAL_FALSE; // SSE2 is only supported on x86 and x64 architectures. #endif -#endif -#ifdef MAL_ENABLE_NULL - #define MAL_HAS_NULL // Everything supports the null backend. -#endif - - -#ifdef MAL_WIN32 - #define MAL_THREADCALL WINAPI - typedef unsigned long mal_thread_result; #else - #define MAL_THREADCALL - typedef void* mal_thread_result; + return MAL_FALSE; // No compiler support. #endif -typedef mal_thread_result (MAL_THREADCALL * mal_thread_entry_proc)(void* pData); +} -#ifdef MAL_WIN32 -typedef HRESULT (WINAPI * MAL_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); -typedef void (WINAPI * MAL_PFN_CoUninitialize)(); -typedef HRESULT (WINAPI * MAL_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); -typedef void (WINAPI * MAL_PFN_CoTaskMemFree)(LPVOID pv); -typedef HRESULT (WINAPI * MAL_PFN_PropVariantClear)(PROPVARIANT *pvar); +#if 0 +static MAL_INLINE mal_bool32 mal_has_avx() +{ +#if defined(MAL_SUPPORT_AVX) + #if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_AVX) + #if defined(_AVX_) || defined(__AVX__) + return MAL_TRUE; // If the compiler is allowed to freely generate AVX code we can assume support. + #else + // AVX requires both CPU and OS support. + #if defined(MAL_NO_CPUID) || defined(MAL_NO_XGETBV) + return MAL_FALSE; + #else + int info[4]; + mal_cpuid(info, 1); + if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { + mal_uint64 xrc = mal_xgetbv(0); + if ((xrc & 0x06) == 0x06) { + return MAL_TRUE; + } else { + return MAL_FALSE; + } + } else { + return MAL_FALSE; + } + #endif + #endif + #else + return MAL_FALSE; // AVX is only supported on x86 and x64 architectures. + #endif +#else + return MAL_FALSE; // No compiler support. +#endif +} +#endif -typedef HWND (WINAPI * MAL_PFN_GetForegroundWindow)(); -typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)(); +static MAL_INLINE mal_bool32 mal_has_avx2() +{ +#if defined(MAL_SUPPORT_AVX2) + #if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_AVX2) + #if defined(_AVX2_) || defined(__AVX2__) + return MAL_TRUE; // If the compiler is allowed to freely generate AVX2 code we can assume support. + #else + // AVX2 requires both CPU and OS support. + #if defined(MAL_NO_CPUID) || defined(MAL_NO_XGETBV) + return MAL_FALSE; + #else + int info1[4]; + int info7[4]; + mal_cpuid(info1, 1); + mal_cpuid(info7, 7); + if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { + mal_uint64 xrc = mal_xgetbv(0); + if ((xrc & 0x06) == 0x06) { + return MAL_TRUE; + } else { + return MAL_FALSE; + } + } else { + return MAL_FALSE; + } + #endif + #endif + #else + return MAL_FALSE; // AVX2 is only supported on x86 and x64 architectures. + #endif +#else + return MAL_FALSE; // No compiler support. +#endif +} + +static MAL_INLINE mal_bool32 mal_has_avx512f() +{ +#if defined(MAL_SUPPORT_AVX512) + #if (defined(MAL_X64) || defined(MAL_X86)) && !defined(MAL_NO_AVX512) + #if defined(__AVX512F__) + return MAL_TRUE; // If the compiler is allowed to freely generate AVX-512F code we can assume support. + #else + // AVX-512 requires both CPU and OS support. + #if defined(MAL_NO_CPUID) || defined(MAL_NO_XGETBV) + return MAL_FALSE; + #else + int info1[4]; + int info7[4]; + mal_cpuid(info1, 1); + mal_cpuid(info7, 7); + if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 16)) != 0)) { + mal_uint64 xrc = mal_xgetbv(0); + if ((xrc & 0xE6) == 0xE6) { + return MAL_TRUE; + } else { + return MAL_FALSE; + } + } else { + return MAL_FALSE; + } + #endif + #endif + #else + return MAL_FALSE; // AVX-512F is only supported on x86 and x64 architectures. + #endif +#else + return MAL_FALSE; // No compiler support. +#endif +} + +static MAL_INLINE mal_bool32 mal_has_neon() +{ +#if defined(MAL_SUPPORT_NEON) + #if defined(MAL_ARM) && !defined(MAL_NO_NEON) + #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + return MAL_TRUE; // If the compiler is allowed to freely generate NEON code we can assume support. + #else + // TODO: Runtime check. + return MAL_FALSE; + #endif + #else + return MAL_FALSE; // NEON is only supported on ARM architectures. + #endif +#else + return MAL_FALSE; // No compiler support. +#endif +} + + +static MAL_INLINE mal_bool32 mal_is_little_endian() +{ +#if defined(MAL_X86) || defined(MAL_X64) + return MAL_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + +static MAL_INLINE mal_bool32 mal_is_big_endian() +{ + return !mal_is_little_endian(); +} + + +#ifndef MAL_COINIT_VALUE +#define MAL_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED*/ #endif -#define MAL_STATE_UNINITIALIZED 0 -#define MAL_STATE_STOPPED 1 // The device's default state after initialization. -#define MAL_STATE_STARTED 2 // The worker thread is in it's main loop waiting for the driver to request or deliver audio data. -#define MAL_STATE_STARTING 3 // Transitioning from a stopped state to started. -#define MAL_STATE_STOPPING 4 // Transitioning from a started state to stopped. + +#ifndef MAL_PI +#define MAL_PI 3.14159265358979323846264f +#endif +#ifndef MAL_PI_D +#define MAL_PI_D 3.14159265358979323846264 +#endif +#ifndef MAL_TAU +#define MAL_TAU 6.28318530717958647693f +#endif +#ifndef MAL_TAU_D +#define MAL_TAU_D 6.28318530717958647693 +#endif -// The default size of the device's buffer in milliseconds. -// -// If this is too small you may get underruns and overruns in which case you'll need to either increase -// this value or use an explicit buffer size. -#ifndef MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS -#define MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS 25 +// The default format when mal_format_unknown (0) is requested when initializing a device. +#ifndef MAL_DEFAULT_FORMAT +#define MAL_DEFAULT_FORMAT mal_format_f32 +#endif + +// The default channel count to use when 0 is used when initializing a device. +#ifndef MAL_DEFAULT_CHANNELS +#define MAL_DEFAULT_CHANNELS 2 +#endif + +// The default sample rate to use when 0 is used when initializing a device. +#ifndef MAL_DEFAULT_SAMPLE_RATE +#define MAL_DEFAULT_SAMPLE_RATE 48000 #endif // Default periods when none is specified in mal_device_init(). More periods means more work on the CPU. #ifndef MAL_DEFAULT_PERIODS -#define MAL_DEFAULT_PERIODS 2 +#define MAL_DEFAULT_PERIODS 2 #endif +// The base buffer size in milliseconds for low latency mode. +#ifndef MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY +#define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY 25 +#endif + +// The base buffer size in milliseconds for conservative mode. +#ifndef MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE +#define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE 150 +#endif + + +// Standard sample rates, in order of priority. +mal_uint32 g_malStandardSampleRatePriorities[] = { + MAL_SAMPLE_RATE_48000, // Most common + MAL_SAMPLE_RATE_44100, + + MAL_SAMPLE_RATE_32000, // Lows + MAL_SAMPLE_RATE_24000, + MAL_SAMPLE_RATE_22050, + + MAL_SAMPLE_RATE_88200, // Highs + MAL_SAMPLE_RATE_96000, + MAL_SAMPLE_RATE_176400, + MAL_SAMPLE_RATE_192000, + + MAL_SAMPLE_RATE_16000, // Extreme lows + MAL_SAMPLE_RATE_11025, + MAL_SAMPLE_RATE_8000, + + MAL_SAMPLE_RATE_352800, // Extreme highs + MAL_SAMPLE_RATE_384000 +}; + +mal_format g_malFormatPriorities[] = { + mal_format_s16, // Most common + mal_format_f32, + + //mal_format_s24_32, // Clean alignment + mal_format_s32, + + mal_format_s24, // Unclean alignment + + mal_format_u8 // Low quality +}; + + /////////////////////////////////////////////////////////////////////////////// // // Standard Library Stuff // /////////////////////////////////////////////////////////////////////////////// -#ifndef mal_zero_memory +#ifndef MAL_MALLOC #ifdef MAL_WIN32 -#define mal_zero_memory(p, sz) ZeroMemory((p), (sz)) +#define MAL_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz)) #else -#define mal_zero_memory(p, sz) memset((p), 0, (sz)) +#define MAL_MALLOC(sz) malloc((sz)) #endif #endif -#define mal_zero_object(p) mal_zero_memory((p), sizeof(*(p))) - -#ifndef mal_copy_memory +#ifndef MAL_REALLOC #ifdef MAL_WIN32 -#define mal_copy_memory(dst, src, sz) CopyMemory((dst), (src), (sz)) +#define MAL_REALLOC(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(size_t)(HeapFree(GetProcessHeap(), 0, (p)) & 0))) #else -#define mal_copy_memory(dst, src, sz) memcpy((dst), (src), (sz)) +#define MAL_REALLOC(p, sz) realloc((p), (sz)) #endif #endif -#ifndef mal_malloc +#ifndef MAL_FREE #ifdef MAL_WIN32 -#define mal_malloc(sz) HeapAlloc(GetProcessHeap(), 0, (sz)) +#define MAL_FREE(p) HeapFree(GetProcessHeap(), 0, (p)) #else -#define mal_malloc(sz) malloc((sz)) +#define MAL_FREE(p) free((p)) #endif #endif -#ifndef mal_realloc +#ifndef MAL_ZERO_MEMORY #ifdef MAL_WIN32 -#define mal_realloc(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(SIZE_T)(HeapFree(GetProcessHeap(), 0, (p)) & 0))) +#define MAL_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz)) #else -#define mal_realloc(p, sz) realloc((p), (sz)) +#define MAL_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif #endif -#ifndef mal_free +#ifndef MAL_COPY_MEMORY #ifdef MAL_WIN32 -#define mal_free(p) HeapFree(GetProcessHeap(), 0, (p)) +#define MAL_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz)) #else -#define mal_free(p) free((p)) +#define MAL_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif #endif -#ifndef mal_assert +#ifndef MAL_ASSERT #ifdef MAL_WIN32 -#define mal_assert(condition) assert(condition) +#define MAL_ASSERT(condition) assert(condition) #else -#define mal_assert(condition) assert(condition) +#define MAL_ASSERT(condition) assert(condition) #endif #endif -#define mal_countof(x) (sizeof(x) / sizeof(x[0])) -#define mal_max(x, y) (((x) > (y)) ? (x) : (y)) -#define mal_min(x, y) (((x) < (y)) ? (x) : (y)) +#define mal_zero_memory MAL_ZERO_MEMORY +#define mal_copy_memory MAL_COPY_MEMORY +#define mal_assert MAL_ASSERT + +#define mal_zero_object(p) mal_zero_memory((p), sizeof(*(p))) +#define mal_countof(x) (sizeof(x) / sizeof(x[0])) +#define mal_max(x, y) (((x) > (y)) ? (x) : (y)) +#define mal_min(x, y) (((x) < (y)) ? (x) : (y)) +#define mal_clamp(x, lo, hi) (mal_max(lo, mal_min(x, hi))) +#define mal_offset_ptr(p, offset) (((mal_uint8*)(p)) + (offset)) + +#define mal_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / mal_get_bytes_per_sample(format) / (channels)) -#define mal_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / mal_get_sample_size_in_bytes(format) / (channels)) -// Some of these string utility functions are unused on some platforms. -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4505) -#elif defined(__GNUC__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-function" -#endif // Return Values: // 0: Success // 22: EINVAL // 34: ERANGE // // Not using symbolic constants for errors because I want to avoid #including errno.h -static int mal_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) +int mal_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) { if (dst == 0) { return 22; @@ -1818,7 +3220,7 @@ static int mal_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) return 34; } -static int mal_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +int mal_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) { if (dst == 0) { return 22; @@ -1850,7 +3252,7 @@ static int mal_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size return 34; } -static int mal_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) +int mal_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) { if (dst == 0) { return 22; @@ -1890,7 +3292,7 @@ static int mal_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) return 0; } -static int mal_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) +int mal_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) { if (dst == NULL || dstSizeInBytes == 0) { return 22; @@ -1956,7 +3358,7 @@ static int mal_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) return 0; } -static int mal_strcmp(const char* str1, const char* str2) +int mal_strcmp(const char* str1, const char* str2) { if (str1 == str2) return 0; @@ -1979,15 +3381,10 @@ static int mal_strcmp(const char* str1, const char* str2) return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; } -#if defined(_MSC_VER) - #pragma warning(pop) -#elif defined(__GNUC__) - #pragma GCC diagnostic pop -#endif // Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 -static inline unsigned int mal_next_power_of_2(unsigned int x) +static MAL_INLINE unsigned int mal_next_power_of_2(unsigned int x) { x--; x |= x >> 1; @@ -2000,12 +3397,12 @@ static inline unsigned int mal_next_power_of_2(unsigned int x) return x; } -static inline unsigned int mal_prev_power_of_2(unsigned int x) +static MAL_INLINE unsigned int mal_prev_power_of_2(unsigned int x) { return mal_next_power_of_2(x) >> 1; } -static inline unsigned int mal_round_to_power_of_2(unsigned int x) +static MAL_INLINE unsigned int mal_round_to_power_of_2(unsigned int x) { unsigned int prev = mal_prev_power_of_2(x); unsigned int next = mal_next_power_of_2(x); @@ -2016,20 +3413,207 @@ static inline unsigned int mal_round_to_power_of_2(unsigned int x) } } +static MAL_INLINE unsigned int mal_count_set_bits(unsigned int x) +{ + unsigned int count = 0; + while (x != 0) { + if (x & 1) { + count += 1; + } + + x = x >> 1; + } + + return count; +} + // Clamps an f32 sample to -1..1 -static inline float mal_clip_f32(float x) +static MAL_INLINE float mal_clip_f32(float x) { if (x < -1) return -1; if (x > +1) return +1; return x; } -static inline float mal_mix_f32(float x, float y, float a) +static MAL_INLINE float mal_mix_f32(float x, float y, float a) { return x*(1-a) + y*a; } +static MAL_INLINE float mal_mix_f32_fast(float x, float y, float a) +{ + float r0 = (y - x); + float r1 = r0*a; + return x + r1; + //return x + (y - x)*a; +} + +#if defined(MAL_SUPPORT_SSE2) +static MAL_INLINE __m128 mal_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) +{ + return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +static MAL_INLINE __m256 mal_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) +{ + return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +static MAL_INLINE __m512 mal_mix_f32_fast__avx512(__m512 x, __m512 y, __m512 a) +{ + return _mm512_add_ps(x, _mm512_mul_ps(_mm512_sub_ps(y, x), a)); +} +#endif +#if defined(MAL_SUPPORT_NEON) +static MAL_INLINE float32x4_t mal_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) +{ + return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); +} +#endif + + +static MAL_INLINE double mal_mix_f64(double x, double y, double a) +{ + return x*(1-a) + y*a; +} +static MAL_INLINE double mal_mix_f64_fast(double x, double y, double a) +{ + return x + (y - x)*a; +} + +static MAL_INLINE float mal_scale_to_range_f32(float x, float lo, float hi) +{ + return lo + x*(hi-lo); +} + + + +// Random Number Generation +// +// mini_al uses the LCG random number generation algorithm. This is good enough for audio. +// +// Note that mini_al's LCG implementation uses global state which is _not_ thread-local. When this is called across +// multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough +// for mini_al's purposes. +#define MAL_LCG_M 4294967296 +#define MAL_LCG_A 1103515245 +#define MAL_LCG_C 12345 +static mal_int32 g_malLCG; + +void mal_seed(mal_int32 seed) +{ + g_malLCG = seed; +} + +mal_int32 mal_rand_s32() +{ + mal_int32 lcg = g_malLCG; + mal_int32 r = (MAL_LCG_A * lcg + MAL_LCG_C) % MAL_LCG_M; + g_malLCG = r; + return r; +} + +double mal_rand_f64() +{ + return (mal_rand_s32() + 0x80000000) / (double)0x7FFFFFFF; +} + +float mal_rand_f32() +{ + return (float)mal_rand_f64(); +} + +static MAL_INLINE float mal_rand_range_f32(float lo, float hi) +{ + return mal_scale_to_range_f32(mal_rand_f32(), lo, hi); +} + +static MAL_INLINE mal_int32 mal_rand_range_s32(mal_int32 lo, mal_int32 hi) +{ + double x = mal_rand_f64(); + return lo + (mal_int32)(x*(hi-lo)); +} + + +static MAL_INLINE float mal_dither_f32_rectangle(float ditherMin, float ditherMax) +{ + return mal_rand_range_f32(ditherMin, ditherMax); +} + +static MAL_INLINE float mal_dither_f32_triangle(float ditherMin, float ditherMax) +{ + float a = mal_rand_range_f32(ditherMin, 0); + float b = mal_rand_range_f32(0, ditherMax); + return a + b; +} + +static MAL_INLINE float mal_dither_f32(mal_dither_mode ditherMode, float ditherMin, float ditherMax) +{ + if (ditherMode == mal_dither_mode_rectangle) { + return mal_dither_f32_rectangle(ditherMin, ditherMax); + } + if (ditherMode == mal_dither_mode_triangle) { + return mal_dither_f32_triangle(ditherMin, ditherMax); + } + + return 0; +} + +static MAL_INLINE mal_int32 mal_dither_s32(mal_dither_mode ditherMode, mal_int32 ditherMin, mal_int32 ditherMax) +{ + if (ditherMode == mal_dither_mode_rectangle) { + mal_int32 a = mal_rand_range_s32(ditherMin, ditherMax); + return a; + } + if (ditherMode == mal_dither_mode_triangle) { + mal_int32 a = mal_rand_range_s32(ditherMin, 0); + mal_int32 b = mal_rand_range_s32(0, ditherMax); + return a + b; + } + + return 0; +} + + +// Splits a buffer into parts of equal length and of the given alignment. The returned size of the split buffers will be a +// multiple of the alignment. The alignment must be a power of 2. +void mal_split_buffer(void* pBuffer, size_t bufferSize, size_t splitCount, size_t alignment, void** ppBuffersOut, size_t* pSplitSizeOut) +{ + if (pSplitSizeOut) { + *pSplitSizeOut = 0; + } + + if (pBuffer == NULL || bufferSize == 0 || splitCount == 0) { + return; + } + + if (alignment == 0) { + alignment = 1; + } + + mal_uintptr pBufferUnaligned = (mal_uintptr)pBuffer; + mal_uintptr pBufferAligned = (pBufferUnaligned + (alignment-1)) & ~(alignment-1); + size_t unalignedBytes = (size_t)(pBufferAligned - pBufferUnaligned); + + size_t splitSize = 0; + if (bufferSize >= unalignedBytes) { + splitSize = (bufferSize - unalignedBytes) / splitCount; + splitSize = splitSize & ~(alignment-1); + } + + if (ppBuffersOut != NULL) { + for (size_t i = 0; i < splitCount; ++i) { + ppBuffersOut[i] = (mal_uint8*)(pBufferAligned + (splitSize*i)); + } + } + + if (pSplitSizeOut) { + *pSplitSizeOut = splitSize; + } +} /////////////////////////////////////////////////////////////////////////////// @@ -2059,13 +3643,214 @@ static inline float mal_mix_f32(float x, float y, float a) #endif +mal_uint32 mal_get_standard_sample_rate_priority_index(mal_uint32 sampleRate) // Lower = higher priority +{ + for (mal_uint32 i = 0; i < mal_countof(g_malStandardSampleRatePriorities); ++i) { + if (g_malStandardSampleRatePriorities[i] == sampleRate) { + return i; + } + } + + return (mal_uint32)-1; +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// DEVICE I/O +// ========== +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef MAL_NO_DEVICE_IO +// Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When +// using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing +// compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply +// disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am +// not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere. +//#define MAL_USE_RUNTIME_LINKING_FOR_PTHREAD + +// Disable run-time linking on certain backends. +#ifndef MAL_NO_RUNTIME_LINKING + #if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTEN) + #define MAL_NO_RUNTIME_LINKING + #endif +#endif + +// Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not +// certain unused functions and variables can be excluded from the build to avoid warnings. +#ifdef MAL_ENABLE_WASAPI + #define MAL_HAS_WASAPI // Every compiler should support WASAPI +#endif +#ifdef MAL_ENABLE_DSOUND + #define MAL_HAS_DSOUND // Every compiler should support DirectSound. +#endif +#ifdef MAL_ENABLE_WINMM + #define MAL_HAS_WINMM // Every compiler I'm aware of supports WinMM. +#endif +#ifdef MAL_ENABLE_ALSA + #define MAL_HAS_ALSA + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_ALSA + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_PULSEAUDIO + #define MAL_HAS_PULSEAUDIO // Development packages are unnecessary for PulseAudio. + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_PULSEAUDIO + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_JACK + #define MAL_HAS_JACK + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_JACK + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_COREAUDIO + #define MAL_HAS_COREAUDIO +#endif +#ifdef MAL_ENABLE_SNDIO + #define MAL_HAS_SNDIO +#endif +#ifdef MAL_ENABLE_AUDIO4 + #define MAL_HAS_AUDIO4 // When enabled, always assume audio(4) is available. +#endif +#ifdef MAL_ENABLE_OSS + #define MAL_HAS_OSS // OSS is the only supported backend for Unix and BSD, so it must be present else this library is useless. +#endif +#ifdef MAL_ENABLE_OPENSL + #define MAL_HAS_OPENSL // OpenSL is the only supported backend for Android. It must be present. +#endif +#ifdef MAL_ENABLE_OPENAL + #define MAL_HAS_OPENAL + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_OPENAL + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_SDL + #define MAL_HAS_SDL + + // SDL headers are necessary if using compile-time linking. + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #ifdef MAL_EMSCRIPTEN + #if !__has_include() + #undef MAL_HAS_SDL + #endif + #else + #if !__has_include() + #undef MAL_HAS_SDL + #endif + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_NULL + #define MAL_HAS_NULL // Everything supports the null backend. +#endif + +const mal_backend g_malDefaultBackends[] = { + mal_backend_wasapi, + mal_backend_dsound, + mal_backend_winmm, + mal_backend_coreaudio, + mal_backend_sndio, + mal_backend_audio4, + mal_backend_oss, + mal_backend_pulseaudio, + mal_backend_alsa, + mal_backend_jack, + mal_backend_opensl, + mal_backend_openal, + mal_backend_sdl, + mal_backend_null +}; + +const char* mal_get_backend_name(mal_backend backend) +{ + switch (backend) + { + case mal_backend_null: return "Null"; + case mal_backend_wasapi: return "WASAPI"; + case mal_backend_dsound: return "DirectSound"; + case mal_backend_winmm: return "WinMM"; + case mal_backend_alsa: return "ALSA"; + case mal_backend_pulseaudio: return "PulseAudio"; + case mal_backend_jack: return "JACK"; + case mal_backend_coreaudio: return "Core Audio"; + case mal_backend_sndio: return "sndio"; + case mal_backend_audio4: return "audio(4)"; + case mal_backend_oss: return "OSS"; + case mal_backend_opensl: return "OpenSL|ES"; + case mal_backend_openal: return "OpenAL"; + case mal_backend_sdl: return "SDL"; + default: return "Unknown"; + } +} + + + +#ifdef MAL_WIN32 + #define MAL_THREADCALL WINAPI + typedef unsigned long mal_thread_result; +#else + #define MAL_THREADCALL + typedef void* mal_thread_result; +#endif +typedef mal_thread_result (MAL_THREADCALL * mal_thread_entry_proc)(void* pData); + +#ifdef MAL_WIN32 +typedef HRESULT (WINAPI * MAL_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); +typedef void (WINAPI * MAL_PFN_CoUninitialize)(); +typedef HRESULT (WINAPI * MAL_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); +typedef void (WINAPI * MAL_PFN_CoTaskMemFree)(LPVOID pv); +typedef HRESULT (WINAPI * MAL_PFN_PropVariantClear)(PROPVARIANT *pvar); +typedef int (WINAPI * MAL_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax); + +typedef HWND (WINAPI * MAL_PFN_GetForegroundWindow)(); +typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)(); + +// Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. +typedef LONG (WINAPI * MAL_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult); +typedef LONG (WINAPI * MAL_PFN_RegCloseKey)(HKEY hKey); +typedef LONG (WINAPI * MAL_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); +#endif + + +#define MAL_STATE_UNINITIALIZED 0 +#define MAL_STATE_STOPPED 1 // The device's default state after initialization. +#define MAL_STATE_STARTED 2 // The worker thread is in it's main loop waiting for the driver to request or deliver audio data. +#define MAL_STATE_STARTING 3 // Transitioning from a stopped state to started. +#define MAL_STATE_STOPPING 4 // Transitioning from a started state to stopped. + +#define MAL_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" +#define MAL_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" + + /////////////////////////////////////////////////////////////////////////////// // // Timing // /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_WIN32 -static LARGE_INTEGER g_mal_TimerFrequency = {{0}}; +LARGE_INTEGER g_mal_TimerFrequency = {{0}}; void mal_timer_init(mal_timer* pTimer) { if (g_mal_TimerFrequency.QuadPart == 0) { @@ -2074,7 +3859,7 @@ void mal_timer_init(mal_timer* pTimer) LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - pTimer->counter = (mal_uint64)counter.QuadPart; + pTimer->counter = counter.QuadPart; } double mal_timer_get_time_in_seconds(mal_timer* pTimer) @@ -2084,31 +3869,37 @@ double mal_timer_get_time_in_seconds(mal_timer* pTimer) return 0; } - return (counter.QuadPart - pTimer->counter) / (double)g_mal_TimerFrequency.QuadPart; + return (double)(counter.QuadPart - pTimer->counter) / g_mal_TimerFrequency.QuadPart; } #elif defined(MAL_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) -static uint64_t g_mal_TimerFrequency = 0; +mal_uint64 g_mal_TimerFrequency = 0; void mal_timer_init(mal_timer* pTimer) { mach_timebase_info_data_t baseTime; mach_timebase_info(&baseTime); g_mal_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; - + pTimer->counter = mach_absolute_time(); } double mal_timer_get_time_in_seconds(mal_timer* pTimer) { - uint64_t newTimeCounter = mach_absolute_time(); - uint64_t oldTimeCounter = pTimer->counter; - + mal_uint64 newTimeCounter = mach_absolute_time(); + mal_uint64 oldTimeCounter = pTimer->counter; + return (newTimeCounter - oldTimeCounter) / g_mal_TimerFrequency; } #else +#if defined(CLOCK_MONOTONIC) + #define MAL_CLOCK_ID CLOCK_MONOTONIC +#else + #define MAL_CLOCK_ID CLOCK_REALTIME +#endif + void mal_timer_init(mal_timer* pTimer) { struct timespec newTime; - clock_gettime(CLOCK_MONOTONIC, &newTime); + clock_gettime(MAL_CLOCK_ID, &newTime); pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; } @@ -2116,10 +3907,10 @@ void mal_timer_init(mal_timer* pTimer) double mal_timer_get_time_in_seconds(mal_timer* pTimer) { struct timespec newTime; - clock_gettime(CLOCK_MONOTONIC, &newTime); + clock_gettime(MAL_CLOCK_ID, &newTime); - uint64_t newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - uint64_t oldTimeCounter = pTimer->counter; + mal_uint64 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; + mal_uint64 oldTimeCounter = pTimer->counter; return (newTimeCounter - oldTimeCounter) / 1000000000.0; } @@ -2175,15 +3966,29 @@ mal_proc mal_dlsym(mal_handle handle, const char* symbol) // /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_WIN32 +int mal_thread_priority_to_win32(mal_thread_priority priority) +{ + switch (priority) { + case mal_thread_priority_idle: return THREAD_PRIORITY_IDLE; + case mal_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; + case mal_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; + case mal_thread_priority_normal: return THREAD_PRIORITY_NORMAL; + case mal_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; + case mal_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; + case mal_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; + default: return mal_thread_priority_normal; + } +} + mal_result mal_thread_create__win32(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) { - (void)pContext; - pThread->win32.hThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL); if (pThread->win32.hThread == NULL) { return MAL_FAILED_TO_CREATE_THREAD; } + SetThreadPriority((HANDLE)pThread->win32.hThread, mal_thread_priority_to_win32(pContext->config.threadPriority)); + return MAL_SUCCESS; } @@ -2256,6 +4061,8 @@ mal_bool32 mal_event_signal__win32(mal_event* pEvent) #ifdef MAL_POSIX +#include + typedef int (* mal_pthread_create_proc)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); typedef int (* mal_pthread_join_proc)(pthread_t thread, void **retval); typedef int (* mal_pthread_mutex_init_proc)(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr); @@ -2266,10 +4073,71 @@ typedef int (* mal_pthread_cond_init_proc)(pthread_cond_t *__restrict __cond, co typedef int (* mal_pthread_cond_destroy_proc)(pthread_cond_t *__cond); typedef int (* mal_pthread_cond_signal_proc)(pthread_cond_t *__cond); typedef int (* mal_pthread_cond_wait_proc)(pthread_cond_t *__restrict __cond, pthread_mutex_t *__restrict __mutex); +typedef int (* mal_pthread_attr_init_proc)(pthread_attr_t *attr); +typedef int (* mal_pthread_attr_destroy_proc)(pthread_attr_t *attr); +typedef int (* mal_pthread_attr_setschedpolicy_proc)(pthread_attr_t *attr, int policy); +typedef int (* mal_pthread_attr_getschedparam_proc)(const pthread_attr_t *attr, struct sched_param *param); +typedef int (* mal_pthread_attr_setschedparam_proc)(pthread_attr_t *attr, const struct sched_param *param); mal_bool32 mal_thread_create__posix(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) { - int result = ((mal_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, NULL, entryProc, pData); + pthread_attr_t* pAttr = NULL; + +#if !defined(__EMSCRIPTEN__) + // Try setting the thread priority. It's not critical if anything fails here. + pthread_attr_t attr; + if (((mal_pthread_attr_init_proc)pContext->posix.pthread_attr_init)(&attr) == 0) { + int scheduler = -1; + if (pContext->config.threadPriority == mal_thread_priority_idle) { +#ifdef SCHED_IDLE + if (((mal_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_IDLE) == 0) { + scheduler = SCHED_IDLE; + } +#endif + } else if (pContext->config.threadPriority == mal_thread_priority_realtime) { +#ifdef SCHED_FIFO + if (((mal_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_FIFO) == 0) { + scheduler = SCHED_FIFO; + } +#endif +#ifdef MAL_LINUX + } else { + scheduler = sched_getscheduler(0); +#endif + } + + if (scheduler != -1) { + int priorityMin = sched_get_priority_min(scheduler); + int priorityMax = sched_get_priority_max(scheduler); + int priorityStep = (priorityMax - priorityMin) / 7; // 7 = number of priorities supported by mini_al. + + struct sched_param sched; + if (((mal_pthread_attr_getschedparam_proc)pContext->posix.pthread_attr_getschedparam)(&attr, &sched) == 0) { + if (pContext->config.threadPriority == mal_thread_priority_idle) { + sched.sched_priority = priorityMin; + } else if (pContext->config.threadPriority == mal_thread_priority_realtime) { + sched.sched_priority = priorityMax; + } else { + sched.sched_priority += ((int)pContext->config.threadPriority + 5) * priorityStep; // +5 because the lowest priority is -5. + if (sched.sched_priority < priorityMin) { + sched.sched_priority = priorityMin; + } + if (sched.sched_priority > priorityMax) { + sched.sched_priority = priorityMax; + } + } + + if (((mal_pthread_attr_setschedparam_proc)pContext->posix.pthread_attr_setschedparam)(&attr, &sched) == 0) { + pAttr = &attr; + } + } + } + + ((mal_pthread_attr_destroy_proc)pContext->posix.pthread_attr_destroy)(&attr); + } +#endif + + int result = ((mal_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, pAttr, entryProc, pData); if (result != 0) { return MAL_FAILED_TO_CREATE_THREAD; } @@ -2401,7 +4269,9 @@ void mal_sleep(mal_uint32 milliseconds) mal_result mal_mutex_init(mal_context* pContext, mal_mutex* pMutex) { - if (pContext == NULL || pMutex == NULL) return MAL_INVALID_ARGS; + if (pContext == NULL || pMutex == NULL) { + return MAL_INVALID_ARGS; + } pMutex->pContext = pContext; @@ -2501,19 +4371,139 @@ mal_bool32 mal_event_signal(mal_event* pEvent) } -// Posts a log message. -static void mal_log(mal_context* pContext, mal_device* pDevice, const char* message) +mal_uint32 mal_get_best_sample_rate_within_range(mal_uint32 sampleRateMin, mal_uint32 sampleRateMax) { - if (pContext == NULL) return; + // Normalize the range in case we were given something stupid. + if (sampleRateMin < MAL_MIN_SAMPLE_RATE) { + sampleRateMin = MAL_MIN_SAMPLE_RATE; + } + if (sampleRateMax > MAL_MAX_SAMPLE_RATE) { + sampleRateMax = MAL_MAX_SAMPLE_RATE; + } + if (sampleRateMin > sampleRateMax) { + sampleRateMin = sampleRateMax; + } - mal_log_proc onLog = pContext->config.onLog; - if (onLog) { - onLog(pContext, pDevice, message); + if (sampleRateMin == sampleRateMax) { + return sampleRateMax; + } else { + for (size_t iStandardRate = 0; iStandardRate < mal_countof(g_malStandardSampleRatePriorities); ++iStandardRate) { + mal_uint32 standardRate = g_malStandardSampleRatePriorities[iStandardRate]; + if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { + return standardRate; + } + } + } + + // Should never get here. + mal_assert(MAL_FALSE); + return 0; +} + +mal_uint32 mal_get_closest_standard_sample_rate(mal_uint32 sampleRateIn) +{ + mal_uint32 closestRate = 0; + mal_uint32 closestDiff = 0xFFFFFFFF; + + for (size_t iStandardRate = 0; iStandardRate < mal_countof(g_malStandardSampleRatePriorities); ++iStandardRate) { + mal_uint32 standardRate = g_malStandardSampleRatePriorities[iStandardRate]; + + mal_uint32 diff; + if (sampleRateIn > standardRate) { + diff = sampleRateIn - standardRate; + } else { + diff = standardRate - sampleRateIn; + } + + if (diff == 0) { + return standardRate; // The input sample rate is a standard rate. + } + + if (closestDiff > diff) { + closestDiff = diff; + closestRate = standardRate; + } + } + + return closestRate; +} + + +mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale) +{ + return mal_max(1, (mal_uint32)(baseBufferSize*scale)); +} + +mal_uint32 mal_calculate_buffer_size_in_milliseconds_from_frames(mal_uint32 bufferSizeInFrames, mal_uint32 sampleRate) +{ + return bufferSizeInFrames / (sampleRate/1000); +} + +mal_uint32 mal_calculate_buffer_size_in_frames_from_milliseconds(mal_uint32 bufferSizeInMilliseconds, mal_uint32 sampleRate) +{ + return bufferSizeInMilliseconds * (sampleRate/1000); +} + +mal_uint32 mal_get_default_buffer_size_in_milliseconds(mal_performance_profile performanceProfile) +{ + if (performanceProfile == mal_performance_profile_low_latency) { + return MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY; + } else { + return MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE; } } +mal_uint32 mal_get_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate) +{ + mal_uint32 bufferSizeInMilliseconds = mal_get_default_buffer_size_in_milliseconds(performanceProfile); + if (bufferSizeInMilliseconds == 0) { + bufferSizeInMilliseconds = 1; + } + + mal_uint32 sampleRateMS = (sampleRate/1000); + if (sampleRateMS == 0) { + sampleRateMS = 1; + } + + return bufferSizeInMilliseconds * sampleRateMS; +} + + +const char* mal_log_level_to_string(mal_uint32 logLevel) +{ + switch (logLevel) + { + case MAL_LOG_LEVEL_VERBOSE: return ""; + case MAL_LOG_LEVEL_INFO: return "INFO"; + case MAL_LOG_LEVEL_WARNING: return "WARNING"; + case MAL_LOG_LEVEL_ERROR: return "ERROR"; + default: return "ERROR"; + } +} + +// Posts a log message. +void mal_log(mal_context* pContext, mal_device* pDevice, mal_uint32 logLevel, const char* message) +{ + if (pContext == NULL) return; + +#if defined(MAL_LOG_LEVEL) + if (logLevel <= MAL_LOG_LEVEL) { + #if defined(MAL_DEBUG_OUTPUT) + if (logLevel <= MAL_LOG_LEVEL) { + printf("%s: %s", mal_log_level_to_string(logLevel), message); + } + #endif + + mal_log_proc onLog = pContext->config.onLog; + if (onLog) { + onLog(pContext, pDevice, message); + } + } +#endif +} + // Posts an error. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". -static mal_result mal_context_post_error(mal_context* pContext, mal_device* pDevice, const char* message, mal_result resultCode) +mal_result mal_context_post_error(mal_context* pContext, mal_device* pDevice, mal_uint32 logLevel, const char* message, mal_result resultCode) { // Derive the context from the device if necessary. if (pContext == NULL) { @@ -2522,89 +4512,18 @@ static mal_result mal_context_post_error(mal_context* pContext, mal_device* pDev } } - mal_log(pContext, pDevice, message); + mal_log(pContext, pDevice, logLevel, message); return resultCode; } -static mal_result mal_post_error(mal_device* pDevice, const char* message, mal_result resultCode) +mal_result mal_post_error(mal_device* pDevice, mal_uint32 logLevel, const char* message, mal_result resultCode) { - return mal_context_post_error(NULL, pDevice, message, resultCode); + return mal_context_post_error(NULL, pDevice, logLevel, message, resultCode); } -#if !defined(MAL_ANDROID) -static void mal_get_default_channel_mapping(mal_backend backend, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) -{ - if (channels == 1) { // Mono - channelMap[0] = MAL_CHANNEL_FRONT_CENTER; - } else if (channels == 2) { // Stereo - channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - } else if (channels == 3) { // 2.1 - channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - channelMap[2] = MAL_CHANNEL_LFE; - } else if (channels == 4) { // 4.0 - channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - channelMap[2] = MAL_CHANNEL_SIDE_LEFT; - channelMap[3] = MAL_CHANNEL_SIDE_RIGHT; - } else if (channels == 5) { // Not sure about this one. 4.1? - channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - channelMap[2] = MAL_CHANNEL_SIDE_LEFT; - channelMap[3] = MAL_CHANNEL_SIDE_RIGHT; - channelMap[4] = MAL_CHANNEL_LFE; - } else if (channels >= 6) { // 5.1 - // Some backends use different default layouts. - if (backend == mal_backend_wasapi || backend == mal_backend_dsound || backend == mal_backend_winmm || backend == mal_backend_oss) { - channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - channelMap[2] = MAL_CHANNEL_FRONT_CENTER; - channelMap[3] = MAL_CHANNEL_LFE; - channelMap[4] = MAL_CHANNEL_SIDE_LEFT; - channelMap[5] = MAL_CHANNEL_SIDE_RIGHT; - } else { - channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - channelMap[2] = MAL_CHANNEL_SIDE_LEFT; - channelMap[3] = MAL_CHANNEL_SIDE_RIGHT; - channelMap[4] = MAL_CHANNEL_FRONT_CENTER; - channelMap[5] = MAL_CHANNEL_LFE; - } - - if (channels == 7) { // Not sure about this one. - channelMap[6] = MAL_CHANNEL_BACK_CENTER; - } else { - // I don't know what mapping to use in this case, but I'm making it upwards compatible with 7.1. Good luck! - mal_assert(channels >= 8); - channelMap[6] = MAL_CHANNEL_BACK_LEFT; - channelMap[7] = MAL_CHANNEL_BACK_RIGHT; - - // Beyond 7.1 I'm just guessing... - if (channels == 9) { - channelMap[8] = MAL_CHANNEL_BACK_CENTER; - } else if (channels == 10) { - channelMap[8] = MAL_CHANNEL_FRONT_LEFT_CENTER; - channelMap[9] = MAL_CHANNEL_FRONT_RIGHT_CENTER; - } else if (channels == 11) { - channelMap[ 8] = MAL_CHANNEL_FRONT_LEFT_CENTER; - channelMap[ 9] = MAL_CHANNEL_FRONT_RIGHT_CENTER; - channelMap[10] = MAL_CHANNEL_BACK_CENTER; - } else { - mal_assert(channels >= 12); - for (mal_uint8 iChannel = 11; iChannel < channels && iChannel < MAL_MAX_CHANNELS; ++iChannel) { - channelMap[iChannel] = iChannel + 1; - } - } - } - } -} -#endif - - // The callback for reading from the client -> DSP -> device. -static inline mal_uint32 mal_device__on_read_from_client(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +mal_uint32 mal_device__on_read_from_client(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { (void)pDSP; @@ -2620,7 +4539,7 @@ static inline mal_uint32 mal_device__on_read_from_client(mal_dsp* pDSP, mal_uint } // The callback for reading from the device -> DSP -> client. -static inline mal_uint32 mal_device__on_read_from_device(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +mal_uint32 mal_device__on_read_from_device(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { (void)pDSP; @@ -2636,7 +4555,7 @@ static inline mal_uint32 mal_device__on_read_from_device(mal_dsp* pDSP, mal_uint framesToRead = pDevice->_dspFrameCount; } - mal_uint32 bytesToRead = framesToRead * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + mal_uint32 bytesToRead = framesToRead * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); mal_copy_memory(pFramesOut, pDevice->_dspFrames, bytesToRead); pDevice->_dspFrameCount -= framesToRead; pDevice->_dspFrames += bytesToRead; @@ -2646,16 +4565,16 @@ static inline mal_uint32 mal_device__on_read_from_device(mal_dsp* pDSP, mal_uint // A helper function for reading sample data from the client. Returns the number of samples read from the client. Remaining samples // are filled with silence. -static inline mal_uint32 mal_device__read_frames_from_client(mal_device* pDevice, mal_uint32 frameCount, void* pSamples) +static MAL_INLINE mal_uint32 mal_device__read_frames_from_client(mal_device* pDevice, mal_uint32 frameCount, void* pSamples) { mal_assert(pDevice != NULL); mal_assert(frameCount > 0); mal_assert(pSamples != NULL); - mal_uint32 framesRead = mal_dsp_read_frames(&pDevice->dsp, frameCount, pSamples); - mal_uint32 samplesRead = framesRead * pDevice->internalChannels; - mal_uint32 sampleSize = mal_get_sample_size_in_bytes(pDevice->internalFormat); - mal_uint32 consumedBytes = samplesRead*sampleSize; + mal_uint32 framesRead = (mal_uint32)mal_dsp_read(&pDevice->dsp, frameCount, pSamples, pDevice->dsp.pUserData); + mal_uint32 samplesRead = framesRead * pDevice->internalChannels; + mal_uint32 sampleSize = mal_get_bytes_per_sample(pDevice->internalFormat); + mal_uint32 consumedBytes = samplesRead*sampleSize; mal_uint32 remainingBytes = ((frameCount * pDevice->internalChannels) - samplesRead)*sampleSize; mal_zero_memory((mal_uint8*)pSamples + consumedBytes, remainingBytes); @@ -2663,7 +4582,7 @@ static inline mal_uint32 mal_device__read_frames_from_client(mal_device* pDevice } // A helper for sending sample data to the client. -static inline void mal_device__send_frames_to_client(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples) +static MAL_INLINE void mal_device__send_frames_to_client(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples) { mal_assert(pDevice != NULL); mal_assert(frameCount > 0); @@ -2675,10 +4594,10 @@ static inline void mal_device__send_frames_to_client(mal_device* pDevice, mal_ui pDevice->_dspFrames = (const mal_uint8*)pSamples; mal_uint8 chunkBuffer[4096]; - mal_uint32 chunkFrameCount = sizeof(chunkBuffer) / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + mal_uint32 chunkFrameCount = sizeof(chunkBuffer) / mal_get_bytes_per_frame(pDevice->format, pDevice->channels); for (;;) { - mal_uint32 framesJustRead = mal_dsp_read_frames(&pDevice->dsp, chunkFrameCount, chunkBuffer); + mal_uint32 framesJustRead = (mal_uint32)mal_dsp_read(&pDevice->dsp, chunkFrameCount, chunkBuffer, pDevice->dsp.pUserData); if (framesJustRead == 0) { break; } @@ -2693,32 +4612,73 @@ static inline void mal_device__send_frames_to_client(mal_device* pDevice, mal_ui } // A helper for changing the state of the device. -static inline void mal_device__set_state(mal_device* pDevice, mal_uint32 newState) +static MAL_INLINE void mal_device__set_state(mal_device* pDevice, mal_uint32 newState) { mal_atomic_exchange_32(&pDevice->state, newState); } // A helper for getting the state of the device. -static inline mal_uint32 mal_device__get_state(mal_device* pDevice) +static MAL_INLINE mal_uint32 mal_device__get_state(mal_device* pDevice) { return pDevice->state; } #ifdef MAL_WIN32 - #if defined(MAL_HAS_WASAPI) || defined(MAL_HAS_DSOUND) - static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - //static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - //static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - #endif + GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + //GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + //GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; #endif +mal_bool32 mal_context__device_id_equal(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + + if (pID0 == pID1) return MAL_TRUE; + + if ((pID0 == NULL && pID1 != NULL) || + (pID0 != NULL && pID1 == NULL)) { + return MAL_FALSE; + } + + if (pContext->onDeviceIDEqual) { + return pContext->onDeviceIDEqual(pContext, pID0, pID1); + } + + return MAL_FALSE; +} + + +typedef struct +{ + mal_device_type deviceType; + const mal_device_id* pDeviceID; + char* pName; + size_t nameBufferSize; + mal_bool32 foundDevice; +} mal_context__try_get_device_name_by_id__enum_callback_data; + +mal_bool32 mal_context__try_get_device_name_by_id__enum_callback(mal_context* pContext, mal_device_type deviceType, const mal_device_info* pDeviceInfo, void* pUserData) +{ + mal_context__try_get_device_name_by_id__enum_callback_data* pData = (mal_context__try_get_device_name_by_id__enum_callback_data*)pUserData; + mal_assert(pData != NULL); + + if (pData->deviceType == deviceType) { + if (pContext->onDeviceIDEqual(pContext, pData->pDeviceID, &pDeviceInfo->id)) { + mal_strncpy_s(pData->pName, pData->nameBufferSize, pDeviceInfo->name, (size_t)-1); + pData->foundDevice = MAL_TRUE; + } + } + + return !pData->foundDevice; +} + // Generic function for retrieving the name of a device by it's ID. // // This function simply enumerates every device and then retrieves the name of the first device that has the same ID. -static mal_result mal_context__try_get_device_name_by_id(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, char* pName, size_t nameBufferSize) +mal_result mal_context__try_get_device_name_by_id(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, char* pName, size_t nameBufferSize) { mal_assert(pContext != NULL); mal_assert(pName != NULL); @@ -2727,192 +4687,142 @@ static mal_result mal_context__try_get_device_name_by_id(mal_context* pContext, return MAL_NO_DEVICE; } - mal_uint32 deviceCount; - mal_result result = mal_enumerate_devices(pContext, type, &deviceCount, NULL); + mal_context__try_get_device_name_by_id__enum_callback_data data; + data.deviceType = type; + data.pDeviceID = pDeviceID; + data.pName = pName; + data.nameBufferSize = nameBufferSize; + data.foundDevice = MAL_FALSE; + mal_result result = mal_context_enumerate_devices(pContext, mal_context__try_get_device_name_by_id__enum_callback, &data); if (result != MAL_SUCCESS) { return result; } - mal_device_info* pInfos = (mal_device_info*)mal_malloc(sizeof(*pInfos) * deviceCount); - if (pInfos == NULL) { - return MAL_OUT_OF_MEMORY; + if (!data.foundDevice) { + return MAL_NO_DEVICE; + } else { + return MAL_SUCCESS; } - - result = mal_enumerate_devices(pContext, type, &deviceCount, pInfos); - if (result != MAL_SUCCESS) { - mal_free(pInfos); - return result; - } - - mal_bool32 found = MAL_FALSE; - for (mal_uint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - // Prefer backend specific comparisons for efficiency and accuracy, but fall back to a generic method if a backend-specific comparison - // is not implemented. - switch (pContext->backend) - { - #ifdef MAL_HAS_WASAPI - case mal_backend_wasapi: - { - if (memcmp(pDeviceID->wasapi, &pInfos[iDevice].id.wasapi, sizeof(pDeviceID->wasapi)) == 0) { - found = MAL_TRUE; - } - } break; - #endif - #ifdef MAL_HAS_DSOUND - case mal_backend_dsound: - { - if (memcmp(pDeviceID->dsound, &pInfos[iDevice].id.dsound, sizeof(pDeviceID->dsound)) == 0) { - found = MAL_TRUE; - } - } break; - #endif - #ifdef MAL_HAS_WINMM - case mal_backend_winmm: - { - if (pInfos[iDevice].id.winmm == pDeviceID->winmm) { - found = MAL_TRUE; - } - } break; - #endif - #ifdef MAL_HAS_ALSA - case mal_backend_alsa: - { - if (mal_strcmp(pInfos[iDevice].id.alsa, pDeviceID->alsa) == 0) { - found = MAL_TRUE; - } - } break; - #endif - #ifdef MAL_HAS_COREAUDIO - //case mal_backend_coreaudio: - //{ - // // TODO: Implement me. - //} break; - #endif - #ifdef MAL_HAS_OSS - case mal_backend_oss: - { - if (mal_strcmp(pInfos[iDevice].id.oss, pDeviceID->oss) == 0) { - found = MAL_TRUE; - } - } break; - #endif - #ifdef MAL_HAS_OPENSL - case mal_backend_opensl: - { - if (pInfos[iDevice].id.opensl == pDeviceID->opensl) { - found = MAL_TRUE; - } - } break; - #endif - #ifdef MAL_HAS_OPENAL - case mal_backend_openal: - { - if (mal_strcmp(pInfos[iDevice].id.openal, pDeviceID->openal) == 0) { - found = MAL_TRUE; - } - } break; - #endif - #ifdef MAL_HAS_SDL - case mal_backend_sdl: - { - if (pInfos[iDevice].id.sdl == pDeviceID->sdl) { - found = MAL_TRUE; - } - } break; - #endif - #ifdef MAL_HAS_NULL - case mal_backend_null: - { - if (pInfos[iDevice].id.nullbackend == pDeviceID->nullbackend) { - found = MAL_TRUE; - } - } break; - #endif - - // Fall back to a generic memory comparison. - default: - { - if (memcmp(pDeviceID, &pInfos[iDevice].id, sizeof(*pDeviceID)) == 0) { - found = MAL_TRUE; - } - } break; - } - - if (found) { - mal_strncpy_s(pName, nameBufferSize, pInfos[iDevice].name, (size_t)-1); - result = MAL_SUCCESS; - break; - } - } - - mal_free(pInfos); - return result; } +mal_uint32 mal_get_format_priority_index(mal_format format) // Lower = better. +{ + for (mal_uint32 i = 0; i < mal_countof(g_malFormatPriorities); ++i) { + if (g_malFormatPriorities[i] == format) { + return i; + } + } + + // Getting here means the format could not be found or is equal to mal_format_unknown. + return (mal_uint32)-1; +} + +void mal_device__post_init_setup(mal_device* pDevice); + /////////////////////////////////////////////////////////////////////////////// // // Null Backend // /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_HAS_NULL -mal_result mal_context_init__null(mal_context* pContext) + +mal_bool32 mal_context_is_device_id_equal__null(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) { mal_assert(pContext != NULL); - - // The null backend always works. + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); (void)pContext; - return MAL_SUCCESS; + + return pID0->nullbackend == pID1->nullbackend; } -mal_result mal_context_uninit__null(mal_context* pContext) +mal_result mal_context_enumerate_devices__null(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) { mal_assert(pContext != NULL); - mal_assert(pContext->backend == mal_backend_null); + mal_assert(callback != NULL); - (void)pContext; - return MAL_SUCCESS; -} + mal_bool32 cbResult = MAL_TRUE; -static mal_result mal_enumerate_devices__null(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) -{ - (void)pContext; + // Playback. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); + cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } - mal_uint32 infoSize = *pCount; - *pCount = 1; // There's only one "device" each for playback and recording for the null backend. - - if (pInfo != NULL && infoSize > 0) { - mal_zero_object(pInfo); - - if (type == mal_device_type_playback) { - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "NULL Playback Device", (size_t)-1); - } else { - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "NULL Capture Device", (size_t)-1); - } + // Capture. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); + cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); } return MAL_SUCCESS; } -static void mal_device_uninit__null(mal_device* pDevice) +mal_result mal_context_get_device_info__null(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + + (void)pContext; + (void)shareMode; + + if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { + return MAL_NO_DEVICE; // Don't know the device. + } + + // Name / Description + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); + } + + // Support everything on the null backend. + pDeviceInfo->formatCount = mal_format_count - 1; // Minus one because we don't want to include mal_format_unknown. + for (mal_uint32 iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) { + pDeviceInfo->formats[iFormat] = (mal_format)(iFormat + 1); // +1 to skip over mal_format_unknown. + } + + pDeviceInfo->minChannels = 1; + pDeviceInfo->maxChannels = MAL_MAX_CHANNELS; + pDeviceInfo->minSampleRate = MAL_SAMPLE_RATE_8000; + pDeviceInfo->maxSampleRate = MAL_SAMPLE_RATE_384000; + + return MAL_SUCCESS; +} + + +void mal_device_uninit__null(mal_device* pDevice) { mal_assert(pDevice != NULL); mal_free(pDevice->null_device.pBuffer); } -static mal_result mal_device_init__null(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +mal_result mal_device_init__null(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) { (void)pContext; (void)type; (void)pDeviceID; + (void)pConfig; mal_assert(pDevice != NULL); mal_zero_object(&pDevice->null_device); - pDevice->bufferSizeInFrames = pConfig->bufferSizeInFrames; - pDevice->periods = pConfig->periods; + if (type == mal_device_type_playback) { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "NULL Playback Device", (size_t)-1); + } else { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "NULL Capture Device", (size_t)-1); + } - pDevice->null_device.pBuffer = (mal_uint8*)mal_malloc(pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format)); + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate); + } + + pDevice->null_device.pBuffer = (mal_uint8*)mal_malloc(pDevice->bufferSizeInFrames * pDevice->channels * mal_get_bytes_per_sample(pDevice->format)); if (pDevice->null_device.pBuffer == NULL) { return MAL_OUT_OF_MEMORY; } @@ -2922,7 +4832,7 @@ static mal_result mal_device_init__null(mal_context* pContext, mal_device_type t return MAL_SUCCESS; } -static mal_result mal_device__start_backend__null(mal_device* pDevice) +mal_result mal_device__start_backend__null(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -2932,7 +4842,7 @@ static mal_result mal_device__start_backend__null(mal_device* pDevice) return MAL_SUCCESS; } -static mal_result mal_device__stop_backend__null(mal_device* pDevice) +mal_result mal_device__stop_backend__null(mal_device* pDevice) { mal_assert(pDevice != NULL); (void)pDevice; @@ -2940,7 +4850,7 @@ static mal_result mal_device__stop_backend__null(mal_device* pDevice) return MAL_SUCCESS; } -static mal_result mal_device__break_main_loop__null(mal_device* pDevice) +mal_result mal_device__break_main_loop__null(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -2948,7 +4858,7 @@ static mal_result mal_device__break_main_loop__null(mal_device* pDevice) return MAL_SUCCESS; } -static mal_bool32 mal_device__get_current_frame__null(mal_device* pDevice, mal_uint32* pCurrentPos) +mal_bool32 mal_device__get_current_frame__null(mal_device* pDevice, mal_uint32* pCurrentPos) { mal_assert(pDevice != NULL); mal_assert(pCurrentPos != NULL); @@ -2960,7 +4870,7 @@ static mal_bool32 mal_device__get_current_frame__null(mal_device* pDevice, mal_u return MAL_TRUE; } -static mal_uint32 mal_device__get_available_frames__null(mal_device* pDevice) +mal_uint32 mal_device__get_available_frames__null(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -3001,7 +4911,7 @@ static mal_uint32 mal_device__get_available_frames__null(mal_device* pDevice) } } -static mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice) +mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -3018,7 +4928,7 @@ static mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice) return mal_device__get_available_frames__null(pDevice); } -static mal_result mal_device__main_loop__null(mal_device* pDevice) +mal_result mal_device__main_loop__null(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -3039,8 +4949,8 @@ static mal_result mal_device__main_loop__null(mal_device* pDevice) } mal_uint32 sampleCount = framesAvailable * pDevice->channels; - mal_uint32 lockOffset = pDevice->null_device.lastProcessedFrame * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); - mal_uint32 lockSize = sampleCount * mal_get_sample_size_in_bytes(pDevice->format); + mal_uint32 lockOffset = pDevice->null_device.lastProcessedFrame * pDevice->channels * mal_get_bytes_per_sample(pDevice->format); + mal_uint32 lockSize = sampleCount * mal_get_bytes_per_sample(pDevice->format); if (pDevice->type == mal_device_type_playback) { if (pDevice->null_device.breakFromMainLoop) { @@ -3058,6 +4968,35 @@ static mal_result mal_device__main_loop__null(mal_device* pDevice) return MAL_SUCCESS; } + + +mal_result mal_context_uninit__null(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_null); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_init__null(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + pContext->onUninit = mal_context_uninit__null; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__null; + pContext->onEnumDevices = mal_context_enumerate_devices__null; + pContext->onGetDeviceInfo = mal_context_get_device_info__null; + pContext->onDeviceInit = mal_device_init__null; + pContext->onDeviceUninit = mal_device_uninit__null; + pContext->onDeviceStart = mal_device__start_backend__null; + pContext->onDeviceStop = mal_device__stop_backend__null; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__null; + pContext->onDeviceMainLoop = mal_device__main_loop__null; + + // The null backend always works. + return MAL_SUCCESS; +} #endif @@ -3081,10 +5020,31 @@ static mal_result mal_device__main_loop__null(mal_device* pDevice) #define mal_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) #define mal_PropVariantClear(pContext, pvar) PropVariantClear(pvar) #endif + +// There's a few common headers for Win32 backends which include here for simplicity. Note that we should never +// include any files that do not come standard with modern compilers, and we may need to manually define a few +// symbols. +#include +#include + +#if !defined(MAXULONG_PTR) +typedef size_t DWORD_PTR; #endif -#if defined(MAL_HAS_WASAPI) || defined(MAL_HAS_DSOUND) -#include +#if !defined(WAVE_FORMAT_44M08) +#define WAVE_FORMAT_44M08 0x00000100 +#define WAVE_FORMAT_44S08 0x00000200 +#define WAVE_FORMAT_44M16 0x00000400 +#define WAVE_FORMAT_44S16 0x00000800 +#define WAVE_FORMAT_48M08 0x00001000 +#define WAVE_FORMAT_48S08 0x00002000 +#define WAVE_FORMAT_48M16 0x00004000 +#define WAVE_FORMAT_48S16 0x00008000 +#define WAVE_FORMAT_96M08 0x00010000 +#define WAVE_FORMAT_96S08 0x00020000 +#define WAVE_FORMAT_96M16 0x00040000 +#define WAVE_FORMAT_96S16 0x00080000 +#endif #ifndef SPEAKER_FRONT_LEFT #define SPEAKER_FRONT_LEFT 0x1 @@ -3109,7 +5069,7 @@ static mal_result mal_device__main_loop__null(mal_device* pDevice) // The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We // define our own implementation in this case. -#if defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_) +#if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__) typedef struct { WAVEFORMATEX Format; @@ -3125,11 +5085,17 @@ typedef struct #endif #ifndef WAVE_FORMAT_EXTENSIBLE -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE #endif +#ifndef WAVE_FORMAT_IEEE_FLOAT +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 +#endif + +GUID MAL_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + // Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to mini_al. -static mal_uint8 mal_channel_id_to_mal__win32(DWORD id) +mal_uint8 mal_channel_id_to_mal__win32(DWORD id) { switch (id) { @@ -3156,10 +5122,11 @@ static mal_uint8 mal_channel_id_to_mal__win32(DWORD id) } // Converts an individual mini_al channel identifier (MAL_CHANNEL_FRONT_LEFT, etc.) to Win32-style. -static DWORD mal_channel_id_to_win32(DWORD id) +DWORD mal_channel_id_to_win32(DWORD id) { switch (id) { + case MAL_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; case MAL_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; case MAL_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; case MAL_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; @@ -3183,7 +5150,7 @@ static DWORD mal_channel_id_to_win32(DWORD id) } // Converts a channel mapping to a Win32-style channel mask. -static DWORD mal_channel_map_to_channel_mask__win32(const mal_uint8 channelMap[MAL_MAX_CHANNELS], mal_uint32 channels) +DWORD mal_channel_map_to_channel_mask__win32(const mal_channel channelMap[MAL_MAX_CHANNELS], mal_uint32 channels) { DWORD dwChannelMask = 0; for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { @@ -3194,26 +5161,100 @@ static DWORD mal_channel_map_to_channel_mask__win32(const mal_uint8 channelMap[M } // Converts a Win32-style channel mask to a mini_al channel map. -static void mal_channel_mask_to_channel_map__win32(DWORD dwChannelMask, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +void mal_channel_mask_to_channel_map__win32(DWORD dwChannelMask, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) { if (channels == 1 && dwChannelMask == 0) { - channelMap[0] = MAL_CHANNEL_FRONT_CENTER; + channelMap[0] = MAL_CHANNEL_MONO; } else if (channels == 2 && dwChannelMask == 0) { channelMap[0] = MAL_CHANNEL_FRONT_LEFT; channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; } else { - // Just iterate over each bit. - mal_uint32 iChannel = 0; - for (mal_uint32 iBit = 0; iBit < 32; ++iBit) { - DWORD bitValue = (dwChannelMask & (1 << iBit)); - if (bitValue != 0) { - // The bit is set. - channelMap[iChannel] = mal_channel_id_to_mal__win32(bitValue); - iChannel += 1; + if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { + channelMap[0] = MAL_CHANNEL_MONO; + } else { + // Just iterate over each bit. + mal_uint32 iChannel = 0; + for (mal_uint32 iBit = 0; iBit < 32; ++iBit) { + DWORD bitValue = (dwChannelMask & (1UL << iBit)); + if (bitValue != 0) { + // The bit is set. + channelMap[iChannel] = mal_channel_id_to_mal__win32(bitValue); + iChannel += 1; + } } } } } + +#ifdef __cplusplus +mal_bool32 mal_is_guid_equal(const void* a, const void* b) +{ + return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); +} +#else +#define mal_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) +#endif + +mal_format mal_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF) +{ + mal_assert(pWF != NULL); + + if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF; + if (mal_is_guid_equal(&pWFEX->SubFormat, &MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { + if (pWFEX->Samples.wValidBitsPerSample == 32) { + return mal_format_s32; + } + if (pWFEX->Samples.wValidBitsPerSample == 24) { + if (pWFEX->Format.wBitsPerSample == 32) { + //return mal_format_s24_32; + } + if (pWFEX->Format.wBitsPerSample == 24) { + return mal_format_s24; + } + } + if (pWFEX->Samples.wValidBitsPerSample == 16) { + return mal_format_s16; + } + if (pWFEX->Samples.wValidBitsPerSample == 8) { + return mal_format_u8; + } + } + if (mal_is_guid_equal(&pWFEX->SubFormat, &MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { + if (pWFEX->Samples.wValidBitsPerSample == 32) { + return mal_format_f32; + } + //if (pWFEX->Samples.wValidBitsPerSample == 64) { + // return mal_format_f64; + //} + } + } else { + if (pWF->wFormatTag == WAVE_FORMAT_PCM) { + if (pWF->wBitsPerSample == 32) { + return mal_format_s32; + } + if (pWF->wBitsPerSample == 24) { + return mal_format_s24; + } + if (pWF->wBitsPerSample == 16) { + return mal_format_s16; + } + if (pWF->wBitsPerSample == 8) { + return mal_format_u8; + } + } + if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { + if (pWF->wBitsPerSample == 32) { + return mal_format_f32; + } + if (pWF->wBitsPerSample == 64) { + //return mal_format_f64; + } + } + } + + return mal_format_unknown; +} #endif @@ -3223,718 +5264,1669 @@ static void mal_channel_mask_to_channel_map__win32(DWORD dwChannelMask, mal_uint // /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_HAS_WASAPI -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4091) // 'typedef ': ignored on left of '' when no variable is declared -#endif -#include -#include -#include -#if defined(_MSC_VER) - #pragma warning(pop) +//#if defined(_MSC_VER) +// #pragma warning(push) +// #pragma warning(disable:4091) // 'typedef ': ignored on left of '' when no variable is declared +//#endif +//#include +//#include +//#if defined(_MSC_VER) +// #pragma warning(pop) +//#endif + + +// Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. +#if defined(__DMC__) +#define _WIN32_WINNT_VISTA 0x0600 +#define VER_MINORVERSION 0x01 +#define VER_MAJORVERSION 0x02 +#define VER_SERVICEPACKMAJOR 0x20 +#define VER_GREATER_EQUAL 0x03 + +typedef struct { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + WCHAR szCSDVersion[128]; + WORD wServicePackMajor; + WORD wServicePackMinor; + WORD wSuiteMask; + BYTE wProductType; + BYTE wReserved; +} mal_OSVERSIONINFOEXW; + +BOOL WINAPI VerifyVersionInfoW(mal_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); +ULONGLONG WINAPI VerSetConditionMask(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); +#else +typedef OSVERSIONINFOEXW mal_OSVERSIONINFOEXW; #endif -const PROPERTYKEY g_malPKEY_Device_FriendlyName = {{0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0}}, 14}; -const PROPERTYKEY g_malPKEY_AudioEngine_DeviceFormat = {{0xf19f064d, 0x82c, 0x4e27, {0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c}}, 0}; -const IID g_malCLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; // BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) -const IID g_malIID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; // A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) -const IID g_malIID_IAudioClient_Instance = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; // 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) -const IID g_malIID_IAudioRenderClient_Instance = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; // F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) -const IID g_malIID_IAudioCaptureClient_Instance = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; // C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) +#ifndef PROPERTYKEY_DEFINED +#define PROPERTYKEY_DEFINED +typedef struct +{ + GUID fmtid; + DWORD pid; +} PROPERTYKEY; +#endif +// Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). +static MAL_INLINE void mal_PropVariantInit(PROPVARIANT* pProp) +{ + mal_zero_object(pProp); +} + + +const PROPERTYKEY MAL_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; +const PROPERTYKEY MAL_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; + +const IID MAL_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; // 00000000-0000-0000-C000-000000000046 +const IID MAL_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; // 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 + +const IID MAL_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; // 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) +const IID MAL_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; // 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) +const IID MAL_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; // 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) +const IID MAL_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; // F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) +const IID MAL_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; // C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) +const IID MAL_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; // 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) #ifndef MAL_WIN32_DESKTOP -const IID g_malIID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; // E6327CAD-DCEC-4949-AE8A-991E976A79D2 -const IID g_malIID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; // 2EEF81BE-33FA-4800-9670-1CD474972C3F +const IID MAL_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; // E6327CAD-DCEC-4949-AE8A-991E976A79D2 +const IID MAL_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; // 2EEF81BE-33FA-4800-9670-1CD474972C3F +const IID MAL_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; // 41D949AB-9862-444A-80F6-C261334DA5EB #endif +const IID MAL_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; // BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) +const IID MAL_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; // A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) #ifdef __cplusplus -#define g_malCLSID_MMDeviceEnumerator g_malCLSID_MMDeviceEnumerator_Instance -#define g_malIID_IMMDeviceEnumerator g_malIID_IMMDeviceEnumerator_Instance -#define g_malIID_IAudioClient g_malIID_IAudioClient_Instance -#define g_malIID_IAudioRenderClient g_malIID_IAudioRenderClient_Instance -#define g_malIID_IAudioCaptureClient g_malIID_IAudioCaptureClient_Instance +#define MAL_CLSID_MMDeviceEnumerator MAL_CLSID_MMDeviceEnumerator_Instance +#define MAL_IID_IMMDeviceEnumerator MAL_IID_IMMDeviceEnumerator_Instance #else -#define g_malCLSID_MMDeviceEnumerator &g_malCLSID_MMDeviceEnumerator_Instance -#define g_malIID_IMMDeviceEnumerator &g_malIID_IMMDeviceEnumerator_Instance -#define g_malIID_IAudioClient &g_malIID_IAudioClient_Instance -#define g_malIID_IAudioRenderClient &g_malIID_IAudioRenderClient_Instance -#define g_malIID_IAudioCaptureClient &g_malIID_IAudioCaptureClient_Instance +#define MAL_CLSID_MMDeviceEnumerator &MAL_CLSID_MMDeviceEnumerator_Instance +#define MAL_IID_IMMDeviceEnumerator &MAL_IID_IMMDeviceEnumerator_Instance #endif -#ifdef __cplusplus -#define mal_is_guid_equal(a, b) IsEqualGUID(a, b) +typedef struct mal_IUnknown mal_IUnknown; +#ifdef MAL_WIN32_DESKTOP +#define MAL_MM_DEVICE_STATE_ACTIVE 1 +#define MAL_MM_DEVICE_STATE_DISABLED 2 +#define MAL_MM_DEVICE_STATE_NOTPRESENT 4 +#define MAL_MM_DEVICE_STATE_UNPLUGGED 8 + +typedef struct mal_IMMDeviceEnumerator mal_IMMDeviceEnumerator; +typedef struct mal_IMMDeviceCollection mal_IMMDeviceCollection; +typedef struct mal_IMMDevice mal_IMMDevice; #else -#define mal_is_guid_equal(a, b) IsEqualGUID(&a, &b) +typedef struct mal_IActivateAudioInterfaceCompletionHandler mal_IActivateAudioInterfaceCompletionHandler; +typedef struct mal_IActivateAudioInterfaceAsyncOperation mal_IActivateAudioInterfaceAsyncOperation; #endif +typedef struct mal_IPropertyStore mal_IPropertyStore; +typedef struct mal_IAudioClient mal_IAudioClient; +typedef struct mal_IAudioClient2 mal_IAudioClient2; +typedef struct mal_IAudioClient3 mal_IAudioClient3; +typedef struct mal_IAudioRenderClient mal_IAudioRenderClient; +typedef struct mal_IAudioCaptureClient mal_IAudioCaptureClient; + +typedef mal_int64 MAL_REFERENCE_TIME; + +#define MAL_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 +#define MAL_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 +#define MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 +#define MAL_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 +#define MAL_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 +#define MAL_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 +#define MAL_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 +#define MAL_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 +#define MAL_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 +#define MAL_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 + +// We only care about a few error codes. +#define MAL_AUDCLNT_E_INVALID_DEVICE_PERIOD (-2004287456) +#define MAL_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED (-2004287463) +#define MAL_AUDCLNT_S_BUFFER_EMPTY (143196161) +#define MAL_AUDCLNT_E_DEVICE_IN_USE (-2004287478) + +typedef enum +{ + mal_eRender = 0, + mal_eCapture = 1, + mal_eAll = 2 +} mal_EDataFlow; + +typedef enum +{ + mal_eConsole = 0, + mal_eMultimedia = 1, + mal_eCommunications = 2 +} mal_ERole; + +typedef enum +{ + MAL_AUDCLNT_SHAREMODE_SHARED, + MAL_AUDCLNT_SHAREMODE_EXCLUSIVE +} MAL_AUDCLNT_SHAREMODE; + +typedef enum +{ + MAL_AudioCategory_Other = 0, // <-- mini_al is only caring about Other. +} MAL_AUDIO_STREAM_CATEGORY; + +typedef struct +{ + UINT32 cbSize; + BOOL bIsOffload; + MAL_AUDIO_STREAM_CATEGORY eCategory; +} mal_AudioClientProperties; + +// IUnknown +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IUnknown* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IUnknown* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IUnknown* pThis); +} mal_IUnknownVtbl; +struct mal_IUnknown +{ + mal_IUnknownVtbl* lpVtbl; +}; +HRESULT mal_IUnknown_QueryInterface(mal_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IUnknown_AddRef(mal_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IUnknown_Release(mal_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } #ifdef MAL_WIN32_DESKTOP + // IMMNotificationClient + typedef struct + { + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IMMNotificationClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IMMNotificationClient* pThis); + + // IMMNotificationClient + HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState); + HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(mal_IMMNotificationClient* pThis, mal_EDataFlow dataFlow, mal_ERole role, LPCWSTR pDefaultDeviceID); + HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key); + } mal_IMMNotificationClientVtbl; + // IMMDeviceEnumerator - #ifdef __cplusplus - #define IMMDeviceEnumerator_Release(p) ((IMMDeviceEnumerator*)p)->Release() - #else - #define IMMDeviceEnumerator_Release(p) ((IMMDeviceEnumerator*)p)->lpVtbl->Release((IMMDeviceEnumerator*)p) - #endif - #ifdef __cplusplus - #define IMMDeviceEnumerator_EnumAudioEndpoints(p, a, b, c) ((IMMDeviceEnumerator*)p)->EnumAudioEndpoints(a, b, c) - #else - #define IMMDeviceEnumerator_EnumAudioEndpoints(p, a, b, c) ((IMMDeviceEnumerator*)p)->lpVtbl->EnumAudioEndpoints(p, a, b, c) - #endif - #ifdef __cplusplus - #define IMMDeviceEnumerator_GetDefaultAudioEndpoint(p, a, b, c) ((IMMDeviceEnumerator*)p)->GetDefaultAudioEndpoint(a, b, c) - #else - #define IMMDeviceEnumerator_GetDefaultAudioEndpoint(p, a, b, c) ((IMMDeviceEnumerator*)p)->lpVtbl->GetDefaultAudioEndpoint(p, a, b, c) - #endif - #ifdef __cplusplus - #define IMMDeviceEnumerator_GetDevice(p, a, b) ((IMMDeviceEnumerator*)p)->GetDevice(a, b) - #else - #define IMMDeviceEnumerator_GetDevice(p, a, b) ((IMMDeviceEnumerator*)p)->lpVtbl->GetDevice(p, a, b) - #endif + typedef struct + { + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IMMDeviceEnumerator* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IMMDeviceEnumerator* pThis); + + // IMMDeviceEnumerator + HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (mal_IMMDeviceEnumerator* pThis, mal_EDataFlow dataFlow, DWORD dwStateMask, mal_IMMDeviceCollection** ppDevices); + HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (mal_IMMDeviceEnumerator* pThis, mal_EDataFlow dataFlow, mal_ERole role, mal_IMMDevice** ppEndpoint); + HRESULT (STDMETHODCALLTYPE * GetDevice) (mal_IMMDeviceEnumerator* pThis, LPCWSTR pID, mal_IMMDevice** ppDevice); + HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (mal_IMMDeviceEnumerator* pThis, mal_IMMNotificationClient* pClient); + HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(mal_IMMDeviceEnumerator* pThis, mal_IMMNotificationClient* pClient); + } mal_IMMDeviceEnumeratorVtbl; + struct mal_IMMDeviceEnumerator + { + mal_IMMDeviceEnumeratorVtbl* lpVtbl; + }; + HRESULT mal_IMMDeviceEnumerator_QueryInterface(mal_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + ULONG mal_IMMDeviceEnumerator_AddRef(mal_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } + ULONG mal_IMMDeviceEnumerator_Release(mal_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } + HRESULT mal_IMMDeviceEnumerator_EnumAudioEndpoints(mal_IMMDeviceEnumerator* pThis, mal_EDataFlow dataFlow, DWORD dwStateMask, mal_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } + HRESULT mal_IMMDeviceEnumerator_GetDefaultAudioEndpoint(mal_IMMDeviceEnumerator* pThis, mal_EDataFlow dataFlow, mal_ERole role, mal_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } + HRESULT mal_IMMDeviceEnumerator_GetDevice(mal_IMMDeviceEnumerator* pThis, LPCWSTR pID, mal_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } + HRESULT mal_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(mal_IMMDeviceEnumerator* pThis, mal_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } + HRESULT mal_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mal_IMMDeviceEnumerator* pThis, mal_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } + // IMMDeviceCollection - #ifdef __cplusplus - #define IMMDeviceCollection_Release(p) ((IMMDeviceCollection*)p)->Release() - #else - #define IMMDeviceCollection_Release(p) ((IMMDeviceCollection*)p)->lpVtbl->Release((IMMDeviceCollection*)p) - #endif - #ifdef __cplusplus - #define IMMDeviceCollection_GetCount(p, a) ((IMMDeviceCollection*)p)->GetCount(a) - #else - #define IMMDeviceCollection_GetCount(p, a) ((IMMDeviceCollection*)p)->lpVtbl->GetCount((IMMDeviceCollection*)p, a) - #endif - #ifdef __cplusplus - #define IMMDeviceCollection_Item(p, a, b) ((IMMDeviceCollection*)p)->Item(a, b) - #else - #define IMMDeviceCollection_Item(p, a, b) ((IMMDeviceCollection*)p)->lpVtbl->Item((IMMDeviceCollection*)p, a, b) - #endif + typedef struct + { + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IMMDeviceCollection* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IMMDeviceCollection* pThis); + + // IMMDeviceCollection + HRESULT (STDMETHODCALLTYPE * GetCount)(mal_IMMDeviceCollection* pThis, UINT* pDevices); + HRESULT (STDMETHODCALLTYPE * Item) (mal_IMMDeviceCollection* pThis, UINT nDevice, mal_IMMDevice** ppDevice); + } mal_IMMDeviceCollectionVtbl; + struct mal_IMMDeviceCollection + { + mal_IMMDeviceCollectionVtbl* lpVtbl; + }; + HRESULT mal_IMMDeviceCollection_QueryInterface(mal_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + ULONG mal_IMMDeviceCollection_AddRef(mal_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } + ULONG mal_IMMDeviceCollection_Release(mal_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } + HRESULT mal_IMMDeviceCollection_GetCount(mal_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } + HRESULT mal_IMMDeviceCollection_Item(mal_IMMDeviceCollection* pThis, UINT nDevice, mal_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } + // IMMDevice - #ifdef __cplusplus - #define IMMDevice_Release(p) ((IMMDevice*)p)->Release() - #else - #define IMMDevice_Release(p) ((IMMDevice*)p)->lpVtbl->Release((IMMDevice*)p) - #endif - #ifdef __cplusplus - #define IMMDevice_GetId(p, a) ((IMMDevice*)p)->GetId(a) - #else - #define IMMDevice_GetId(p, a) ((IMMDevice*)p)->lpVtbl->GetId((IMMDevice*)p, a) - #endif - #ifdef __cplusplus - #define IMMDevice_OpenPropertyStore(p, a, b) ((IMMDevice*)p)->OpenPropertyStore(a, b) - #else - #define IMMDevice_OpenPropertyStore(p, a, b) ((IMMDevice*)p)->lpVtbl->OpenPropertyStore((IMMDevice*)p, a, b) - #endif - #ifdef __cplusplus - #define IMMDevice_Activate(p, a, b, c, d) ((IMMDevice*)p)->Activate(a, b, c, d) - #else - #define IMMDevice_Activate(p, a, b, c, d) ((IMMDevice*)p)->lpVtbl->Activate((IMMDevice*)p, a, b, c, d) - #endif + typedef struct + { + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IMMDevice* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IMMDevice* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IMMDevice* pThis); + + // IMMDevice + HRESULT (STDMETHODCALLTYPE * Activate) (mal_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface); + HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(mal_IMMDevice* pThis, DWORD stgmAccess, mal_IPropertyStore** ppProperties); + HRESULT (STDMETHODCALLTYPE * GetId) (mal_IMMDevice* pThis, LPWSTR *pID); + HRESULT (STDMETHODCALLTYPE * GetState) (mal_IMMDevice* pThis, DWORD *pState); + } mal_IMMDeviceVtbl; + struct mal_IMMDevice + { + mal_IMMDeviceVtbl* lpVtbl; + }; + HRESULT mal_IMMDevice_QueryInterface(mal_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + ULONG mal_IMMDevice_AddRef(mal_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } + ULONG mal_IMMDevice_Release(mal_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } + HRESULT mal_IMMDevice_Activate(mal_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } + HRESULT mal_IMMDevice_OpenPropertyStore(mal_IMMDevice* pThis, DWORD stgmAccess, mal_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } + HRESULT mal_IMMDevice_GetId(mal_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); } + HRESULT mal_IMMDevice_GetState(mal_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } #else // IActivateAudioInterfaceAsyncOperation - #ifdef __cplusplus - #define IActivateAudioInterfaceAsyncOperation_Release(p) ((IActivateAudioInterfaceAsyncOperation*)p)->Release() - #else - #define IActivateAudioInterfaceAsyncOperation_Release(p) ((IActivateAudioInterfaceAsyncOperation*)p)->lpVtbl->Release((IActivateAudioInterfaceAsyncOperation*)p) - #endif - #ifdef __cplusplus - #define IActivateAudioInterfaceAsyncOperation_GetActivateResult(p, a, b) ((IActivateAudioInterfaceAsyncOperation*)p)->GetActivateResult(a, b) - #else - #define IActivateAudioInterfaceAsyncOperation_GetActivateResult(p, a, b) ((IActivateAudioInterfaceAsyncOperation*)p)->lpVtbl->GetActivateResult((IActivateAudioInterfaceAsyncOperation*)p, a, b) - #endif + typedef struct + { + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IActivateAudioInterfaceAsyncOperation* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IActivateAudioInterfaceAsyncOperation* pThis); + + // IActivateAudioInterfaceAsyncOperation + HRESULT (STDMETHODCALLTYPE * GetActivateResult)(mal_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, mal_IUnknown** ppActivatedInterface); + } mal_IActivateAudioInterfaceAsyncOperationVtbl; + struct mal_IActivateAudioInterfaceAsyncOperation + { + mal_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; + }; + HRESULT mal_IActivateAudioInterfaceAsyncOperation_QueryInterface(mal_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + ULONG mal_IActivateAudioInterfaceAsyncOperation_AddRef(mal_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } + ULONG mal_IActivateAudioInterfaceAsyncOperation_Release(mal_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } + HRESULT mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(mal_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, mal_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } #endif // IPropertyStore -#ifdef __cplusplus - #define IPropertyStore_Release(p) ((IPropertyStore*)p)->Release() -#else - #define IPropertyStore_Release(p) ((IPropertyStore*)p)->lpVtbl->Release((IPropertyStore*)p) -#endif -#ifdef __cplusplus - #define IPropertyStore_GetValue(p, a, b) ((IPropertyStore*)p)->GetValue(a, b) -#else - #define IPropertyStore_GetValue(p, a, b) ((IPropertyStore*)p)->lpVtbl->GetValue((IPropertyStore*)p, &a, b) -#endif +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IPropertyStore* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IPropertyStore* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IPropertyStore* pThis); + + // IPropertyStore + HRESULT (STDMETHODCALLTYPE * GetCount)(mal_IPropertyStore* pThis, DWORD* pPropCount); + HRESULT (STDMETHODCALLTYPE * GetAt) (mal_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); + HRESULT (STDMETHODCALLTYPE * GetValue)(mal_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar); + HRESULT (STDMETHODCALLTYPE * SetValue)(mal_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar); + HRESULT (STDMETHODCALLTYPE * Commit) (mal_IPropertyStore* pThis); +} mal_IPropertyStoreVtbl; +struct mal_IPropertyStore +{ + mal_IPropertyStoreVtbl* lpVtbl; +}; +HRESULT mal_IPropertyStore_QueryInterface(mal_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IPropertyStore_AddRef(mal_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IPropertyStore_Release(mal_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IPropertyStore_GetCount(mal_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } +HRESULT mal_IPropertyStore_GetAt(mal_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } +HRESULT mal_IPropertyStore_GetValue(mal_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } +HRESULT mal_IPropertyStore_SetValue(mal_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } +HRESULT mal_IPropertyStore_Commit(mal_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } + // IAudioClient -#ifdef __cplusplus - #define IAudioClient_Release(p) ((IAudioClient*)p)->Release() -#else - #define IAudioClient_Release(p) ((IAudioClient*)p)->lpVtbl->Release((IAudioClient*)p) -#endif -#ifdef __cplusplus - #define IAudioClient_IsFormatSupported(p, a, b, c) ((IAudioClient*)p)->IsFormatSupported(a, b, c) -#else - #define IAudioClient_IsFormatSupported(p, a, b, c) ((IAudioClient*)p)->lpVtbl->IsFormatSupported((IAudioClient*)p, a, b, c) -#endif -#ifdef __cplusplus - #define IAudioClient_GetMixFormat(p, a) ((IAudioClient*)p)->GetMixFormat(a) -#else - #define IAudioClient_GetMixFormat(p, a) ((IAudioClient*)p)->lpVtbl->GetMixFormat((IAudioClient*)p, a) -#endif -#ifdef __cplusplus - #define IAudioClient_Initialize(p, a, b, c, d, e, f) ((IAudioClient*)p)->Initialize(a, b, c, d, e, f) -#else - #define IAudioClient_Initialize(p, a, b, c, d, e, f) ((IAudioClient*)p)->lpVtbl->Initialize((IAudioClient*)p, a, b, c, d, e, f) -#endif -#ifdef __cplusplus - #define IAudioClient_GetBufferSize(p, a) ((IAudioClient*)p)->GetBufferSize(a) -#else - #define IAudioClient_GetBufferSize(p, a) ((IAudioClient*)p)->lpVtbl->GetBufferSize((IAudioClient*)p, a) -#endif -#ifdef __cplusplus - #define IAudioClient_GetService(p, a, b) ((IAudioClient*)p)->GetService(a, b) -#else - #define IAudioClient_GetService(p, a, b) ((IAudioClient*)p)->lpVtbl->GetService((IAudioClient*)p, a, b) -#endif -#ifdef __cplusplus - #define IAudioClient_Start(p) ((IAudioClient*)p)->Start() -#else - #define IAudioClient_Start(p) ((IAudioClient*)p)->lpVtbl->Start((IAudioClient*)p) -#endif -#ifdef __cplusplus - #define IAudioClient_Stop(p) ((IAudioClient*)p)->Stop() -#else - #define IAudioClient_Stop(p) ((IAudioClient*)p)->lpVtbl->Stop((IAudioClient*)p) -#endif -#ifdef __cplusplus - #define IAudioClient_GetCurrentPadding(p, a) ((IAudioClient*)p)->GetCurrentPadding(a) -#else - #define IAudioClient_GetCurrentPadding(p, a) ((IAudioClient*)p)->lpVtbl->GetCurrentPadding((IAudioClient*)p, a) -#endif -#ifdef __cplusplus - #define IAudioClient_SetEventHandle(p, a) ((IAudioClient*)p)->SetEventHandle(a) -#else - #define IAudioClient_SetEventHandle(p, a) ((IAudioClient*)p)->lpVtbl->SetEventHandle((IAudioClient*)p, a) -#endif +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IAudioClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IAudioClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IAudioClient* pThis); + + // IAudioClient + HRESULT (STDMETHODCALLTYPE * Initialize) (mal_IAudioClient* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (mal_IAudioClient* pThis, mal_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (mal_IAudioClient* pThis, MAL_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(mal_IAudioClient* pThis, mal_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(mal_IAudioClient* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (mal_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (mal_IAudioClient* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (mal_IAudioClient* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (mal_IAudioClient* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (mal_IAudioClient* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (mal_IAudioClient* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (mal_IAudioClient* pThis, const IID* const riid, void** pp); +} mal_IAudioClientVtbl; +struct mal_IAudioClient +{ + mal_IAudioClientVtbl* lpVtbl; +}; +HRESULT mal_IAudioClient_QueryInterface(mal_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IAudioClient_AddRef(mal_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IAudioClient_Release(mal_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IAudioClient_Initialize(mal_IAudioClient* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +HRESULT mal_IAudioClient_GetBufferSize(mal_IAudioClient* pThis, mal_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +HRESULT mal_IAudioClient_GetStreamLatency(mal_IAudioClient* pThis, MAL_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +HRESULT mal_IAudioClient_GetCurrentPadding(mal_IAudioClient* pThis, mal_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +HRESULT mal_IAudioClient_IsFormatSupported(mal_IAudioClient* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +HRESULT mal_IAudioClient_GetMixFormat(mal_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +HRESULT mal_IAudioClient_GetDevicePeriod(mal_IAudioClient* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +HRESULT mal_IAudioClient_Start(mal_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } +HRESULT mal_IAudioClient_Stop(mal_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } +HRESULT mal_IAudioClient_Reset(mal_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } +HRESULT mal_IAudioClient_SetEventHandle(mal_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +HRESULT mal_IAudioClient_GetService(mal_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } + +// IAudioClient2 +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IAudioClient2* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IAudioClient2* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IAudioClient2* pThis); + + // IAudioClient + HRESULT (STDMETHODCALLTYPE * Initialize) (mal_IAudioClient2* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (mal_IAudioClient2* pThis, mal_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (mal_IAudioClient2* pThis, MAL_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(mal_IAudioClient2* pThis, mal_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(mal_IAudioClient2* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (mal_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (mal_IAudioClient2* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (mal_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (mal_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (mal_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (mal_IAudioClient2* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (mal_IAudioClient2* pThis, const IID* const riid, void** pp); + + // IAudioClient2 + HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (mal_IAudioClient2* pThis, MAL_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); + HRESULT (STDMETHODCALLTYPE * SetClientProperties)(mal_IAudioClient2* pThis, const mal_AudioClientProperties* pProperties); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(mal_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MAL_REFERENCE_TIME* pMinBufferDuration, MAL_REFERENCE_TIME* pMaxBufferDuration); +} mal_IAudioClient2Vtbl; +struct mal_IAudioClient2 +{ + mal_IAudioClient2Vtbl* lpVtbl; +}; +HRESULT mal_IAudioClient2_QueryInterface(mal_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IAudioClient2_AddRef(mal_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IAudioClient2_Release(mal_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IAudioClient2_Initialize(mal_IAudioClient2* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +HRESULT mal_IAudioClient2_GetBufferSize(mal_IAudioClient2* pThis, mal_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +HRESULT mal_IAudioClient2_GetStreamLatency(mal_IAudioClient2* pThis, MAL_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +HRESULT mal_IAudioClient2_GetCurrentPadding(mal_IAudioClient2* pThis, mal_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +HRESULT mal_IAudioClient2_IsFormatSupported(mal_IAudioClient2* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +HRESULT mal_IAudioClient2_GetMixFormat(mal_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +HRESULT mal_IAudioClient2_GetDevicePeriod(mal_IAudioClient2* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +HRESULT mal_IAudioClient2_Start(mal_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } +HRESULT mal_IAudioClient2_Stop(mal_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } +HRESULT mal_IAudioClient2_Reset(mal_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } +HRESULT mal_IAudioClient2_SetEventHandle(mal_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +HRESULT mal_IAudioClient2_GetService(mal_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } +HRESULT mal_IAudioClient2_IsOffloadCapable(mal_IAudioClient2* pThis, MAL_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } +HRESULT mal_IAudioClient2_SetClientProperties(mal_IAudioClient2* pThis, const mal_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } +HRESULT mal_IAudioClient2_GetBufferSizeLimits(mal_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MAL_REFERENCE_TIME* pMinBufferDuration, MAL_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } + + +// IAudioClient3 +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IAudioClient3* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IAudioClient3* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IAudioClient3* pThis); + + // IAudioClient + HRESULT (STDMETHODCALLTYPE * Initialize) (mal_IAudioClient3* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (mal_IAudioClient3* pThis, mal_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (mal_IAudioClient3* pThis, MAL_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(mal_IAudioClient3* pThis, mal_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(mal_IAudioClient3* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (mal_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (mal_IAudioClient3* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (mal_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (mal_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (mal_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (mal_IAudioClient3* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (mal_IAudioClient3* pThis, const IID* const riid, void** pp); + + // IAudioClient2 + HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (mal_IAudioClient3* pThis, MAL_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); + HRESULT (STDMETHODCALLTYPE * SetClientProperties)(mal_IAudioClient3* pThis, const mal_AudioClientProperties* pProperties); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(mal_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MAL_REFERENCE_TIME* pMinBufferDuration, MAL_REFERENCE_TIME* pMaxBufferDuration); + + // IAudioClient3 + HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (mal_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(mal_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (mal_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); +} mal_IAudioClient3Vtbl; +struct mal_IAudioClient3 +{ + mal_IAudioClient3Vtbl* lpVtbl; +}; +HRESULT mal_IAudioClient3_QueryInterface(mal_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IAudioClient3_AddRef(mal_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IAudioClient3_Release(mal_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IAudioClient3_Initialize(mal_IAudioClient3* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +HRESULT mal_IAudioClient3_GetBufferSize(mal_IAudioClient3* pThis, mal_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +HRESULT mal_IAudioClient3_GetStreamLatency(mal_IAudioClient3* pThis, MAL_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +HRESULT mal_IAudioClient3_GetCurrentPadding(mal_IAudioClient3* pThis, mal_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +HRESULT mal_IAudioClient3_IsFormatSupported(mal_IAudioClient3* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +HRESULT mal_IAudioClient3_GetMixFormat(mal_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +HRESULT mal_IAudioClient3_GetDevicePeriod(mal_IAudioClient3* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +HRESULT mal_IAudioClient3_Start(mal_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } +HRESULT mal_IAudioClient3_Stop(mal_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } +HRESULT mal_IAudioClient3_Reset(mal_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } +HRESULT mal_IAudioClient3_SetEventHandle(mal_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +HRESULT mal_IAudioClient3_GetService(mal_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } +HRESULT mal_IAudioClient3_IsOffloadCapable(mal_IAudioClient3* pThis, MAL_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } +HRESULT mal_IAudioClient3_SetClientProperties(mal_IAudioClient3* pThis, const mal_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } +HRESULT mal_IAudioClient3_GetBufferSizeLimits(mal_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MAL_REFERENCE_TIME* pMinBufferDuration, MAL_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } +HRESULT mal_IAudioClient3_GetSharedModeEnginePeriod(mal_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } +HRESULT mal_IAudioClient3_GetCurrentSharedModeEnginePeriod(mal_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } +HRESULT mal_IAudioClient3_InitializeSharedAudioStream(mal_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } + // IAudioRenderClient -#ifdef __cplusplus - #define IAudioRenderClient_Release(p) ((IAudioRenderClient*)p)->Release() -#else - #define IAudioRenderClient_Release(p) ((IAudioRenderClient*)p)->lpVtbl->Release((IAudioRenderClient*)p) -#endif -#ifdef __cplusplus - #define IAudioRenderClient_GetBuffer(p, a, b) ((IAudioRenderClient*)p)->GetBuffer(a, b) -#else - #define IAudioRenderClient_GetBuffer(p, a, b) ((IAudioRenderClient*)p)->lpVtbl->GetBuffer((IAudioRenderClient*)p, a, b) -#endif -#ifdef __cplusplus - #define IAudioRenderClient_ReleaseBuffer(p, a, b) ((IAudioRenderClient*)p)->ReleaseBuffer(a, b) -#else - #define IAudioRenderClient_ReleaseBuffer(p, a, b) ((IAudioRenderClient*)p)->lpVtbl->ReleaseBuffer((IAudioRenderClient*)p, a, b) -#endif +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IAudioRenderClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IAudioRenderClient* pThis); + + // IAudioRenderClient + HRESULT (STDMETHODCALLTYPE * GetBuffer) (mal_IAudioRenderClient* pThis, mal_uint32 numFramesRequested, BYTE** ppData); + HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(mal_IAudioRenderClient* pThis, mal_uint32 numFramesWritten, DWORD dwFlags); +} mal_IAudioRenderClientVtbl; +struct mal_IAudioRenderClient +{ + mal_IAudioRenderClientVtbl* lpVtbl; +}; +HRESULT mal_IAudioRenderClient_QueryInterface(mal_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IAudioRenderClient_AddRef(mal_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IAudioRenderClient_Release(mal_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IAudioRenderClient_GetBuffer(mal_IAudioRenderClient* pThis, mal_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } +HRESULT mal_IAudioRenderClient_ReleaseBuffer(mal_IAudioRenderClient* pThis, mal_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } + // IAudioCaptureClient -#ifdef __cplusplus - #define IAudioCaptureClient_Release(p) ((IAudioCaptureClient*)p)->Release() -#else - #define IAudioCaptureClient_Release(p) ((IAudioCaptureClient*)p)->lpVtbl->Release((IAudioCaptureClient*)p) -#endif -#ifdef __cplusplus - #define IAudioCaptureClient_GetNextPacketSize(p, a) ((IAudioCaptureClient*)p)->GetNextPacketSize(a) -#else - #define IAudioCaptureClient_GetNextPacketSize(p, a) ((IAudioCaptureClient*)p)->lpVtbl->GetNextPacketSize((IAudioCaptureClient*)p, a) -#endif -#ifdef __cplusplus - #define IAudioCaptureClient_GetBuffer(p, a, b, c, d, e) ((IAudioCaptureClient*)p)->GetBuffer(a, b, c, d, e) -#else - #define IAudioCaptureClient_GetBuffer(p, a, b, c, d, e) ((IAudioCaptureClient*)p)->lpVtbl->GetBuffer((IAudioCaptureClient*)p, a, b, c, d, e) -#endif -#ifdef __cplusplus - #define IAudioCaptureClient_ReleaseBuffer(p, a) ((IAudioCaptureClient*)p)->ReleaseBuffer(a) -#else - #define IAudioCaptureClient_ReleaseBuffer(p, a) ((IAudioCaptureClient*)p)->lpVtbl->ReleaseBuffer((IAudioCaptureClient*)p, a) +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IAudioCaptureClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IAudioCaptureClient* pThis); + + // IAudioRenderClient + HRESULT (STDMETHODCALLTYPE * GetBuffer) (mal_IAudioCaptureClient* pThis, BYTE** ppData, mal_uint32* pNumFramesToRead, DWORD* pFlags, mal_uint64* pDevicePosition, mal_uint64* pQPCPosition); + HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (mal_IAudioCaptureClient* pThis, mal_uint32 numFramesRead); + HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(mal_IAudioCaptureClient* pThis, mal_uint32* pNumFramesInNextPacket); +} mal_IAudioCaptureClientVtbl; +struct mal_IAudioCaptureClient +{ + mal_IAudioCaptureClientVtbl* lpVtbl; +}; +HRESULT mal_IAudioCaptureClient_QueryInterface(mal_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IAudioCaptureClient_AddRef(mal_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IAudioCaptureClient_Release(mal_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IAudioCaptureClient_GetBuffer(mal_IAudioCaptureClient* pThis, BYTE** ppData, mal_uint32* pNumFramesToRead, DWORD* pFlags, mal_uint64* pDevicePosition, mal_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } +HRESULT mal_IAudioCaptureClient_ReleaseBuffer(mal_IAudioCaptureClient* pThis, mal_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } +HRESULT mal_IAudioCaptureClient_GetNextPacketSize(mal_IAudioCaptureClient* pThis, mal_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } + +#ifndef MAL_WIN32_DESKTOP +#include +typedef struct mal_completion_handler_uwp mal_completion_handler_uwp; + +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_completion_handler_uwp* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_completion_handler_uwp* pThis); + + // IActivateAudioInterfaceCompletionHandler + HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(mal_completion_handler_uwp* pThis, mal_IActivateAudioInterfaceAsyncOperation* pActivateOperation); +} mal_completion_handler_uwp_vtbl; +struct mal_completion_handler_uwp +{ + mal_completion_handler_uwp_vtbl* lpVtbl; + mal_uint32 counter; + HANDLE hEvent; +}; + +HRESULT STDMETHODCALLTYPE mal_completion_handler_uwp_QueryInterface(mal_completion_handler_uwp* pThis, const IID* const riid, void** ppObject) +{ + // We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To + // "implement" this, we just make sure we return pThis when the IAgileObject is requested. + if (!mal_is_guid_equal(riid, &MAL_IID_IUnknown) && !mal_is_guid_equal(riid, &MAL_IID_IActivateAudioInterfaceCompletionHandler) && !mal_is_guid_equal(riid, &MAL_IID_IAgileObject)) { + *ppObject = NULL; + return E_NOINTERFACE; + } + + // Getting here means the IID is IUnknown or IMMNotificationClient. + *ppObject = (void*)pThis; + ((mal_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); + return S_OK; +} + +ULONG STDMETHODCALLTYPE mal_completion_handler_uwp_AddRef(mal_completion_handler_uwp* pThis) +{ + return (ULONG)mal_atomic_increment_32(&pThis->counter); +} + +ULONG STDMETHODCALLTYPE mal_completion_handler_uwp_Release(mal_completion_handler_uwp* pThis) +{ + mal_uint32 newRefCount = mal_atomic_decrement_32(&pThis->counter); + if (newRefCount == 0) { + return 0; // We don't free anything here because we never allocate the object on the heap. + } + + return (ULONG)newRefCount; +} + +HRESULT STDMETHODCALLTYPE mal_completion_handler_uwp_ActivateCompleted(mal_completion_handler_uwp* pThis, mal_IActivateAudioInterfaceAsyncOperation* pActivateOperation) +{ + (void)pActivateOperation; + SetEvent(pThis->hEvent); + return S_OK; +} + + +static mal_completion_handler_uwp_vtbl g_malCompletionHandlerVtblInstance = { + mal_completion_handler_uwp_QueryInterface, + mal_completion_handler_uwp_AddRef, + mal_completion_handler_uwp_Release, + mal_completion_handler_uwp_ActivateCompleted +}; + +mal_result mal_completion_handler_uwp_init(mal_completion_handler_uwp* pHandler) +{ + mal_assert(pHandler != NULL); + mal_zero_object(pHandler); + + pHandler->lpVtbl = &g_malCompletionHandlerVtblInstance; + pHandler->counter = 1; + pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pHandler->hEvent == NULL) { + return MAL_ERROR; + } + + return MAL_SUCCESS; +} + +void mal_completion_handler_uwp_uninit(mal_completion_handler_uwp* pHandler) +{ + if (pHandler->hEvent != NULL) { + CloseHandle(pHandler->hEvent); + } +} + +void mal_completion_handler_uwp_wait(mal_completion_handler_uwp* pHandler) +{ + WaitForSingleObject(pHandler->hEvent, INFINITE); +} +#endif // !MAL_WIN32_DESKTOP + +// We need a virtual table for our notification client object that's used for detecting changes to the default device. +#ifdef MAL_WIN32_DESKTOP +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_QueryInterface(mal_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) +{ + // We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else + // we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK. + if (!mal_is_guid_equal(riid, &MAL_IID_IUnknown) && !mal_is_guid_equal(riid, &MAL_IID_IMMNotificationClient)) { + *ppObject = NULL; + return E_NOINTERFACE; + } + + // Getting here means the IID is IUnknown or IMMNotificationClient. + *ppObject = (void*)pThis; + ((mal_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); + return S_OK; +} + +ULONG STDMETHODCALLTYPE mal_IMMNotificationClient_AddRef(mal_IMMNotificationClient* pThis) +{ + return (ULONG)mal_atomic_increment_32(&pThis->counter); +} + +ULONG STDMETHODCALLTYPE mal_IMMNotificationClient_Release(mal_IMMNotificationClient* pThis) +{ + mal_uint32 newRefCount = mal_atomic_decrement_32(&pThis->counter); + if (newRefCount == 0) { + return 0; // We don't free anything here because we never allocate the object on the heap. + } + + return (ULONG)newRefCount; +} + + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnDeviceStateChanged(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", pDeviceID, (unsigned int)dwNewState); #endif -mal_result mal_context_init__wasapi(mal_context* pContext) + (void)pThis; + (void)pDeviceID; + (void)dwNewState; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnDeviceAdded(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", pDeviceID); +#endif + + // We don't need to worry about this event for our purposes. + (void)pThis; + (void)pDeviceID; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnDeviceRemoved(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", pDeviceID); +#endif + + // We don't need to worry about this event for our purposes. + (void)pThis; + (void)pDeviceID; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnDefaultDeviceChanged(mal_IMMNotificationClient* pThis, mal_EDataFlow dataFlow, mal_ERole role, LPCWSTR pDefaultDeviceID) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, pDefaultDeviceID); +#endif + + // We only ever use the eConsole role in mini_al. + if (role != mal_eConsole) { + return S_OK; + } + + // We only care about devices with the same data flow and role as the current device. + if ((pThis->pDevice->type == mal_device_type_playback && dataFlow != mal_eRender ) || + (pThis->pDevice->type == mal_device_type_capture && dataFlow != mal_eCapture)) { + return S_OK; + } + + // Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to + // AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in mini_al, we can try re-enabling this once + // it's fixed. + if (pThis->pDevice->exclusiveMode) { + return S_OK; + } + + // We don't change the device here - we change it in the worker thread to keep synchronization simple. To this I'm just setting a flag to + // indicate that the default device has changed. + mal_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultDeviceChanged, MAL_TRUE); + SetEvent(pThis->pDevice->wasapi.hBreakEvent); // <-- The main loop will be waiting on some events. We want to break from this wait ASAP so we can change the device as quickly as possible. + + + (void)pDefaultDeviceID; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnPropertyValueChanged(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", pDeviceID); +#endif + + (void)pThis; + (void)pDeviceID; + (void)key; + return S_OK; +} + +static mal_IMMNotificationClientVtbl g_malNotificationCientVtbl = { + mal_IMMNotificationClient_QueryInterface, + mal_IMMNotificationClient_AddRef, + mal_IMMNotificationClient_Release, + mal_IMMNotificationClient_OnDeviceStateChanged, + mal_IMMNotificationClient_OnDeviceAdded, + mal_IMMNotificationClient_OnDeviceRemoved, + mal_IMMNotificationClient_OnDefaultDeviceChanged, + mal_IMMNotificationClient_OnPropertyValueChanged +}; +#endif // MAL_WIN32_DESKTOP + +mal_bool32 mal_context_is_device_id_equal__wasapi(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) { mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); (void)pContext; -#ifdef MAL_WIN32_DESKTOP - // WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven - // exclusive mode does not work until SP1. - OSVERSIONINFOEXW osvi; - mal_zero_object(&osvi); - osvi.dwOSVersionInfoSize = sizeof(osvi); - osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_VISTA); - osvi.dwMinorVersion = LOBYTE(_WIN32_WINNT_VISTA); - osvi.wServicePackMajor = 1; - if (VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL))) { - return MAL_SUCCESS; - } else { - return MAL_NO_BACKEND; - } -#else - return MAL_SUCCESS; -#endif + return memcmp(pID0->wasapi, pID1->wasapi, sizeof(pID0->wasapi)) == 0; } -mal_result mal_context_uninit__wasapi(mal_context* pContext) +void mal_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, mal_device_info* pInfo) { - mal_assert(pContext != NULL); - mal_assert(pContext->backend == mal_backend_wasapi); - (void)pContext; + mal_assert(pWF != NULL); + mal_assert(pInfo != NULL); - return MAL_SUCCESS; -} - -static mal_result mal_enumerate_devices__wasapi(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) -{ - mal_uint32 infoSize = *pCount; - *pCount = 0; - -#ifdef MAL_WIN32_DESKTOP - IMMDeviceEnumerator* pDeviceEnumerator; - HRESULT hr = mal_CoCreateInstance(pContext, g_malCLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, g_malIID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to create device enumerator.", MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR); - } - - IMMDeviceCollection* pDeviceCollection; - hr = IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, (type == mal_device_type_playback) ? eRender : eCapture, DEVICE_STATE_ACTIVE, &pDeviceCollection); - if (FAILED(hr)) { - IMMDeviceEnumerator_Release(pDeviceEnumerator); - return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to enumerate audio endpoints.", MAL_NO_DEVICE); - } - - IMMDeviceEnumerator_Release(pDeviceEnumerator); - - UINT count; - hr = IMMDeviceCollection_GetCount(pDeviceCollection, &count); - if (FAILED(hr)) { - IMMDeviceCollection_Release(pDeviceCollection); - return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to get device count.", MAL_NO_DEVICE); - } - - for (mal_uint32 iDevice = 0; iDevice < count; ++iDevice) { - if (pInfo != NULL) { - if (infoSize > 0) { - mal_zero_object(pInfo); - - IMMDevice* pDevice; - hr = IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pDevice); - if (SUCCEEDED(hr)) { - // ID. - LPWSTR id; - hr = IMMDevice_GetId(pDevice, &id); - if (SUCCEEDED(hr)) { - size_t idlen = wcslen(id); - if (idlen+sizeof(wchar_t) > sizeof(pInfo->id.wasapi)) { - mal_CoTaskMemFree(pContext, id); - mal_assert(MAL_FALSE); // NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. - continue; - } - - memcpy(pInfo->id.wasapi, id, idlen * sizeof(wchar_t)); - pInfo->id.wasapi[idlen] = '\0'; - - mal_CoTaskMemFree(pContext, id); - } - - // Description / Friendly Name. - IPropertyStore *pProperties; - hr = IMMDevice_OpenPropertyStore(pDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - PROPVARIANT varName; - PropVariantInit(&varName); - hr = IPropertyStore_GetValue(pProperties, g_malPKEY_Device_FriendlyName, &varName); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); - mal_PropVariantClear(pContext, &varName); - } - - IPropertyStore_Release(pProperties); - } - } - - pInfo += 1; - infoSize -= 1; - *pCount += 1; - } - } else { - *pCount += 1; - } - } - - IMMDeviceCollection_Release(pDeviceCollection); -#else - // The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate - // over devices without using MMDevice, I'm restricting devices to defaults. - if (pInfo != NULL) { - if (infoSize > 0) { - if (type == mal_device_type_playback) { - mal_copy_memory(pInfo->id.wasapi, &g_malIID_DEVINTERFACE_AUDIO_RENDER, sizeof(g_malIID_DEVINTERFACE_AUDIO_RENDER)); - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1); - } else { - mal_copy_memory(pInfo->id.wasapi, &g_malIID_DEVINTERFACE_AUDIO_CAPTURE, sizeof(g_malIID_DEVINTERFACE_AUDIO_CAPTURE)); - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1); - } - - pInfo += 1; - *pCount += 1; - } - } else { - *pCount += 1; - } -#endif - - return MAL_SUCCESS; -} - -static void mal_device_uninit__wasapi(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - if (pDevice->wasapi.pRenderClient) { - IAudioRenderClient_Release(pDevice->wasapi.pRenderClient); - } - if (pDevice->wasapi.pCaptureClient) { - IAudioCaptureClient_Release(pDevice->wasapi.pCaptureClient); - } - if (pDevice->wasapi.pAudioClient) { - IAudioClient_Release(pDevice->wasapi.pAudioClient); - } - - if (pDevice->wasapi.hEvent) { - CloseHandle(pDevice->wasapi.hEvent); - } - if (pDevice->wasapi.hStopEvent) { - CloseHandle(pDevice->wasapi.hStopEvent); - } + pInfo->formatCount = 1; + pInfo->formats[0] = mal_format_from_WAVEFORMATEX(pWF); + pInfo->minChannels = pWF->nChannels; + pInfo->maxChannels = pWF->nChannels; + pInfo->minSampleRate = pWF->nSamplesPerSec; + pInfo->maxSampleRate = pWF->nSamplesPerSec; } #ifndef MAL_WIN32_DESKTOP - #ifdef __cplusplus - #include - class malCompletionHandler : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::ClassicCom >, Microsoft::WRL::FtmBase, IActivateAudioInterfaceCompletionHandler > - { - public: - - malCompletionHandler() - : m_hEvent(NULL) - { - } - - mal_result Init() - { - m_hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (m_hEvent == NULL) { - return MAL_ERROR; - } - - return MAL_SUCCESS; - } - - void Uninit() - { - if (m_hEvent != NULL) { - CloseHandle(m_hEvent); - } - } - - void Wait() - { - WaitForSingleObject(m_hEvent, INFINITE); - } - - HRESULT STDMETHODCALLTYPE ActivateCompleted(IActivateAudioInterfaceAsyncOperation *activateOperation) - { - (void)activateOperation; - SetEvent(m_hEvent); - return S_OK; - } - - private: - HANDLE m_hEvent; // This is created in Init(), deleted in Uninit(), waited on in Wait() and signaled in ActivateCompleted(). - }; - #else - #error "The UWP build is currently only supported in C++." - #endif -#endif // !MAL_WIN32_DESKTOP - -static mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IAudioClient** ppAudioClient, mal_IUnknown** ppActivatedInterface) { - (void)pContext; + mal_assert(pContext != NULL); + mal_assert(ppAudioClient != NULL); - mal_assert(pDevice != NULL); - mal_zero_object(&pDevice->wasapi); - - HRESULT hr; - mal_result result = MAL_SUCCESS; - const char* errorMsg = ""; - AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED; - - WAVEFORMATEXTENSIBLE wf; - mal_zero_object(&wf); - wf.Format.cbSize = sizeof(wf); - wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.Format.nChannels = (WORD)pDevice->channels; - wf.Format.nSamplesPerSec = (DWORD)pDevice->sampleRate; - wf.Format.wBitsPerSample = (WORD)mal_get_sample_size_in_bytes(pDevice->format)*8; - wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8; - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; - wf.dwChannelMask = mal_channel_map_to_channel_mask__win32(pDevice->channelMap, pDevice->channels); - if (pDevice->format == mal_format_f32) { - wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } else { - wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } - -#ifdef MAL_WIN32_DESKTOP - IMMDevice* pMMDevice = NULL; - - IMMDeviceEnumerator* pDeviceEnumerator; - hr = mal_CoCreateInstance(pContext, g_malCLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, g_malIID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to create IMMDeviceEnumerator.", result = MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR; - goto done; - } - - if (pDeviceID == NULL) { - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (type == mal_device_type_playback) ? eRender : eCapture, eConsole, &pMMDevice); - if (FAILED(hr)) { - IMMDeviceEnumerator_Release(pDeviceEnumerator); - errorMsg = "[WASAPI] Failed to create default backend device.", result = MAL_WASAPI_FAILED_TO_CREATE_DEVICE; - goto done; - } - } else { - hr = IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, &pMMDevice); - if (FAILED(hr)) { - IMMDeviceEnumerator_Release(pDeviceEnumerator); - errorMsg = "[WASAPI] Failed to create backend device.", result = MAL_WASAPI_FAILED_TO_CREATE_DEVICE; - goto done; - } - } - - IMMDeviceEnumerator_Release(pDeviceEnumerator); - - hr = IMMDevice_Activate(pMMDevice, g_malIID_IAudioClient, CLSCTX_ALL, NULL, &pDevice->wasapi.pAudioClient); - if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to activate device.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; - goto done; - } -#else - IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; - malCompletionHandler completionHandler; + mal_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; + mal_completion_handler_uwp completionHandler; IID iid; if (pDeviceID != NULL) { mal_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid)); } else { - if (type == mal_device_type_playback) { - iid = g_malIID_DEVINTERFACE_AUDIO_RENDER; + if (deviceType == mal_device_type_playback) { + iid = MAL_IID_DEVINTERFACE_AUDIO_RENDER; } else { - iid = g_malIID_DEVINTERFACE_AUDIO_CAPTURE; + iid = MAL_IID_DEVINTERFACE_AUDIO_CAPTURE; } } LPOLESTR iidStr; - hr = StringFromIID(iid, &iidStr); +#if defined(__cplusplus) + HRESULT hr = StringFromIID(iid, &iidStr); +#else + HRESULT hr = StringFromIID(&iid, &iidStr); +#endif if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", result = MAL_OUT_OF_MEMORY; - goto done; + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", MAL_OUT_OF_MEMORY); } - result = completionHandler.Init(); + mal_result result = mal_completion_handler_uwp_init(&completionHandler); if (result != MAL_SUCCESS) { mal_CoTaskMemFree(pContext, iidStr); - - errorMsg = "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; - goto done; + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } - hr = ActivateAudioInterfaceAsync(iidStr, g_malIID_IAudioClient, NULL, &completionHandler, &pAsyncOp); +#if defined(__cplusplus) + hr = ActivateAudioInterfaceAsync(iidStr, MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); +#else + hr = ActivateAudioInterfaceAsync(iidStr, &MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); +#endif if (FAILED(hr)) { - completionHandler.Uninit(); + mal_completion_handler_uwp_uninit(&completionHandler); mal_CoTaskMemFree(pContext, iidStr); - - errorMsg = "[WASAPI] ActivateAudioInterfaceAsync() failed.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; - goto done; + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } mal_CoTaskMemFree(pContext, iidStr); // Wait for the async operation for finish. - completionHandler.Wait(); - completionHandler.Uninit(); + mal_completion_handler_uwp_wait(&completionHandler); + mal_completion_handler_uwp_uninit(&completionHandler); HRESULT activateResult; - IUnknown* pActivatedInterface; - hr = IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); + mal_IUnknown* pActivatedInterface; + hr = mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); + mal_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); + if (FAILED(hr) || FAILED(activateResult)) { - errorMsg = "[WASAPI] Failed to activate device.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; - goto done; + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } // Here is where we grab the IAudioClient interface. - hr = pActivatedInterface->QueryInterface(g_malIID_IAudioClient, &pDevice->wasapi.pAudioClient); + hr = mal_IUnknown_QueryInterface(pActivatedInterface, &MAL_IID_IAudioClient, (void**)ppAudioClient); if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to query IAudioClient interface.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (ppActivatedInterface) { + *ppActivatedInterface = pActivatedInterface; + } else { + mal_IUnknown_Release(pActivatedInterface); + } + + return MAL_SUCCESS; +} +#endif + +mal_result mal_context_get_device_info_from_IAudioClient__wasapi(mal_context* pContext, /*mal_IMMDevice**/void* pMMDevice, mal_IAudioClient* pAudioClient, mal_share_mode shareMode, mal_device_info* pInfo) +{ + mal_assert(pAudioClient != NULL); + mal_assert(pInfo != NULL); + + // We use a different technique to retrieve the device information depending on whether or not we are using shared or exclusive mode. + if (shareMode == mal_share_mode_shared) { + // Shared Mode. We use GetMixFormat() here. + WAVEFORMATEX* pWF = NULL; + HRESULT hr = mal_IAudioClient_GetMixFormat((mal_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF); + if (SUCCEEDED(hr)) { + mal_set_device_info_from_WAVEFORMATEX(pWF, pInfo); + return MAL_SUCCESS; + } else { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } else { + // Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently support on UWP. +#ifdef MAL_WIN32_DESKTOP + // The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is + // correct which will simplify our searching. + mal_IPropertyStore *pProperties; + HRESULT hr = mal_IMMDevice_OpenPropertyStore((mal_IMMDevice*)pMMDevice, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + PROPVARIANT var; + mal_PropVariantInit(&var); + + hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_AudioEngine_DeviceFormat, &var); + if (SUCCEEDED(hr)) { + WAVEFORMATEX* pWF = (WAVEFORMATEX*)var.blob.pBlobData; + mal_set_device_info_from_WAVEFORMATEX(pWF, pInfo); + + // In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format + // first. If this fails, fall back to a search. + hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); + mal_PropVariantClear(pContext, &var); + + if (FAILED(hr)) { + // The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel + // count returned by MAL_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. + mal_uint32 channels = pInfo->minChannels; + + mal_format formatsToSearch[] = { + mal_format_s16, + mal_format_s24, + //mal_format_s24_32, + mal_format_f32, + mal_format_s32, + mal_format_u8 + }; + + mal_channel defaultChannelMap[MAL_MAX_CHANNELS]; + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, channels, defaultChannelMap); + + WAVEFORMATEXTENSIBLE wf; + mal_zero_object(&wf); + wf.Format.cbSize = sizeof(wf); + wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf.Format.nChannels = (WORD)channels; + wf.dwChannelMask = mal_channel_map_to_channel_mask__win32(defaultChannelMap, channels); + + mal_bool32 found = MAL_FALSE; + for (mal_uint32 iFormat = 0; iFormat < mal_countof(formatsToSearch); ++iFormat) { + mal_format format = formatsToSearch[iFormat]; + + wf.Format.wBitsPerSample = (WORD)mal_get_bytes_per_sample(format)*8; + wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8; + wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = /*(format == mal_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample; + if (format == mal_format_f32) { + wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } else { + wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM; + } + + for (mal_uint32 iSampleRate = 0; iSampleRate < mal_countof(g_malStandardSampleRatePriorities); ++iSampleRate) { + wf.Format.nSamplesPerSec = g_malStandardSampleRatePriorities[iSampleRate]; + + hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL); + if (SUCCEEDED(hr)) { + mal_set_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, pInfo); + found = MAL_TRUE; + break; + } + } + + if (found) { + break; + } + } + + if (!found) { + mal_IPropertyStore_Release(pProperties); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to find suitable device format for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } + } else { + mal_IPropertyStore_Release(pProperties); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve device format for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } else { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to open property store for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + return MAL_SUCCESS; +#else + // Exclusive mode not fully supported in UWP right now. + return MAL_ERROR; +#endif + } +} + +#ifdef MAL_WIN32_DESKTOP +mal_result mal_context_get_MMDevice__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IMMDevice** ppMMDevice) +{ + mal_assert(pContext != NULL); + mal_assert(ppMMDevice != NULL); + + mal_IMMDeviceEnumerator* pDeviceEnumerator; + HRESULT hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.", MAL_FAILED_TO_INIT_BACKEND); + } + + if (pDeviceID == NULL) { + hr = mal_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == mal_device_type_playback) ? mal_eRender : mal_eCapture, mal_eConsole, ppMMDevice); + } else { + hr = mal_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); + } + + mal_IMMDeviceEnumerator_Release(pDeviceEnumerator); + if (FAILED(hr)) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info_from_MMDevice__wasapi(mal_context* pContext, mal_IMMDevice* pMMDevice, mal_share_mode shareMode, mal_bool32 onlySimpleInfo, mal_device_info* pInfo) +{ + mal_assert(pContext != NULL); + mal_assert(pMMDevice != NULL); + mal_assert(pInfo != NULL); + + // ID. + LPWSTR id; + HRESULT hr = mal_IMMDevice_GetId(pMMDevice, &id); + if (SUCCEEDED(hr)) { + size_t idlen = wcslen(id); + if (idlen+1 > mal_countof(pInfo->id.wasapi)) { + mal_CoTaskMemFree(pContext, id); + mal_assert(MAL_FALSE); // NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. + return MAL_ERROR; + } + + mal_copy_memory(pInfo->id.wasapi, id, idlen * sizeof(wchar_t)); + pInfo->id.wasapi[idlen] = '\0'; + + mal_CoTaskMemFree(pContext, id); + } + + { + mal_IPropertyStore *pProperties; + hr = mal_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + PROPVARIANT var; + + // Description / Friendly Name + mal_PropVariantInit(&var); + hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_Device_FriendlyName, &var); + if (SUCCEEDED(hr)) { + WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); + mal_PropVariantClear(pContext, &var); + } + + mal_IPropertyStore_Release(pProperties); + } + } + + // Format + if (!onlySimpleInfo) { + mal_IAudioClient* pAudioClient; + hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); + if (SUCCEEDED(hr)) { + mal_result result = mal_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, shareMode, pInfo); + + mal_IAudioClient_Release(pAudioClient); + return result; + } else { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_enumerate_device_collection__wasapi(mal_context* pContext, mal_IMMDeviceCollection* pDeviceCollection, mal_device_type deviceType, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + UINT deviceCount; + HRESULT hr = mal_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); + if (FAILED(hr)) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to get playback device count.", MAL_NO_DEVICE); + } + + for (mal_uint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + + mal_IMMDevice* pMMDevice; + hr = mal_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); + if (SUCCEEDED(hr)) { + mal_result result = mal_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, mal_share_mode_shared, MAL_TRUE, &deviceInfo); // MAL_TRUE = onlySimpleInfo. + + mal_IMMDevice_Release(pMMDevice); + if (result == MAL_SUCCESS) { + mal_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + break; + } + } + } + } + + return MAL_SUCCESS; +} +#endif + +mal_result mal_context_enumerate_devices__wasapi(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + // Different enumeration for desktop and UWP. +#ifdef MAL_WIN32_DESKTOP + // Desktop + mal_IMMDeviceEnumerator* pDeviceEnumerator; + HRESULT hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + mal_IMMDeviceCollection* pDeviceCollection; + + // Playback. + hr = mal_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, mal_eRender, MAL_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); + if (SUCCEEDED(hr)) { + mal_context_enumerate_device_collection__wasapi(pContext, pDeviceCollection, mal_device_type_playback, callback, pUserData); + mal_IMMDeviceCollection_Release(pDeviceCollection); + } + + // Capture. + hr = mal_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, mal_eCapture, MAL_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); + if (SUCCEEDED(hr)) { + mal_context_enumerate_device_collection__wasapi(pContext, pDeviceCollection, mal_device_type_capture, callback, pUserData); + mal_IMMDeviceCollection_Release(pDeviceCollection); + } + + mal_IMMDeviceEnumerator_Release(pDeviceEnumerator); +#else + // UWP + // + // The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate + // over devices without using MMDevice, I'm restricting devices to defaults. + // + // Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ + if (callback) { + mal_bool32 cbResult = MAL_TRUE; + + // Playback. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + + // Capture. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } + } +#endif + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ +#ifdef MAL_WIN32_DESKTOP + mal_IMMDevice* pMMDevice = NULL; + mal_result result = mal_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); + if (result != MAL_SUCCESS) { + return result; + } + + result = mal_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, MAL_FALSE, pDeviceInfo); // MAL_FALSE = !onlySimpleInfo. + + mal_IMMDevice_Release(pMMDevice); + return result; +#else + // UWP currently only uses default devices. + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + // Not currently supporting exclusive mode on UWP. + if (shareMode == mal_share_mode_exclusive) { + return MAL_ERROR; + } + + mal_IAudioClient* pAudioClient; + mal_result result = mal_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL); + if (result != MAL_SUCCESS) { + return result; + } + + result = mal_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, shareMode, pDeviceInfo); + + mal_IAudioClient_Release(pAudioClient); + return result; +#endif +} + +void mal_device_uninit__wasapi(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + +#ifdef MAL_WIN32_DESKTOP + if (pDevice->wasapi.pDeviceEnumerator) { + ((mal_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((mal_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); + mal_IMMDeviceEnumerator_Release((mal_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); + } +#endif + + if (pDevice->wasapi.pRenderClient) { + mal_IAudioRenderClient_Release((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient); + } + if (pDevice->wasapi.pCaptureClient) { + mal_IAudioCaptureClient_Release((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + } + if (pDevice->wasapi.pAudioClient) { + mal_IAudioClient_Release((mal_IAudioClient*)pDevice->wasapi.pAudioClient); + } + + if (pDevice->wasapi.hEvent) { + CloseHandle(pDevice->wasapi.hEvent); + } + if (pDevice->wasapi.hBreakEvent) { + CloseHandle(pDevice->wasapi.hBreakEvent); + } +} + +typedef struct +{ + // Input. + mal_format formatIn; + mal_uint32 channelsIn; + mal_uint32 sampleRateIn; + mal_channel channelMapIn[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFramesIn; + mal_uint32 bufferSizeInMillisecondsIn; + mal_uint32 periodsIn; + mal_bool32 usingDefaultFormat; + mal_bool32 usingDefaultChannels; + mal_bool32 usingDefaultSampleRate; + mal_bool32 usingDefaultChannelMap; + mal_share_mode shareMode; + + // Output. + mal_IAudioClient* pAudioClient; + mal_IAudioRenderClient* pRenderClient; + mal_IAudioCaptureClient* pCaptureClient; + mal_format formatOut; + mal_uint32 channelsOut; + mal_uint32 sampleRateOut; + mal_channel channelMapOut[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFramesOut; + mal_uint32 periodsOut; + mal_bool32 exclusiveMode; + char deviceName[256]; +} mal_device_init_internal_data__wasapi; + +mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, mal_device_init_internal_data__wasapi* pData) +{ + (void)pContext; + + mal_assert(pContext != NULL); + mal_assert(pData != NULL); + + pData->pAudioClient = NULL; + pData->pRenderClient = NULL; + pData->pCaptureClient = NULL; + + + HRESULT hr; + mal_result result = MAL_SUCCESS; + const char* errorMsg = ""; + MAL_AUDCLNT_SHAREMODE shareMode = MAL_AUDCLNT_SHAREMODE_SHARED; + MAL_REFERENCE_TIME bufferDurationInMicroseconds; + mal_bool32 wasInitializedUsingIAudioClient3 = MAL_FALSE; + WAVEFORMATEXTENSIBLE wf; + +#ifdef MAL_WIN32_DESKTOP + mal_IMMDevice* pMMDevice = NULL; + result = mal_context_get_MMDevice__wasapi(pContext, type, pDeviceID, &pMMDevice); + if (result != MAL_SUCCESS) { + goto done; + } + + hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to activate device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + goto done; + } +#else + mal_IUnknown* pActivatedInterface = NULL; + result = mal_context_get_IAudioClient_UWP__wasapi(pContext, type, pDeviceID, &pData->pAudioClient, &pActivatedInterface); + if (result != MAL_SUCCESS) { goto done; } #endif - // Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. - WAVEFORMATEXTENSIBLE* pBestFormatTemp = NULL; - result = MAL_FORMAT_NOT_SUPPORTED; - if (pConfig->preferExclusiveMode) { - hr = IAudioClient_IsFormatSupported(pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL); - #ifdef MAL_WIN32_DESKTOP - if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) { - // The format isn't supported, so retrieve the actual format from the property store and try that. - IPropertyStore* pStore = NULL; - hr = IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pStore); - if (SUCCEEDED(hr)) { - PROPVARIANT prop; - PropVariantInit(&prop); - hr = IPropertyStore_GetValue(pStore, g_malPKEY_AudioEngine_DeviceFormat, &prop); - if (SUCCEEDED(hr)) { - WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData; - hr = IAudioClient_IsFormatSupported(pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); - if (SUCCEEDED(hr)) { - mal_copy_memory(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE)); - } + // Try enabling hardware offloading. + mal_IAudioClient2* pAudioClient2; + hr = mal_IAudioClient_QueryInterface(pData->pAudioClient, &MAL_IID_IAudioClient2, (void**)&pAudioClient2); + if (SUCCEEDED(hr)) { + BOOL isHardwareOffloadingSupported = 0; + hr = mal_IAudioClient2_IsOffloadCapable(pAudioClient2, MAL_AudioCategory_Other, &isHardwareOffloadingSupported); + if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { + mal_AudioClientProperties clientProperties; + mal_zero_object(&clientProperties); + clientProperties.cbSize = sizeof(clientProperties); + clientProperties.bIsOffload = 1; + clientProperties.eCategory = MAL_AudioCategory_Other; + mal_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); + } + } - mal_PropVariantClear(pDevice->pContext, &prop); + + // Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. + result = MAL_FORMAT_NOT_SUPPORTED; + if (pData->shareMode == mal_share_mode_exclusive) { + #ifdef MAL_WIN32_DESKTOP + // In exclusive mode on desktop we always use the backend's native format. + mal_IPropertyStore* pStore = NULL; + hr = mal_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pStore); + if (SUCCEEDED(hr)) { + PROPVARIANT prop; + mal_PropVariantInit(&prop); + hr = mal_IPropertyStore_GetValue(pStore, &MAL_PKEY_AudioEngine_DeviceFormat, &prop); + if (SUCCEEDED(hr)) { + WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData; + hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pData->pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); + if (SUCCEEDED(hr)) { + mal_copy_memory(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE)); } - IPropertyStore_Release(pStore); + mal_PropVariantClear(pContext, &prop); } + + mal_IPropertyStore_Release(pStore); } + #else + // I do not know how to query the device's native format on UWP so for now I'm just disabling support for + // exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() + // until you find one that works. + // + // TODO: Add support for exclusive mode to UWP. + hr = S_FALSE; #endif if (hr == S_OK) { - shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; + shareMode = MAL_AUDCLNT_SHAREMODE_EXCLUSIVE; result = MAL_SUCCESS; } } // Fall back to shared mode if necessary. if (result != MAL_SUCCESS) { - hr = IAudioClient_IsFormatSupported(pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&wf, (WAVEFORMATEX**)&pBestFormatTemp); - if (hr != S_OK && hr != S_FALSE) { - hr = IAudioClient_GetMixFormat(pDevice->wasapi.pAudioClient, (WAVEFORMATEX**)&pBestFormatTemp); - if (hr != S_OK) { - result = MAL_WASAPI_FAILED_TO_FIND_BEST_FORMAT; - } else { - result = MAL_SUCCESS; - } + // In shared mode we are always using the format reported by the operating system. + WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; + hr = mal_IAudioClient_GetMixFormat((mal_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat); + if (hr != S_OK) { + result = MAL_FORMAT_NOT_SUPPORTED; } else { + mal_copy_memory(&wf, pNativeFormat, sizeof(wf)); result = MAL_SUCCESS; } - shareMode = AUDCLNT_SHAREMODE_SHARED; + mal_CoTaskMemFree(pContext, pNativeFormat); + + shareMode = MAL_AUDCLNT_SHAREMODE_SHARED; } // Return an error if we still haven't found a format. if (result != MAL_SUCCESS) { - errorMsg = "[WASAPI] Failed to find best device mix format.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; + errorMsg = "[WASAPI] Failed to find best device mix format.", result = MAL_FORMAT_NOT_SUPPORTED; goto done; } - if (pBestFormatTemp != NULL) { - mal_copy_memory(&wf, pBestFormatTemp, sizeof(wf)); - mal_CoTaskMemFree(pDevice->pContext, pBestFormatTemp); - } - - - REFERENCE_TIME bufferDurationInMicroseconds = ((mal_uint64)pDevice->bufferSizeInFrames * 1000 * 1000) / pConfig->sampleRate; - - if (mal_is_guid_equal(wf.SubFormat, MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { - pDevice->internalFormat = mal_format_f32; - } else { - if (wf.Format.wBitsPerSample == 32) { - pDevice->internalFormat = mal_format_s32; - } else if (wf.Format.wBitsPerSample == 24) { - pDevice->internalFormat = mal_format_s24; - } else if (wf.Format.wBitsPerSample == 16) { - pDevice->internalFormat = mal_format_s16; - } else if (wf.Format.wBitsPerSample == 8) { - pDevice->internalFormat = mal_format_u8; - } else { - errorMsg = "[WASAPI] Device's native format is not supported.", result = MAL_FORMAT_NOT_SUPPORTED; - goto done; - } - } - - pDevice->internalChannels = wf.Format.nChannels; - pDevice->internalSampleRate = wf.Format.nSamplesPerSec; + pData->formatOut = mal_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf); + pData->channelsOut = wf.Format.nChannels; + pData->sampleRateOut = wf.Format.nSamplesPerSec; // Get the internal channel map based on the channel mask. - mal_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); + mal_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); - // Slightly different initialization for shared and exclusive modes. - if (shareMode == AUDCLNT_SHAREMODE_SHARED) { - // Shared. - REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10; - hr = IAudioClient_Initialize(pDevice->wasapi.pAudioClient, shareMode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL); - if (FAILED(hr)) { - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MAL_ACCESS_DENIED; - } else { - errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE; - } + // If we're using a default buffer size we need to calculate it based on the efficiency of the system. + pData->periodsOut = pData->periodsIn; + pData->bufferSizeInFramesOut = pData->bufferSizeInFramesIn; + if (pData->bufferSizeInFramesOut == 0) { + pData->bufferSizeInFramesOut = mal_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, pData->sampleRateOut); + } - goto done; - } - } else { + bufferDurationInMicroseconds = ((mal_uint64)pData->bufferSizeInFramesOut * 1000 * 1000) / pData->sampleRateOut; + + + // Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. + if (shareMode == MAL_AUDCLNT_SHAREMODE_EXCLUSIVE) { // Exclusive. - REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10; - hr = IAudioClient_Initialize(pDevice->wasapi.pAudioClient, shareMode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); - if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { + MAL_REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10; + + // If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing + // it and trying it again. + hr = E_FAIL; + for (;;) { + hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pData->pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + if (hr == MAL_AUDCLNT_E_INVALID_DEVICE_PERIOD) { + if (bufferDuration > 500*10000) { + break; + } else { + if (bufferDuration == 0) { // <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. + break; + } + + bufferDuration = bufferDuration * 2; + continue; + } + } else { + break; + } + } + + if (hr == MAL_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { UINT bufferSizeInFrames; - hr = IAudioClient_GetBufferSize(pDevice->wasapi.pAudioClient, &bufferSizeInFrames); + hr = mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); if (SUCCEEDED(hr)) { - bufferDuration = (REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5); + bufferDuration = (MAL_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5); // Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! - IAudioClient_Release(pDevice->wasapi.pAudioClient); + mal_IAudioClient_Release((mal_IAudioClient*)pData->pAudioClient); #ifdef MAL_WIN32_DESKTOP - hr = IMMDevice_Activate(pMMDevice, g_malIID_IAudioClient, CLSCTX_ALL, NULL, &pDevice->wasapi.pAudioClient); + hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); #else - hr = pActivatedInterface->QueryInterface(g_malIID_IAudioClient, &pDevice->wasapi.pAudioClient); + hr = mal_IUnknown_QueryInterface(pActivatedInterface, &MAL_IID_IAudioClient, (void**)&pData->pAudioClient); #endif if (SUCCEEDED(hr)) { - hr = IAudioClient_Initialize(pDevice->wasapi.pAudioClient, shareMode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pData->pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); } } } if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE; + // Failed to initialize in exclusive mode. We don't return an error here, but instead fall back to shared mode. + shareMode = MAL_AUDCLNT_SHAREMODE_SHARED; + } + } + + if (shareMode == MAL_AUDCLNT_SHAREMODE_SHARED) { + // Shared. + + // Low latency shared mode via IAudioClient3. + mal_IAudioClient3* pAudioClient3 = NULL; + hr = mal_IAudioClient_QueryInterface(pData->pAudioClient, &MAL_IID_IAudioClient3, (void**)&pAudioClient3); + if (SUCCEEDED(hr)) { + UINT32 defaultPeriodInFrames; + UINT32 fundamentalPeriodInFrames; + UINT32 minPeriodInFrames; + UINT32 maxPeriodInFrames; + hr = mal_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); + if (SUCCEEDED(hr)) { + UINT32 desiredPeriodInFrames = pData->bufferSizeInFramesOut / pData->periodsOut; + + // Make sure the period size is a multiple of fundamentalPeriodInFrames. + desiredPeriodInFrames = desiredPeriodInFrames / fundamentalPeriodInFrames; + desiredPeriodInFrames = desiredPeriodInFrames * fundamentalPeriodInFrames; + + // The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. + desiredPeriodInFrames = mal_clamp(desiredPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); + + hr = mal_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, desiredPeriodInFrames, (WAVEFORMATEX*)&wf, NULL); + if (SUCCEEDED(hr)) { + wasInitializedUsingIAudioClient3 = MAL_TRUE; + pData->bufferSizeInFramesOut = desiredPeriodInFrames * pData->periodsOut; + } + } + + mal_IAudioClient3_Release(pAudioClient3); + pAudioClient3 = NULL; + } + + // If we don't have an IAudioClient3 then we need to use the normal initialization routine. + if (!wasInitializedUsingIAudioClient3) { + MAL_REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10; + hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pData->pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL); + if (FAILED(hr)) { + if (hr == E_ACCESSDENIED) { + errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MAL_ACCESS_DENIED; + } else if (hr == MAL_AUDCLNT_E_DEVICE_IN_USE) { + errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MAL_DEVICE_BUSY; + } else { + errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + goto done; + } + } + } + + if (!wasInitializedUsingIAudioClient3) { + hr = mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pData->pAudioClient, &pData->bufferSizeInFramesOut); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; goto done; } } - hr = IAudioClient_GetBufferSize(pDevice->wasapi.pAudioClient, &pDevice->bufferSizeInFrames); - if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE; - goto done; - } - if (type == mal_device_type_playback) { - hr = IAudioClient_GetService((IAudioClient*)pDevice->wasapi.pAudioClient, g_malIID_IAudioRenderClient, &pDevice->wasapi.pRenderClient); + hr = mal_IAudioClient_GetService((mal_IAudioClient*)pData->pAudioClient, &MAL_IID_IAudioRenderClient, (void**)&pData->pRenderClient); } else { - hr = IAudioClient_GetService((IAudioClient*)pDevice->wasapi.pAudioClient, g_malIID_IAudioCaptureClient, &pDevice->wasapi.pCaptureClient); + hr = mal_IAudioClient_GetService((mal_IAudioClient*)pData->pAudioClient, &MAL_IID_IAudioCaptureClient, (void**)&pData->pCaptureClient); } if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to get audio client service.", result = MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE; + errorMsg = "[WASAPI] Failed to get audio client service.", result = MAL_API_NOT_FOUND; goto done; } - if (shareMode == AUDCLNT_SHAREMODE_SHARED) { - pDevice->exclusiveMode = MAL_FALSE; - } else /*if (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)*/ { - pDevice->exclusiveMode = MAL_TRUE; + if (shareMode == MAL_AUDCLNT_SHAREMODE_SHARED) { + pData->exclusiveMode = MAL_FALSE; + } else /*if (shareMode == MAL_AUDCLNT_SHAREMODE_EXCLUSIVE)*/ { + pData->exclusiveMode = MAL_TRUE; } + // Grab the name of the device. +#ifdef MAL_WIN32_DESKTOP + mal_IPropertyStore *pProperties; + hr = mal_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + PROPVARIANT varName; + mal_PropVariantInit(&varName); + hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_Device_FriendlyName, &varName); + if (SUCCEEDED(hr)) { + WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); + mal_PropVariantClear(pContext, &varName); + } + + mal_IPropertyStore_Release(pProperties); + } +#endif + +done: + // Clean up. +#ifdef MAL_WIN32_DESKTOP + if (pMMDevice != NULL) { + mal_IMMDevice_Release(pMMDevice); + } +#else + if (pActivatedInterface != NULL) { + mal_IUnknown_Release(pActivatedInterface); + } +#endif + + if (result != MAL_SUCCESS) { + if (pData->pRenderClient) { + mal_IAudioRenderClient_Release((mal_IAudioRenderClient*)pData->pRenderClient); + pData->pRenderClient = NULL; + } + if (pData->pCaptureClient) { + mal_IAudioCaptureClient_Release((mal_IAudioCaptureClient*)pData->pCaptureClient); + pData->pCaptureClient = NULL; + } + if (pData->pAudioClient) { + mal_IAudioClient_Release((mal_IAudioClient*)pData->pAudioClient); + pData->pAudioClient = NULL; + } + + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, errorMsg, result); + } else { + return MAL_SUCCESS; + } +} + +mal_result mal_device_reinit__wasapi(mal_device* pDevice) +{ + mal_device_init_internal_data__wasapi data; + data.formatIn = pDevice->format; + data.channelsIn = pDevice->channels; + data.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(data.channelMapIn, pDevice->channelMap, sizeof(pDevice->channelMap)); + data.bufferSizeInFramesIn = pDevice->bufferSizeInFrames; + data.bufferSizeInMillisecondsIn = pDevice->bufferSizeInMilliseconds; + data.periodsIn = pDevice->periods; + data.usingDefaultFormat = pDevice->usingDefaultFormat; + data.usingDefaultChannels = pDevice->usingDefaultChannels; + data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; + data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; + data.shareMode = pDevice->initConfig.shareMode; + mal_result result = mal_device_init_internal__wasapi(pDevice->pContext, pDevice->type, NULL, &data); + if (result != MAL_SUCCESS) { + return result; + } + + // At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. + if (pDevice->wasapi.pRenderClient) { + mal_IAudioRenderClient_Release((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient); + pDevice->wasapi.pRenderClient = NULL; + } + if (pDevice->wasapi.pCaptureClient) { + mal_IAudioCaptureClient_Release((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + if (pDevice->wasapi.pAudioClient) { + mal_IAudioClient_Release((mal_IAudioClient*)pDevice->wasapi.pAudioClient); + pDevice->wasapi.pAudioClient = NULL; + } + + pDevice->wasapi.pAudioClient = data.pAudioClient; + pDevice->wasapi.pRenderClient = data.pRenderClient; + pDevice->wasapi.pCaptureClient = data.pCaptureClient; + + pDevice->internalFormat = data.formatOut; + pDevice->internalChannels = data.channelsOut; + pDevice->internalSampleRate = data.sampleRateOut; + mal_copy_memory(pDevice->internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->bufferSizeInFrames = data.bufferSizeInFramesOut; + pDevice->periods = data.periodsOut; + pDevice->exclusiveMode = data.exclusiveMode; + mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName); + + mal_IAudioClient_SetEventHandle((mal_IAudioClient*)pDevice->wasapi.pAudioClient, pDevice->wasapi.hEvent); + + return MAL_SUCCESS; +} + +mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + (void)pConfig; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->wasapi); + + mal_result result = MAL_SUCCESS; + const char* errorMsg = ""; + + mal_device_init_internal_data__wasapi data; + data.formatIn = pDevice->format; + data.channelsIn = pDevice->channels; + data.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(data.channelMapIn, pDevice->channelMap, sizeof(pDevice->channelMap)); + data.bufferSizeInFramesIn = pDevice->bufferSizeInFrames; + data.bufferSizeInMillisecondsIn = pDevice->bufferSizeInMilliseconds; + data.periodsIn = pDevice->periods; + data.usingDefaultFormat = pDevice->usingDefaultFormat; + data.usingDefaultChannels = pDevice->usingDefaultChannels; + data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; + data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; + data.shareMode = pDevice->initConfig.shareMode; + result = mal_device_init_internal__wasapi(pDevice->pContext, type, pDeviceID, &data); + if (result != MAL_SUCCESS) { + return result; + } + + pDevice->wasapi.pAudioClient = data.pAudioClient; + pDevice->wasapi.pRenderClient = data.pRenderClient; + pDevice->wasapi.pCaptureClient = data.pCaptureClient; + + pDevice->internalFormat = data.formatOut; + pDevice->internalChannels = data.channelsOut; + pDevice->internalSampleRate = data.sampleRateOut; + mal_copy_memory(pDevice->internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->bufferSizeInFrames = data.bufferSizeInFramesOut; + pDevice->periods = data.periodsOut; + pDevice->exclusiveMode = data.exclusiveMode; + mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName); + + + + // We need to get notifications of when the default device changes. We do this through a device enumerator by + // registering a IMMNotificationClient with it. We only care about this if it's the default device. +#ifdef MAL_WIN32_DESKTOP + mal_IMMDeviceEnumerator* pDeviceEnumerator; + HRESULT hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to create device enumerator.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + goto done; + } + + pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_malNotificationCientVtbl; + pDevice->wasapi.notificationClient.counter = 1; + pDevice->wasapi.notificationClient.pDevice = pDevice; + + hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); + if (SUCCEEDED(hr)) { + pDevice->wasapi.pDeviceEnumerator = (mal_ptr)pDeviceEnumerator; + } else { + // Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. + mal_IMMDeviceEnumerator_Release(pDeviceEnumerator); + } +#endif + + // We need to create and set the event for event-driven mode. This event is signalled whenever a new chunk of audio // data needs to be written or read from the device. pDevice->wasapi.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); @@ -3943,14 +6935,14 @@ static mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type goto done; } - IAudioClient_SetEventHandle(pDevice->wasapi.pAudioClient, pDevice->wasapi.hEvent); + mal_IAudioClient_SetEventHandle((mal_IAudioClient*)pDevice->wasapi.pAudioClient, pDevice->wasapi.hEvent); // When the device is playing the worker thread will be waiting on a bunch of notification events. To return from // this wait state we need to signal a special event. - pDevice->wasapi.hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (pDevice->wasapi.hStopEvent == NULL) { - errorMsg = "[WASAPI] Failed to create stop event for main loop break notification.", result = MAL_FAILED_TO_CREATE_EVENT; + pDevice->wasapi.hBreakEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pDevice->wasapi.hBreakEvent == NULL) { + errorMsg = "[WASAPI] Failed to create break event for main loop break notification.", result = MAL_FAILED_TO_CREATE_EVENT; goto done; } @@ -3958,125 +6950,113 @@ static mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type done: // Clean up. -#ifdef MAL_WIN32_DESKTOP - if (pMMDevice != NULL) { - IMMDevice_Release(pMMDevice); - } -#else - if (pAsyncOp != NULL) { - IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); - } -#endif - if (result != MAL_SUCCESS) { mal_device_uninit__wasapi(pDevice); - return mal_post_error(pDevice, errorMsg, result); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, errorMsg, result); } else { return MAL_SUCCESS; } } -static mal_result mal_device__start_backend__wasapi(mal_device* pDevice) +mal_result mal_device__start_backend__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); // Playback devices need to have an initial chunk of data loaded. if (pDevice->type == mal_device_type_playback) { BYTE* pData; - HRESULT hr = IAudioRenderClient_GetBuffer(pDevice->wasapi.pRenderClient, pDevice->bufferSizeInFrames, &pData); + HRESULT hr = mal_IAudioRenderClient_GetBuffer((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->bufferSizeInFrames, &pData); if (FAILED(hr)) { - return mal_post_error(pDevice, "[WASAPI] Failed to retrieve buffer from internal playback device.", MAL_WASAPI_FAILED_TO_GET_INTERNAL_BUFFER); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve buffer from internal playback device.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); } mal_device__read_frames_from_client(pDevice, pDevice->bufferSizeInFrames, pData); - hr = IAudioRenderClient_ReleaseBuffer(pDevice->wasapi.pRenderClient, pDevice->bufferSizeInFrames, 0); + hr = mal_IAudioRenderClient_ReleaseBuffer((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->bufferSizeInFrames, 0); if (FAILED(hr)) { - return mal_post_error(pDevice, "[WASAPI] Failed to release internal buffer for playback device.", MAL_WASAPI_FAILED_TO_RELEASE_INTERNAL_BUFFER); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer for playback device.", MAL_FAILED_TO_UNMAP_DEVICE_BUFFER); } } - HRESULT hr = IAudioClient_Start(pDevice->wasapi.pAudioClient); + HRESULT hr = mal_IAudioClient_Start((mal_IAudioClient*)pDevice->wasapi.pAudioClient); if (FAILED(hr)) { - return mal_post_error(pDevice, "[WASAPI] Failed to start internal device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } return MAL_SUCCESS; } -static mal_result mal_device__stop_backend__wasapi(mal_device* pDevice) +mal_result mal_device__stop_backend__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); - HRESULT hr = IAudioClient_Stop(pDevice->wasapi.pAudioClient); + if (pDevice->wasapi.pAudioClient == NULL) { + return MAL_DEVICE_NOT_INITIALIZED; + } + + HRESULT hr = mal_IAudioClient_Stop((mal_IAudioClient*)pDevice->wasapi.pAudioClient); if (FAILED(hr)) { - return mal_post_error(pDevice, "[WASAPI] Failed to stop internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } + + // The client needs to be reset or else we won't be able to resume it again. + hr = mal_IAudioClient_Reset((mal_IAudioClient*)pDevice->wasapi.pAudioClient); + if (FAILED(hr)) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); } return MAL_SUCCESS; } -static mal_result mal_device__break_main_loop__wasapi(mal_device* pDevice) +mal_result mal_device__break_main_loop__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); // The main loop will be waiting on a bunch of events via the WaitForMultipleObjects() API. One of those events // is a special event we use for forcing that function to return. pDevice->wasapi.breakFromMainLoop = MAL_TRUE; - SetEvent(pDevice->wasapi.hStopEvent); + SetEvent(pDevice->wasapi.hBreakEvent); return MAL_SUCCESS; } -static mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice) +mal_result mal_device__get_available_frames__wasapi(mal_device* pDevice, mal_uint32* pFrameCount) { mal_assert(pDevice != NULL); + mal_assert(pFrameCount != NULL); + + *pFrameCount = 0; -#if 1 - if (pDevice->type == mal_device_type_playback) { - UINT32 paddingFramesCount; - HRESULT hr = IAudioClient_GetCurrentPadding(pDevice->wasapi.pAudioClient, &paddingFramesCount); - if (FAILED(hr)) { - return 0; - } - - if (pDevice->exclusiveMode) { - return paddingFramesCount; - } else { - return pDevice->bufferSizeInFrames - paddingFramesCount; - } - } else { - UINT32 framesAvailable; - HRESULT hr = IAudioCaptureClient_GetNextPacketSize(pDevice->wasapi.pCaptureClient, &framesAvailable); - if (FAILED(hr)) { - return 0; - } - - return framesAvailable; - } -#else - UINT32 paddingFramesCount; - HRESULT hr = IAudioClient_GetCurrentPadding(pDevice->wasapi.pAudioClient, &paddingFramesCount); + mal_uint32 paddingFramesCount; + HRESULT hr = mal_IAudioClient_GetCurrentPadding((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &paddingFramesCount); if (FAILED(hr)) { - return 0; + return MAL_DEVICE_UNAVAILABLE; } + // Slightly different rules for exclusive and shared modes. if (pDevice->exclusiveMode) { - return paddingFramesCount; + *pFrameCount = paddingFramesCount; } else { - return pDevice->bufferSizeInFrames - paddingFramesCount; + if (pDevice->type == mal_device_type_playback) { + *pFrameCount = pDevice->bufferSizeInFrames - paddingFramesCount; + } else { + *pFrameCount = paddingFramesCount; + } } -#endif + + return MAL_SUCCESS; } -static mal_uint32 mal_device__wait_for_frames__wasapi(mal_device* pDevice) +mal_result mal_device__wait_for_frames__wasapi(mal_device* pDevice, mal_uint32* pFrameCount) { mal_assert(pDevice != NULL); + mal_result result; + while (!pDevice->wasapi.breakFromMainLoop) { // Wait for a buffer to become available or for the stop event to be signalled. HANDLE hEvents[2]; hEvents[0] = (HANDLE)pDevice->wasapi.hEvent; - hEvents[1] = (HANDLE)pDevice->wasapi.hStopEvent; + hEvents[1] = (HANDLE)pDevice->wasapi.hBreakEvent; if (WaitForMultipleObjects(mal_countof(hEvents), hEvents, FALSE, INFINITE) == WAIT_FAILED) { break; } @@ -4087,66 +7067,122 @@ static mal_uint32 mal_device__wait_for_frames__wasapi(mal_device* pDevice) break; } - mal_uint32 framesAvailable = mal_device__get_available_frames__wasapi(pDevice); - if (framesAvailable > 0) { - return framesAvailable; + // Make sure we break from the main loop if requested from an external factor. + if (pDevice->wasapi.breakFromMainLoop) { + break; + } + + // We may want to reinitialize the device. Only do this if this device is the default. + mal_bool32 needDeviceReinit = MAL_FALSE; + + mal_bool32 hasDefaultDeviceChanged = pDevice->wasapi.hasDefaultDeviceChanged; + if (hasDefaultDeviceChanged && pDevice->isDefaultDevice) { + needDeviceReinit = MAL_TRUE; + } + + if (!needDeviceReinit) { + result = mal_device__get_available_frames__wasapi(pDevice, pFrameCount); + if (result != MAL_SUCCESS) { + if (!pDevice->exclusiveMode) { + needDeviceReinit = MAL_TRUE; + } else { + return result; + } + } + } + + + mal_atomic_exchange_32(&pDevice->wasapi.hasDefaultDeviceChanged, MAL_FALSE); + + // Here is where the device is re-initialized if required. + if (needDeviceReinit) { + #ifdef MAL_DEBUG_OUTPUT + printf("=== CHANGING DEVICE ===\n"); + #endif + + if (pDevice->pContext->onDeviceReinit) { + mal_result reinitResult = pDevice->pContext->onDeviceReinit(pDevice); + if (reinitResult != MAL_SUCCESS) { + return reinitResult; + } + + mal_device__post_init_setup(pDevice); + + // Start playing the device again, and then continue the loop from the top. + if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { + if (pDevice->pContext->onDeviceStart) { + pDevice->pContext->onDeviceStart(pDevice); + } + continue; + } + } + } + + + if (*pFrameCount > 0) { + return MAL_SUCCESS; } } // We'll get here if the loop was terminated. Just return whatever's available. - return mal_device__get_available_frames__wasapi(pDevice); + return mal_device__get_available_frames__wasapi(pDevice, pFrameCount); } -static mal_result mal_device__main_loop__wasapi(mal_device* pDevice) +mal_result mal_device__main_loop__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); - // Make sure the stop event is not signaled to ensure we don't end up immediately returning from WaitForMultipleObjects(). - ResetEvent(pDevice->wasapi.hStopEvent); + // Make sure the break event is not signaled to ensure we don't end up immediately returning from WaitForMultipleObjects(). + ResetEvent(pDevice->wasapi.hBreakEvent); pDevice->wasapi.breakFromMainLoop = MAL_FALSE; while (!pDevice->wasapi.breakFromMainLoop) { - mal_uint32 framesAvailable = mal_device__wait_for_frames__wasapi(pDevice); + mal_uint32 framesAvailable; + mal_result result = mal_device__wait_for_frames__wasapi(pDevice, &framesAvailable); + if (result != MAL_SUCCESS) { + return result; + } + if (framesAvailable == 0) { continue; } // If it's a playback device, don't bother grabbing more data if the device is being stopped. if (pDevice->wasapi.breakFromMainLoop && pDevice->type == mal_device_type_playback) { - return MAL_FALSE; + return MAL_SUCCESS; } if (pDevice->type == mal_device_type_playback) { BYTE* pData; - HRESULT hr = IAudioRenderClient_GetBuffer(pDevice->wasapi.pRenderClient, framesAvailable, &pData); + HRESULT hr = mal_IAudioRenderClient_GetBuffer((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailable, &pData); if (FAILED(hr)) { - return mal_post_error(pDevice, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for sending new data to the device.", MAL_WASAPI_FAILED_TO_GET_INTERNAL_BUFFER); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for sending new data to the device.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); } mal_device__read_frames_from_client(pDevice, framesAvailable, pData); - hr = IAudioRenderClient_ReleaseBuffer(pDevice->wasapi.pRenderClient, framesAvailable, 0); + hr = mal_IAudioRenderClient_ReleaseBuffer((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailable, 0); if (FAILED(hr)) { - return mal_post_error(pDevice, "[WASAPI] Failed to release internal buffer from playback device in preparation for sending new data to the device.", MAL_WASAPI_FAILED_TO_RELEASE_INTERNAL_BUFFER); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device in preparation for sending new data to the device.", MAL_FAILED_TO_UNMAP_DEVICE_BUFFER); } } else { - UINT32 framesRemaining = framesAvailable; + mal_uint32 framesRemaining = framesAvailable; while (framesRemaining > 0) { BYTE* pData; - UINT32 framesToSend; + mal_uint32 framesToSend; DWORD flags; - HRESULT hr = IAudioCaptureClient_GetBuffer(pDevice->wasapi.pCaptureClient, &pData, &framesToSend, &flags, NULL, NULL); + HRESULT hr = mal_IAudioCaptureClient_GetBuffer((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, &pData, &framesToSend, &flags, NULL, NULL); if (FAILED(hr)) { - mal_post_error(pDevice, "[WASAPI] WARNING: Failed to retrieve internal buffer from capture device in preparation for sending new data to the client.", MAL_WASAPI_FAILED_TO_GET_INTERNAL_BUFFER); + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] WARNING: Failed to retrieve internal buffer from capture device in preparation for sending new data to the client.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); break; } - if (hr != AUDCLNT_S_BUFFER_EMPTY) { + if (hr != MAL_AUDCLNT_S_BUFFER_EMPTY) { mal_device__send_frames_to_client(pDevice, framesToSend, pData); - hr = IAudioCaptureClient_ReleaseBuffer(pDevice->wasapi.pCaptureClient, framesToSend); + hr = mal_IAudioCaptureClient_ReleaseBuffer((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, framesToSend); if (FAILED(hr)) { - mal_post_error(pDevice, "[WASAPI] WARNING: Failed to release internal buffer from capture device in preparation for sending new data to the client.", MAL_WASAPI_FAILED_TO_RELEASE_INTERNAL_BUFFER); + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] WARNING: Failed to release internal buffer from capture device in preparation for sending new data to the client.", MAL_FAILED_TO_UNMAP_DEVICE_BUFFER); break; } @@ -4162,6 +7198,57 @@ static mal_result mal_device__main_loop__wasapi(mal_device* pDevice) return MAL_SUCCESS; } + +mal_result mal_context_uninit__wasapi(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_wasapi); + (void)pContext; + + return MAL_SUCCESS; +} + +mal_result mal_context_init__wasapi(mal_context* pContext) +{ + mal_assert(pContext != NULL); + (void)pContext; + + mal_result result = MAL_SUCCESS; + +#ifdef MAL_WIN32_DESKTOP + // WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven + // exclusive mode does not work until SP1. + mal_OSVERSIONINFOEXW osvi; + mal_zero_object(&osvi); + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_VISTA); + osvi.dwMinorVersion = LOBYTE(_WIN32_WINNT_VISTA); + osvi.wServicePackMajor = 1; + if (VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL))) { + result = MAL_SUCCESS; + } else { + result = MAL_NO_BACKEND; + } +#endif + + if (result != MAL_SUCCESS) { + return result; + } + + pContext->onUninit = mal_context_uninit__wasapi; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__wasapi; + pContext->onEnumDevices = mal_context_enumerate_devices__wasapi; + pContext->onGetDeviceInfo = mal_context_get_device_info__wasapi; + pContext->onDeviceInit = mal_device_init__wasapi; + pContext->onDeviceUninit = mal_device_uninit__wasapi; + pContext->onDeviceReinit = mal_device_reinit__wasapi; + pContext->onDeviceStart = mal_device__start_backend__wasapi; + pContext->onDeviceStop = mal_device__stop_backend__wasapi; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__wasapi; + pContext->onDeviceMainLoop = mal_device__main_loop__wasapi; + + return result; +} #endif /////////////////////////////////////////////////////////////////////////////// @@ -4170,44 +7257,752 @@ static mal_result mal_device__main_loop__wasapi(mal_device* pDevice) // /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_HAS_DSOUND -#include +//#include -#if 0 // MAL_GUID_NULL is not currently used, but leaving it here in case I need to add it back again. -static GUID MAL_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -#endif -static GUID MAL_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}}; -static GUID MAL_GUID_IID_IDirectSoundCaptureBuffer = {0xb0210782, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}}; +GUID MAL_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}}; -typedef HRESULT (WINAPI * mal_DirectSoundCreateProc)(const GUID* pcGuidDevice, LPDIRECTSOUND *ppDS8, LPUNKNOWN pUnkOuter); -typedef HRESULT (WINAPI * mal_DirectSoundEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext); -typedef HRESULT (WINAPI * mal_DirectSoundCaptureCreateProc)(const GUID* pcGuidDevice, LPDIRECTSOUNDCAPTURE *ppDSC8, LPUNKNOWN pUnkOuter); -typedef HRESULT (WINAPI * mal_DirectSoundCaptureEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext); +// mini_al only uses priority or exclusive modes. +#define MAL_DSSCL_NORMAL 1 +#define MAL_DSSCL_PRIORITY 2 +#define MAL_DSSCL_EXCLUSIVE 3 +#define MAL_DSSCL_WRITEPRIMARY 4 -static HMODULE mal_open_dsound_dll() +#define MAL_DSCAPS_PRIMARYMONO 0x00000001 +#define MAL_DSCAPS_PRIMARYSTEREO 0x00000002 +#define MAL_DSCAPS_PRIMARY8BIT 0x00000004 +#define MAL_DSCAPS_PRIMARY16BIT 0x00000008 +#define MAL_DSCAPS_CONTINUOUSRATE 0x00000010 +#define MAL_DSCAPS_EMULDRIVER 0x00000020 +#define MAL_DSCAPS_CERTIFIED 0x00000040 +#define MAL_DSCAPS_SECONDARYMONO 0x00000100 +#define MAL_DSCAPS_SECONDARYSTEREO 0x00000200 +#define MAL_DSCAPS_SECONDARY8BIT 0x00000400 +#define MAL_DSCAPS_SECONDARY16BIT 0x00000800 + +#define MAL_DSBCAPS_PRIMARYBUFFER 0x00000001 +#define MAL_DSBCAPS_STATIC 0x00000002 +#define MAL_DSBCAPS_LOCHARDWARE 0x00000004 +#define MAL_DSBCAPS_LOCSOFTWARE 0x00000008 +#define MAL_DSBCAPS_CTRL3D 0x00000010 +#define MAL_DSBCAPS_CTRLFREQUENCY 0x00000020 +#define MAL_DSBCAPS_CTRLPAN 0x00000040 +#define MAL_DSBCAPS_CTRLVOLUME 0x00000080 +#define MAL_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 +#define MAL_DSBCAPS_CTRLFX 0x00000200 +#define MAL_DSBCAPS_STICKYFOCUS 0x00004000 +#define MAL_DSBCAPS_GLOBALFOCUS 0x00008000 +#define MAL_DSBCAPS_GETCURRENTPOSITION2 0x00010000 +#define MAL_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 +#define MAL_DSBCAPS_LOCDEFER 0x00040000 +#define MAL_DSBCAPS_TRUEPLAYPOSITION 0x00080000 + +#define MAL_DSBPLAY_LOOPING 0x00000001 +#define MAL_DSBPLAY_LOCHARDWARE 0x00000002 +#define MAL_DSBPLAY_LOCSOFTWARE 0x00000004 +#define MAL_DSBPLAY_TERMINATEBY_TIME 0x00000008 +#define MAL_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 +#define MAL_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 + +#define MAL_DSCBSTART_LOOPING 0x00000001 + +typedef struct { - return LoadLibraryW(L"dsound.dll"); -} + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + WAVEFORMATEX* lpwfxFormat; + GUID guid3DAlgorithm; +} MAL_DSBUFFERDESC; -static void mal_close_dsound_dll(HMODULE hModule) +typedef struct { - FreeLibrary(hModule); + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + WAVEFORMATEX* lpwfxFormat; + DWORD dwFXCount; + void* lpDSCFXDesc; // <-- mini_al doesn't use this, so set to void*. +} MAL_DSCBUFFERDESC; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwMinSecondarySampleRate; + DWORD dwMaxSecondarySampleRate; + DWORD dwPrimaryBuffers; + DWORD dwMaxHwMixingAllBuffers; + DWORD dwMaxHwMixingStaticBuffers; + DWORD dwMaxHwMixingStreamingBuffers; + DWORD dwFreeHwMixingAllBuffers; + DWORD dwFreeHwMixingStaticBuffers; + DWORD dwFreeHwMixingStreamingBuffers; + DWORD dwMaxHw3DAllBuffers; + DWORD dwMaxHw3DStaticBuffers; + DWORD dwMaxHw3DStreamingBuffers; + DWORD dwFreeHw3DAllBuffers; + DWORD dwFreeHw3DStaticBuffers; + DWORD dwFreeHw3DStreamingBuffers; + DWORD dwTotalHwMemBytes; + DWORD dwFreeHwMemBytes; + DWORD dwMaxContigFreeHwMemBytes; + DWORD dwUnlockTransferRateHwBuffers; + DWORD dwPlayCpuOverheadSwBuffers; + DWORD dwReserved1; + DWORD dwReserved2; +} MAL_DSCAPS; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwUnlockTransferRate; + DWORD dwPlayCpuOverhead; +} MAL_DSBCAPS; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwFormats; + DWORD dwChannels; +} MAL_DSCCAPS; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; +} MAL_DSCBCAPS; + +typedef struct +{ + DWORD dwOffset; + HANDLE hEventNotify; +} MAL_DSBPOSITIONNOTIFY; + +typedef struct mal_IDirectSound mal_IDirectSound; +typedef struct mal_IDirectSoundBuffer mal_IDirectSoundBuffer; +typedef struct mal_IDirectSoundCapture mal_IDirectSoundCapture; +typedef struct mal_IDirectSoundCaptureBuffer mal_IDirectSoundCaptureBuffer; +typedef struct mal_IDirectSoundNotify mal_IDirectSoundNotify; + + +// COM objects. The way these work is that you have a vtable (a list of function pointers, kind of +// like how C++ works internally), and then you have a structure with a single member, which is a +// pointer to the vtable. The vtable is where the methods of the object are defined. Methods need +// to be in a specific order, and parent classes need to have their methods declared first. + +// IDirectSound +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IDirectSound* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IDirectSound* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IDirectSound* pThis); + + // IDirectSound + HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (mal_IDirectSound* pThis, const MAL_DSBUFFERDESC* pDSBufferDesc, mal_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); + HRESULT (STDMETHODCALLTYPE * GetCaps) (mal_IDirectSound* pThis, MAL_DSCAPS* pDSCaps); + HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(mal_IDirectSound* pThis, mal_IDirectSoundBuffer* pDSBufferOriginal, mal_IDirectSoundBuffer** ppDSBufferDuplicate); + HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (mal_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); + HRESULT (STDMETHODCALLTYPE * Compact) (mal_IDirectSound* pThis); + HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (mal_IDirectSound* pThis, DWORD* pSpeakerConfig); + HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (mal_IDirectSound* pThis, DWORD dwSpeakerConfig); + HRESULT (STDMETHODCALLTYPE * Initialize) (mal_IDirectSound* pThis, const GUID* pGuidDevice); +} mal_IDirectSoundVtbl; +struct mal_IDirectSound +{ + mal_IDirectSoundVtbl* lpVtbl; +}; +HRESULT mal_IDirectSound_QueryInterface(mal_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IDirectSound_AddRef(mal_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IDirectSound_Release(mal_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IDirectSound_CreateSoundBuffer(mal_IDirectSound* pThis, const MAL_DSBUFFERDESC* pDSBufferDesc, mal_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } +HRESULT mal_IDirectSound_GetCaps(mal_IDirectSound* pThis, MAL_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } +HRESULT mal_IDirectSound_DuplicateSoundBuffer(mal_IDirectSound* pThis, mal_IDirectSoundBuffer* pDSBufferOriginal, mal_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } +HRESULT mal_IDirectSound_SetCooperativeLevel(mal_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } +HRESULT mal_IDirectSound_Compact(mal_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } +HRESULT mal_IDirectSound_GetSpeakerConfig(mal_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } +HRESULT mal_IDirectSound_SetSpeakerConfig(mal_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } +HRESULT mal_IDirectSound_Initialize(mal_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } + + +// IDirectSoundBuffer +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IDirectSoundBuffer* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IDirectSoundBuffer* pThis); + + // IDirectSoundBuffer + HRESULT (STDMETHODCALLTYPE * GetCaps) (mal_IDirectSoundBuffer* pThis, MAL_DSBCAPS* pDSBufferCaps); + HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(mal_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); + HRESULT (STDMETHODCALLTYPE * GetFormat) (mal_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetVolume) (mal_IDirectSoundBuffer* pThis, LONG* pVolume); + HRESULT (STDMETHODCALLTYPE * GetPan) (mal_IDirectSoundBuffer* pThis, LONG* pPan); + HRESULT (STDMETHODCALLTYPE * GetFrequency) (mal_IDirectSoundBuffer* pThis, DWORD* pFrequency); + HRESULT (STDMETHODCALLTYPE * GetStatus) (mal_IDirectSoundBuffer* pThis, DWORD* pStatus); + HRESULT (STDMETHODCALLTYPE * Initialize) (mal_IDirectSoundBuffer* pThis, mal_IDirectSound* pDirectSound, const MAL_DSBUFFERDESC* pDSBufferDesc); + HRESULT (STDMETHODCALLTYPE * Lock) (mal_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * Play) (mal_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(mal_IDirectSoundBuffer* pThis, DWORD dwNewPosition); + HRESULT (STDMETHODCALLTYPE * SetFormat) (mal_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat); + HRESULT (STDMETHODCALLTYPE * SetVolume) (mal_IDirectSoundBuffer* pThis, LONG volume); + HRESULT (STDMETHODCALLTYPE * SetPan) (mal_IDirectSoundBuffer* pThis, LONG pan); + HRESULT (STDMETHODCALLTYPE * SetFrequency) (mal_IDirectSoundBuffer* pThis, DWORD dwFrequency); + HRESULT (STDMETHODCALLTYPE * Stop) (mal_IDirectSoundBuffer* pThis); + HRESULT (STDMETHODCALLTYPE * Unlock) (mal_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); + HRESULT (STDMETHODCALLTYPE * Restore) (mal_IDirectSoundBuffer* pThis); +} mal_IDirectSoundBufferVtbl; +struct mal_IDirectSoundBuffer +{ + mal_IDirectSoundBufferVtbl* lpVtbl; +}; +HRESULT mal_IDirectSoundBuffer_QueryInterface(mal_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IDirectSoundBuffer_AddRef(mal_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IDirectSoundBuffer_Release(mal_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IDirectSoundBuffer_GetCaps(mal_IDirectSoundBuffer* pThis, MAL_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } +HRESULT mal_IDirectSoundBuffer_GetCurrentPosition(mal_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } +HRESULT mal_IDirectSoundBuffer_GetFormat(mal_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +HRESULT mal_IDirectSoundBuffer_GetVolume(mal_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } +HRESULT mal_IDirectSoundBuffer_GetPan(mal_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } +HRESULT mal_IDirectSoundBuffer_GetFrequency(mal_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } +HRESULT mal_IDirectSoundBuffer_GetStatus(mal_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } +HRESULT mal_IDirectSoundBuffer_Initialize(mal_IDirectSoundBuffer* pThis, mal_IDirectSound* pDirectSound, const MAL_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } +HRESULT mal_IDirectSoundBuffer_Lock(mal_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } +HRESULT mal_IDirectSoundBuffer_Play(mal_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } +HRESULT mal_IDirectSoundBuffer_SetCurrentPosition(mal_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } +HRESULT mal_IDirectSoundBuffer_SetFormat(mal_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } +HRESULT mal_IDirectSoundBuffer_SetVolume(mal_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } +HRESULT mal_IDirectSoundBuffer_SetPan(mal_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } +HRESULT mal_IDirectSoundBuffer_SetFrequency(mal_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } +HRESULT mal_IDirectSoundBuffer_Stop(mal_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } +HRESULT mal_IDirectSoundBuffer_Unlock(mal_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } +HRESULT mal_IDirectSoundBuffer_Restore(mal_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } + + +// IDirectSoundCapture +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IDirectSoundCapture* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IDirectSoundCapture* pThis); + + // IDirectSoundCapture + HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(mal_IDirectSoundCapture* pThis, const MAL_DSCBUFFERDESC* pDSCBufferDesc, mal_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); + HRESULT (STDMETHODCALLTYPE * GetCaps) (mal_IDirectSoundCapture* pThis, MAL_DSCCAPS* pDSCCaps); + HRESULT (STDMETHODCALLTYPE * Initialize) (mal_IDirectSoundCapture* pThis, const GUID* pGuidDevice); +} mal_IDirectSoundCaptureVtbl; +struct mal_IDirectSoundCapture +{ + mal_IDirectSoundCaptureVtbl* lpVtbl; +}; +HRESULT mal_IDirectSoundCapture_QueryInterface(mal_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IDirectSoundCapture_AddRef(mal_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IDirectSoundCapture_Release(mal_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IDirectSoundCapture_CreateCaptureBuffer(mal_IDirectSoundCapture* pThis, const MAL_DSCBUFFERDESC* pDSCBufferDesc, mal_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } +HRESULT mal_IDirectSoundCapture_GetCaps (mal_IDirectSoundCapture* pThis, MAL_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } +HRESULT mal_IDirectSoundCapture_Initialize (mal_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } + + +// IDirectSoundCaptureBuffer +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IDirectSoundCaptureBuffer* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IDirectSoundCaptureBuffer* pThis); + + // IDirectSoundCaptureBuffer + HRESULT (STDMETHODCALLTYPE * GetCaps) (mal_IDirectSoundCaptureBuffer* pThis, MAL_DSCBCAPS* pDSCBCaps); + HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(mal_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); + HRESULT (STDMETHODCALLTYPE * GetFormat) (mal_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetStatus) (mal_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); + HRESULT (STDMETHODCALLTYPE * Initialize) (mal_IDirectSoundCaptureBuffer* pThis, mal_IDirectSoundCapture* pDirectSoundCapture, const MAL_DSCBUFFERDESC* pDSCBufferDesc); + HRESULT (STDMETHODCALLTYPE * Lock) (mal_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * Start) (mal_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * Stop) (mal_IDirectSoundCaptureBuffer* pThis); + HRESULT (STDMETHODCALLTYPE * Unlock) (mal_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); +} mal_IDirectSoundCaptureBufferVtbl; +struct mal_IDirectSoundCaptureBuffer +{ + mal_IDirectSoundCaptureBufferVtbl* lpVtbl; +}; +HRESULT mal_IDirectSoundCaptureBuffer_QueryInterface(mal_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IDirectSoundCaptureBuffer_AddRef(mal_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IDirectSoundCaptureBuffer_Release(mal_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IDirectSoundCaptureBuffer_GetCaps(mal_IDirectSoundCaptureBuffer* pThis, MAL_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } +HRESULT mal_IDirectSoundCaptureBuffer_GetCurrentPosition(mal_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } +HRESULT mal_IDirectSoundCaptureBuffer_GetFormat(mal_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +HRESULT mal_IDirectSoundCaptureBuffer_GetStatus(mal_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } +HRESULT mal_IDirectSoundCaptureBuffer_Initialize(mal_IDirectSoundCaptureBuffer* pThis, mal_IDirectSoundCapture* pDirectSoundCapture, const MAL_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } +HRESULT mal_IDirectSoundCaptureBuffer_Lock(mal_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } +HRESULT mal_IDirectSoundCaptureBuffer_Start(mal_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } +HRESULT mal_IDirectSoundCaptureBuffer_Stop(mal_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } +HRESULT mal_IDirectSoundCaptureBuffer_Unlock(mal_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } + + +// IDirectSoundNotify +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IDirectSoundNotify* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IDirectSoundNotify* pThis); + + // IDirectSoundNotify + HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(mal_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MAL_DSBPOSITIONNOTIFY* pPositionNotifies); +} mal_IDirectSoundNotifyVtbl; +struct mal_IDirectSoundNotify +{ + mal_IDirectSoundNotifyVtbl* lpVtbl; +}; +HRESULT mal_IDirectSoundNotify_QueryInterface(mal_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IDirectSoundNotify_AddRef(mal_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IDirectSoundNotify_Release(mal_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IDirectSoundNotify_SetNotificationPositions(mal_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MAL_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } + + +typedef BOOL (CALLBACK * mal_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext); +typedef HRESULT (WINAPI * mal_DirectSoundCreateProc) (const GUID* pcGuidDevice, mal_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter); +typedef HRESULT (WINAPI * mal_DirectSoundEnumerateAProc) (mal_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); +typedef HRESULT (WINAPI * mal_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, mal_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter); +typedef HRESULT (WINAPI * mal_DirectSoundCaptureEnumerateAProc)(mal_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); + + +// Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, +// the channel count and channel map will be left unmodified. +void mal_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) +{ + WORD channels = 0; + if (pChannelsOut != NULL) { + channels = *pChannelsOut; + } + + DWORD channelMap = 0; + if (pChannelMapOut != NULL) { + channelMap = *pChannelMapOut; + } + + // The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper + // 16 bits is for the geometry. + switch ((BYTE)(speakerConfig)) { + case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; + case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; + case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; + case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; + case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; + case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; + case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; + case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; + case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; + default: break; + } + + if (pChannelsOut != NULL) { + *pChannelsOut = channels; + } + + if (pChannelMapOut != NULL) { + *pChannelMapOut = channelMap; + } } -mal_result mal_context_init__dsound(mal_context* pContext) +mal_result mal_context_create_IDirectSound__dsound(mal_context* pContext, mal_share_mode shareMode, const mal_device_id* pDeviceID, mal_IDirectSound** ppDirectSound) { mal_assert(pContext != NULL); + mal_assert(ppDirectSound != NULL); - (void)pContext; + *ppDirectSound = NULL; + mal_IDirectSound* pDirectSound = NULL; + + if (FAILED(((mal_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + // The cooperative level must be set before doing anything else. + HWND hWnd = ((MAL_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); + if (hWnd == NULL) { + hWnd = ((MAL_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); + } + if (FAILED(mal_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == mal_share_mode_exclusive) ? MAL_DSSCL_EXCLUSIVE : MAL_DSSCL_PRIORITY))) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + *ppDirectSound = pDirectSound; return MAL_SUCCESS; } -mal_result mal_context_uninit__dsound(mal_context* pContext) +mal_result mal_context_create_IDirectSoundCapture__dsound(mal_context* pContext, mal_share_mode shareMode, const mal_device_id* pDeviceID, mal_IDirectSoundCapture** ppDirectSoundCapture) { mal_assert(pContext != NULL); - mal_assert(pContext->backend == mal_backend_dsound); + mal_assert(ppDirectSoundCapture != NULL); + // Everything is shared in capture mode by the looks of it. + (void)shareMode; + + *ppDirectSoundCapture = NULL; + mal_IDirectSoundCapture* pDirectSoundCapture = NULL; + + if (FAILED(((mal_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL))) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + *ppDirectSoundCapture = pDirectSoundCapture; + return MAL_SUCCESS; +} + +mal_result mal_context_get_format_info_for_IDirectSoundCapture__dsound(mal_context* pContext, mal_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) +{ + mal_assert(pContext != NULL); + mal_assert(pDirectSoundCapture != NULL); + + if (pChannels) { + *pChannels = 0; + } + if (pBitsPerSample) { + *pBitsPerSample = 0; + } + if (pSampleRate) { + *pSampleRate = 0; + } + + MAL_DSCCAPS caps; + mal_zero_object(&caps); + caps.dwSize = sizeof(caps); + if (FAILED(mal_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps))) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (pChannels) { + *pChannels = (WORD)caps.dwChannels; + } + + // The device can support multiple formats. We just go through the different formats in order of priority and + // pick the first one. This the same type of system as the WinMM backend. + WORD bitsPerSample = 16; + DWORD sampleRate = 48000; + + if (caps.dwChannels == 1) { + if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 16; // Didn't find it. Just fall back to 16-bit. + } + } + } else if (caps.dwChannels == 2) { + if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 16; // Didn't find it. Just fall back to 16-bit. + } + } + } + + if (pBitsPerSample) { + *pBitsPerSample = bitsPerSample; + } + if (pSampleRate) { + *pSampleRate = sampleRate; + } + + return MAL_SUCCESS; +} + +mal_bool32 mal_context_is_device_id_equal__dsound(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); (void)pContext; + + return memcmp(pID0->dsound, pID1->dsound, sizeof(pID0->dsound)) == 0; +} + + +typedef struct +{ + mal_context* pContext; + mal_device_type deviceType; + mal_enum_devices_callback_proc callback; + void* pUserData; + mal_bool32 terminated; +} mal_context_enumerate_devices_callback_data__dsound; + +BOOL CALLBACK mal_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) +{ + (void)lpcstrModule; + + mal_context_enumerate_devices_callback_data__dsound* pData = (mal_context_enumerate_devices_callback_data__dsound*)lpContext; + mal_assert(pData != NULL); + + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + + // ID. + if (lpGuid != NULL) { + mal_copy_memory(deviceInfo.id.dsound, lpGuid, 16); + } else { + mal_zero_memory(deviceInfo.id.dsound, 16); + } + + // Name / Description + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); + + + // Call the callback function, but make sure we stop enumerating if the callee requested so. + pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData); + if (pData->terminated) { + return FALSE; // Stop enumeration. + } else { + return TRUE; // Continue enumeration. + } +} + +mal_result mal_context_enumerate_devices__dsound(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + mal_context_enumerate_devices_callback_data__dsound data; + data.pContext = pContext; + data.callback = callback; + data.pUserData = pUserData; + data.terminated = MAL_FALSE; + + // Playback. + if (!data.terminated) { + data.deviceType = mal_device_type_playback; + ((mal_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(mal_context_enumerate_devices_callback__dsound, &data); + } + + // Capture. + if (!data.terminated) { + data.deviceType = mal_device_type_capture; + ((mal_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(mal_context_enumerate_devices_callback__dsound, &data); + } + + return MAL_SUCCESS; +} + + +typedef struct +{ + const mal_device_id* pDeviceID; + mal_device_info* pDeviceInfo; + mal_bool32 found; +} mal_context_get_device_info_callback_data__dsound; + +BOOL CALLBACK mal_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) +{ + (void)lpcstrModule; + + mal_context_get_device_info_callback_data__dsound* pData = (mal_context_get_device_info_callback_data__dsound*)lpContext; + mal_assert(pData != NULL); + + if ((pData->pDeviceID == NULL || mal_is_guid_equal(pData->pDeviceID->dsound, &MAL_GUID_NULL)) && (lpGuid == NULL || mal_is_guid_equal(lpGuid, &MAL_GUID_NULL))) { + // Default device. + mal_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); + pData->found = MAL_TRUE; + return FALSE; // Stop enumeration. + } else { + // Not the default device. + if (lpGuid != NULL) { + if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { + mal_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); + pData->found = MAL_TRUE; + return FALSE; // Stop enumeration. + } + } + } + + return TRUE; +} + +mal_result mal_context_get_device_info__dsound(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + (void)shareMode; + + if (pDeviceID != NULL) { + // ID. + mal_copy_memory(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); + + // Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. + mal_context_get_device_info_callback_data__dsound data; + data.pDeviceID = pDeviceID; + data.pDeviceInfo = pDeviceInfo; + data.found = MAL_FALSE; + if (deviceType == mal_device_type_playback) { + ((mal_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(mal_context_get_device_info_callback__dsound, &data); + } else { + ((mal_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(mal_context_get_device_info_callback__dsound, &data); + } + + if (!data.found) { + return MAL_NO_DEVICE; + } + } else { + // I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. + + // ID + mal_zero_memory(pDeviceInfo->id.dsound, 16); + + // Name / Description/ + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + } + + // Retrieving detailed information is slightly different depending on the device type. + if (deviceType == mal_device_type_playback) { + // Playback. + mal_IDirectSound* pDirectSound; + mal_result result = mal_context_create_IDirectSound__dsound(pContext, shareMode, pDeviceID, &pDirectSound); + if (result != MAL_SUCCESS) { + return result; + } + + MAL_DSCAPS caps; + mal_zero_object(&caps); + caps.dwSize = sizeof(caps); + if (FAILED(mal_IDirectSound_GetCaps(pDirectSound, &caps))) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if ((caps.dwFlags & MAL_DSCAPS_PRIMARYSTEREO) != 0) { + // It supports at least stereo, but could support more. + WORD channels = 2; + + // Look at the speaker configuration to get a better idea on the channel count. + DWORD speakerConfig; + if (SUCCEEDED(mal_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig))) { + mal_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); + } + + pDeviceInfo->minChannels = channels; + pDeviceInfo->maxChannels = channels; + } else { + // It does not support stereo, which means we are stuck with mono. + pDeviceInfo->minChannels = 1; + pDeviceInfo->maxChannels = 1; + } + + // Sample rate. + if ((caps.dwFlags & MAL_DSCAPS_CONTINUOUSRATE) != 0) { + pDeviceInfo->minSampleRate = caps.dwMinSecondarySampleRate; + pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate; + + // On my machine the min and max sample rates can return 100 and 200000 respectively. I'd rather these be within + // the range of our standard sample rates so I'm clamping. + if (caps.dwMinSecondarySampleRate < MAL_MIN_SAMPLE_RATE && caps.dwMaxSecondarySampleRate >= MAL_MIN_SAMPLE_RATE) { + pDeviceInfo->minSampleRate = MAL_MIN_SAMPLE_RATE; + } + if (caps.dwMaxSecondarySampleRate > MAL_MAX_SAMPLE_RATE && caps.dwMinSecondarySampleRate <= MAL_MAX_SAMPLE_RATE) { + pDeviceInfo->maxSampleRate = MAL_MAX_SAMPLE_RATE; + } + } else { + // Only supports a single sample rate. Set both min an max to the same thing. Do not clamp within the standard rates. + pDeviceInfo->minSampleRate = caps.dwMaxSecondarySampleRate; + pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate; + } + + // DirectSound can support all formats. + pDeviceInfo->formatCount = mal_format_count - 1; // Minus one because we don't want to include mal_format_unknown. + for (mal_uint32 iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) { + pDeviceInfo->formats[iFormat] = (mal_format)(iFormat + 1); // +1 to skip over mal_format_unknown. + } + + mal_IDirectSound_Release(pDirectSound); + } else { + // Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture + // devices can support a number of different formats, but for simplicity and consistency with mal_device_init() I'm just + // reporting the best format. + mal_IDirectSoundCapture* pDirectSoundCapture; + mal_result result = mal_context_create_IDirectSoundCapture__dsound(pContext, shareMode, pDeviceID, &pDirectSoundCapture); + if (result != MAL_SUCCESS) { + return result; + } + + WORD channels; + WORD bitsPerSample; + DWORD sampleRate; + result = mal_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); + if (result != MAL_SUCCESS) { + mal_IDirectSoundCapture_Release(pDirectSoundCapture); + return result; + } + + pDeviceInfo->minChannels = channels; + pDeviceInfo->maxChannels = channels; + pDeviceInfo->minSampleRate = sampleRate; + pDeviceInfo->maxSampleRate = sampleRate; + pDeviceInfo->formatCount = 1; + if (bitsPerSample == 8) { + pDeviceInfo->formats[0] = mal_format_u8; + } else if (bitsPerSample == 16) { + pDeviceInfo->formats[0] = mal_format_s16; + } else if (bitsPerSample == 24) { + pDeviceInfo->formats[0] = mal_format_s24; + } else if (bitsPerSample == 32) { + pDeviceInfo->formats[0] = mal_format_s32; + } else { + mal_IDirectSoundCapture_Release(pDirectSoundCapture); + return MAL_FORMAT_NOT_SUPPORTED; + } + + mal_IDirectSoundCapture_Release(pDirectSoundCapture); + } + return MAL_SUCCESS; } @@ -4219,7 +8014,7 @@ typedef struct mal_device_info* pInfo; } mal_device_enum_data__dsound; -static BOOL CALLBACK mal_enum_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) +BOOL CALLBACK mal_enum_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) { (void)lpcstrModule; @@ -4248,101 +8043,48 @@ static BOOL CALLBACK mal_enum_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpc return TRUE; } -static mal_result mal_enumerate_devices__dsound(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) -{ - (void)pContext; - - mal_uint32 infoSize = *pCount; - *pCount = 0; - - mal_device_enum_data__dsound enumData; - enumData.deviceCount = 0; - enumData.infoCount = infoSize; - enumData.pInfo = pInfo; - - HMODULE dsoundDLL = mal_open_dsound_dll(); - if (dsoundDLL == NULL) { - return MAL_NO_BACKEND; - } - - if (type == mal_device_type_playback) { - mal_DirectSoundEnumerateAProc pDirectSoundEnumerateA = (mal_DirectSoundEnumerateAProc)GetProcAddress(dsoundDLL, "DirectSoundEnumerateA"); - if (pDirectSoundEnumerateA) { - pDirectSoundEnumerateA(mal_enum_devices_callback__dsound, &enumData); - } - } else { - mal_DirectSoundCaptureEnumerateAProc pDirectSoundCaptureEnumerateA = (mal_DirectSoundCaptureEnumerateAProc)GetProcAddress(dsoundDLL, "DirectSoundCaptureEnumerateA"); - if (pDirectSoundCaptureEnumerateA) { - pDirectSoundCaptureEnumerateA(mal_enum_devices_callback__dsound, &enumData); - } - } - - - mal_close_dsound_dll(dsoundDLL); - - *pCount = enumData.deviceCount; - return MAL_SUCCESS; -} - -static void mal_device_uninit__dsound(mal_device* pDevice) +void mal_device_uninit__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); - if (pDevice->dsound.hDSoundDLL != NULL) { - if (pDevice->dsound.pNotify) { - IDirectSoundNotify_Release((LPDIRECTSOUNDNOTIFY)pDevice->dsound.pNotify); - } + if (pDevice->dsound.pNotify != NULL) { + mal_IDirectSoundNotify_Release((mal_IDirectSoundNotify*)pDevice->dsound.pNotify); + } - if (pDevice->dsound.hStopEvent) { - CloseHandle(pDevice->dsound.hStopEvent); - } - for (mal_uint32 i = 0; i < pDevice->periods; ++i) { - if (pDevice->dsound.pNotifyEvents[i]) { - CloseHandle(pDevice->dsound.pNotifyEvents[i]); - } + if (pDevice->dsound.hStopEvent) { + CloseHandle(pDevice->dsound.hStopEvent); + } + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + if (pDevice->dsound.pNotifyEvents[i]) { + CloseHandle(pDevice->dsound.pNotifyEvents[i]); } + } - if (pDevice->dsound.pCaptureBuffer) { - IDirectSoundCaptureBuffer_Release((LPDIRECTSOUNDBUFFER)pDevice->dsound.pCaptureBuffer); - } - if (pDevice->dsound.pCapture) { - IDirectSoundCapture_Release((LPDIRECTSOUNDCAPTURE)pDevice->dsound.pCapture); - } + if (pDevice->dsound.pCaptureBuffer != NULL) { + mal_IDirectSoundCaptureBuffer_Release((mal_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + } + if (pDevice->dsound.pCapture != NULL) { + mal_IDirectSoundCapture_Release((mal_IDirectSoundCapture*)pDevice->dsound.pCapture); + } - if (pDevice->dsound.pPlaybackBuffer) { - IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer); - } - if (pDevice->dsound.pPlaybackPrimaryBuffer) { - IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer); - } - if (pDevice->dsound.pPlayback != NULL) { - IDirectSound_Release((LPDIRECTSOUND)pDevice->dsound.pPlayback); - } - - mal_close_dsound_dll((HMODULE)pDevice->dsound.hDSoundDLL); + if (pDevice->dsound.pPlaybackBuffer != NULL) { + mal_IDirectSoundBuffer_Release((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); + } + if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { + mal_IDirectSoundBuffer_Release((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); + } + if (pDevice->dsound.pPlayback != NULL) { + mal_IDirectSound_Release((mal_IDirectSound*)pDevice->dsound.pPlayback); } } -static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) { (void)pContext; -#ifdef __cplusplus - GUID _MAL_GUID_IID_DirectSoundNotify = MAL_GUID_IID_DirectSoundNotify; - GUID _MAL_GUID_IID_IDirectSoundCaptureBuffer = MAL_GUID_IID_IDirectSoundCaptureBuffer; -#else - GUID* _MAL_GUID_IID_DirectSoundNotify = &MAL_GUID_IID_DirectSoundNotify; - GUID* _MAL_GUID_IID_IDirectSoundCaptureBuffer = &MAL_GUID_IID_IDirectSoundCaptureBuffer; -#endif - mal_assert(pDevice != NULL); mal_zero_object(&pDevice->dsound); - pDevice->dsound.hDSoundDLL = (mal_handle)mal_open_dsound_dll(); - if (pDevice->dsound.hDSoundDLL == NULL) { - return MAL_NO_BACKEND; - } - // Check that we have a valid format. GUID subformat; switch (pConfig->format) @@ -4350,6 +8092,7 @@ static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type case mal_format_u8: case mal_format_s16: case mal_format_s24: + //case mal_format_s24_32: case mal_format_s32: { subformat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM; @@ -4364,14 +8107,13 @@ static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type return MAL_FORMAT_NOT_SUPPORTED; } - WAVEFORMATEXTENSIBLE wf; mal_zero_object(&wf); wf.Format.cbSize = sizeof(wf); wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wf.Format.nChannels = (WORD)pConfig->channels; wf.Format.nSamplesPerSec = (DWORD)pConfig->sampleRate; - wf.Format.wBitsPerSample = (WORD)mal_get_sample_size_in_bytes(pConfig->format)*8; + wf.Format.wBitsPerSample = (WORD)mal_get_bytes_per_sample(pConfig->format)*8; wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8; wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; @@ -4382,66 +8124,95 @@ static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type // Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices :( if (type == mal_device_type_playback) { - mal_DirectSoundCreateProc pDirectSoundCreate = (mal_DirectSoundCreateProc)GetProcAddress((HMODULE)pDevice->dsound.hDSoundDLL, "DirectSoundCreate"); - if (pDirectSoundCreate == NULL) { + mal_result result = mal_context_create_IDirectSound__dsound(pContext, pConfig->shareMode, pDeviceID, (mal_IDirectSound**)&pDevice->dsound.pPlayback); + if (result != MAL_SUCCESS) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] Could not find DirectSoundCreate().", MAL_API_NOT_FOUND); + return result; } - if (FAILED(pDirectSoundCreate((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, (LPDIRECTSOUND*)&pDevice->dsound.pPlayback, NULL))) { - mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] DirectSoundCreate() failed for playback device.", MAL_DSOUND_FAILED_TO_CREATE_DEVICE); - } - - // The cooperative level must be set before doing anything else. - HWND hWnd = ((MAL_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); - if (hWnd == NULL) { - hWnd = ((MAL_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); - } - if (FAILED(IDirectSound_SetCooperativeLevel((LPDIRECTSOUND)pDevice->dsound.pPlayback, hWnd, (pConfig->preferExclusiveMode) ? DSSCL_EXCLUSIVE : DSSCL_PRIORITY))) { - mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", MAL_DSOUND_FAILED_TO_SET_COOP_LEVEL); - } - - DSBUFFERDESC descDSPrimary; + MAL_DSBUFFERDESC descDSPrimary; mal_zero_object(&descDSPrimary); - descDSPrimary.dwSize = sizeof(DSBUFFERDESC); - descDSPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; - if (FAILED(IDirectSound_CreateSoundBuffer((LPDIRECTSOUND)pDevice->dsound.pPlayback, &descDSPrimary, (LPDIRECTSOUNDBUFFER*)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL))) { + descDSPrimary.dwSize = sizeof(MAL_DSBUFFERDESC); + descDSPrimary.dwFlags = MAL_DSBCAPS_PRIMARYBUFFER | MAL_DSBCAPS_CTRLVOLUME; + if (FAILED(mal_IDirectSound_CreateSoundBuffer((mal_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (mal_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL))) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", MAL_DSOUND_FAILED_TO_CREATE_BUFFER); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } + + // We may want to make some adjustments to the format if we are using defaults. + MAL_DSCAPS caps; + mal_zero_object(&caps); + caps.dwSize = sizeof(caps); + if (FAILED(mal_IDirectSound_GetCaps((mal_IDirectSound*)pDevice->dsound.pPlayback, &caps))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (pDevice->usingDefaultChannels) { + if ((caps.dwFlags & MAL_DSCAPS_PRIMARYSTEREO) != 0) { + // It supports at least stereo, but could support more. + wf.Format.nChannels = 2; + + // Look at the speaker configuration to get a better idea on the channel count. + DWORD speakerConfig; + if (SUCCEEDED(mal_IDirectSound_GetSpeakerConfig((mal_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { + mal_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask); + } + } else { + // It does not support stereo, which means we are stuck with mono. + wf.Format.nChannels = 1; + } + } + + if (pDevice->usingDefaultSampleRate) { + // We base the sample rate on the values returned by GetCaps(). + if ((caps.dwFlags & MAL_DSCAPS_CONTINUOUSRATE) != 0) { + wf.Format.nSamplesPerSec = mal_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); + } else { + wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate; + } + } + + wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8; + wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; + // From MSDN: // // The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest // supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer // and compare the result with the format that was requested with the SetFormat method. - if (FAILED(IDirectSoundBuffer_SetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf))) { + if (FAILED(mal_IDirectSoundBuffer_SetFormat((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf))) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] Failed to set format of playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED); - } - - // Get the _actual_ properties of the buffer. This is silly API design... - DWORD requiredSize; - if (FAILED(IDirectSoundBuffer_GetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, NULL, 0, &requiredSize))) { - mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED); } + // Get the _actual_ properties of the buffer. char rawdata[1024]; WAVEFORMATEXTENSIBLE* pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; - if (FAILED(IDirectSoundBuffer_GetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, requiredSize, NULL))) { + if (FAILED(mal_IDirectSoundBuffer_GetFormat((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL))) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED); } + pDevice->internalFormat = mal_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); pDevice->internalChannels = pActualFormat->Format.nChannels; pDevice->internalSampleRate = pActualFormat->Format.nSamplesPerSec; - bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->format); // Get the internal channel map based on the channel mask. - mal_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); + if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + mal_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); + } else { + mal_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); + } + + // We need to wait until we know the sample rate before we can calculate the buffer size. + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + } + + bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); + // Meaning of dwFlags (from MSDN): @@ -4457,21 +8228,21 @@ static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type // In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated // sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the // application can get a more accurate play cursor. - DSBUFFERDESC descDS; + MAL_DSBUFFERDESC descDS; mal_zero_object(&descDS); - descDS.dwSize = sizeof(DSBUFFERDESC); - descDS.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; + descDS.dwSize = sizeof(descDS); + descDS.dwFlags = MAL_DSBCAPS_CTRLPOSITIONNOTIFY | MAL_DSBCAPS_GLOBALFOCUS | MAL_DSBCAPS_GETCURRENTPOSITION2; descDS.dwBufferBytes = bufferSizeInBytes; descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; - if (FAILED(IDirectSound_CreateSoundBuffer((LPDIRECTSOUND)pDevice->dsound.pPlayback, &descDS, (LPDIRECTSOUNDBUFFER*)&pDevice->dsound.pPlaybackBuffer, NULL))) { + if (FAILED(mal_IDirectSound_CreateSoundBuffer((mal_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (mal_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL))) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", MAL_DSOUND_FAILED_TO_CREATE_BUFFER); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } // Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer. - if (FAILED(IDirectSoundBuffer_QueryInterface((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, _MAL_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) { + if (FAILED(mal_IDirectSoundBuffer_QueryInterface((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &MAL_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_QueryInterface() failed for playback device's IDirectSoundNotify object.", MAL_DSOUND_FAILED_TO_QUERY_INTERFACE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_QueryInterface() failed for playback device's IDirectSoundNotify object.", MAL_API_NOT_FOUND); } } else { // The default buffer size is treated slightly differently for DirectSound which, for some reason, seems to @@ -4480,42 +8251,64 @@ static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type pDevice->bufferSizeInFrames *= 2; // <-- Might need to fiddle with this to find a more ideal value. May even be able to just add a fixed amount rather than scaling. } - mal_DirectSoundCaptureCreateProc pDirectSoundCaptureCreate = (mal_DirectSoundCaptureCreateProc)GetProcAddress((HMODULE)pDevice->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - if (pDirectSoundCaptureCreate == NULL) { + mal_result result = mal_context_create_IDirectSoundCapture__dsound(pContext, pConfig->shareMode, pDeviceID, (mal_IDirectSoundCapture**)&pDevice->dsound.pCapture); + if (result != MAL_SUCCESS) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] Could not find DirectSoundCreate().", MAL_API_NOT_FOUND); + return result; } - if (FAILED(pDirectSoundCaptureCreate((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, (LPDIRECTSOUNDCAPTURE*)&pDevice->dsound.pCapture, NULL))) { + result = mal_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, (mal_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec); + if (result != MAL_SUCCESS) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", MAL_DSOUND_FAILED_TO_CREATE_DEVICE); + return result; } - bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); + wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8; + wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; + wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM; - DSCBUFFERDESC descDS; + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, wf.Format.nSamplesPerSec); + } + + bufferSizeInBytes = pDevice->bufferSizeInFrames * wf.Format.nChannels * mal_get_bytes_per_sample(pDevice->format); + + MAL_DSCBUFFERDESC descDS; mal_zero_object(&descDS); descDS.dwSize = sizeof(descDS); descDS.dwFlags = 0; descDS.dwBufferBytes = bufferSizeInBytes; descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; - LPDIRECTSOUNDCAPTUREBUFFER pDSCB_Temp; - if (FAILED(IDirectSoundCapture_CreateCaptureBuffer((LPDIRECTSOUNDCAPTURE)pDevice->dsound.pCapture, &descDS, &pDSCB_Temp, NULL))) { + if (FAILED(mal_IDirectSoundCapture_CreateCaptureBuffer((mal_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (mal_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL))) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MAL_DSOUND_FAILED_TO_CREATE_BUFFER); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } - HRESULT hr = IDirectSoundCapture_QueryInterface(pDSCB_Temp, _MAL_GUID_IID_IDirectSoundCaptureBuffer, (LPVOID*)&pDevice->dsound.pCaptureBuffer); - IDirectSoundCaptureBuffer_Release(pDSCB_Temp); - if (FAILED(hr)) { + // Get the _actual_ properties of the buffer. + char rawdata[1024]; + WAVEFORMATEXTENSIBLE* pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; + if (FAILED(mal_IDirectSoundCaptureBuffer_GetFormat((mal_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL))) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] IDirectSoundCapture_QueryInterface() failed for capture device's IDirectSoundCaptureBuffer8 object.", MAL_DSOUND_FAILED_TO_QUERY_INTERFACE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer.", MAL_FORMAT_NOT_SUPPORTED); } + pDevice->internalFormat = mal_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); + pDevice->internalChannels = pActualFormat->Format.nChannels; + pDevice->internalSampleRate = pActualFormat->Format.nSamplesPerSec; + + // Get the internal channel map based on the channel mask. + if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + mal_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); + } else { + mal_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); + } + + // Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer. - if (FAILED(IDirectSoundCaptureBuffer_QueryInterface((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, _MAL_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) { + if (FAILED(mal_IDirectSoundCaptureBuffer_QueryInterface((mal_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &MAL_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_QueryInterface() failed for capture device's IDirectSoundNotify object.", MAL_DSOUND_FAILED_TO_QUERY_INTERFACE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_QueryInterface() failed for capture device's IDirectSoundNotify object.", MAL_API_NOT_FOUND); } } @@ -4523,12 +8316,12 @@ static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type // device is a playback or capture device. For a playback device we want to be notified when a period just starts playing, // whereas for a capture device we want to be notified when a period has just _finished_ capturing. mal_uint32 periodSizeInBytes = pDevice->bufferSizeInFrames / pDevice->periods; - DSBPOSITIONNOTIFY notifyPoints[MAL_MAX_PERIODS_DSOUND]; // One notification event for each period. + MAL_DSBPOSITIONNOTIFY notifyPoints[MAL_MAX_PERIODS_DSOUND]; // One notification event for each period. for (mal_uint32 i = 0; i < pDevice->periods; ++i) { pDevice->dsound.pNotifyEvents[i] = CreateEventA(NULL, FALSE, FALSE, NULL); if (pDevice->dsound.pNotifyEvents[i] == NULL) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] Failed to create event for buffer notifications.", MAL_FAILED_TO_CREATE_EVENT); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to create event for buffer notifications.", MAL_FAILED_TO_CREATE_EVENT); } // The notification offset is in bytes. @@ -4536,9 +8329,9 @@ static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type notifyPoints[i].hEventNotify = pDevice->dsound.pNotifyEvents[i]; } - if (FAILED(IDirectSoundNotify_SetNotificationPositions((LPDIRECTSOUNDNOTIFY)pDevice->dsound.pNotify, pDevice->periods, notifyPoints))) { + if (FAILED(mal_IDirectSoundNotify_SetNotificationPositions((mal_IDirectSoundNotify*)pDevice->dsound.pNotify, pDevice->periods, notifyPoints))) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] IDirectSoundNotify_SetNotificationPositions() failed.", MAL_DSOUND_FAILED_TO_SET_NOTIFICATIONS); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundNotify_SetNotificationPositions() failed.", MAL_FAILED_TO_CREATE_EVENT); } // When the device is playing the worker thread will be waiting on a bunch of notification events. To return from @@ -4546,67 +8339,67 @@ static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type pDevice->dsound.hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL); if (pDevice->dsound.hStopEvent == NULL) { mal_device_uninit__dsound(pDevice); - return mal_post_error(pDevice, "[DirectSound] Failed to create event for main loop break notification.", MAL_FAILED_TO_CREATE_EVENT); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] Failed to create event for main loop break notification.", MAL_FAILED_TO_CREATE_EVENT); } return MAL_SUCCESS; } -static mal_result mal_device__start_backend__dsound(mal_device* pDevice) +mal_result mal_device__start_backend__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); if (pDevice->type == mal_device_type_playback) { // Before playing anything we need to grab an initial group of samples from the client. mal_uint32 framesToRead = pDevice->bufferSizeInFrames / pDevice->periods; - mal_uint32 desiredLockSize = framesToRead * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); + mal_uint32 desiredLockSize = framesToRead * pDevice->channels * mal_get_bytes_per_sample(pDevice->format); void* pLockPtr; DWORD actualLockSize; void* pLockPtr2; DWORD actualLockSize2; - if (SUCCEEDED(IDirectSoundBuffer_Lock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0, desiredLockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) { - framesToRead = actualLockSize / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + if (SUCCEEDED(mal_IDirectSoundBuffer_Lock((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, desiredLockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) { + framesToRead = actualLockSize / mal_get_bytes_per_sample(pDevice->format) / pDevice->channels; mal_device__read_frames_from_client(pDevice, framesToRead, pLockPtr); - IDirectSoundBuffer_Unlock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2); + mal_IDirectSoundBuffer_Unlock((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2); pDevice->dsound.lastProcessedFrame = framesToRead; - if (FAILED(IDirectSoundBuffer_Play((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0, 0, DSBPLAY_LOOPING))) { - return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Play() failed.", MAL_FAILED_TO_START_BACKEND_DEVICE); + if (FAILED(mal_IDirectSoundBuffer_Play((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MAL_DSBPLAY_LOOPING))) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MAL_FAILED_TO_START_BACKEND_DEVICE); } } else { - return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); } } else { - if (FAILED(IDirectSoundCaptureBuffer_Start((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, DSCBSTART_LOOPING))) { - return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MAL_FAILED_TO_START_BACKEND_DEVICE); + if (FAILED(mal_IDirectSoundCaptureBuffer_Start((mal_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MAL_DSCBSTART_LOOPING))) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MAL_FAILED_TO_START_BACKEND_DEVICE); } } return MAL_SUCCESS; } -static mal_result mal_device__stop_backend__dsound(mal_device* pDevice) +mal_result mal_device__stop_backend__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); if (pDevice->type == mal_device_type_playback) { - if (FAILED(IDirectSoundBuffer_Stop((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer))) { - return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + if (FAILED(mal_IDirectSoundBuffer_Stop((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer))) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); } - IDirectSoundBuffer_SetCurrentPosition((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0); + mal_IDirectSoundBuffer_SetCurrentPosition((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); } else { - if (FAILED(IDirectSoundCaptureBuffer_Stop((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer))) { - return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + if (FAILED(mal_IDirectSoundCaptureBuffer_Stop((mal_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer))) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); } } return MAL_SUCCESS; } -static mal_result mal_device__break_main_loop__dsound(mal_device* pDevice) +mal_result mal_device__break_main_loop__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -4617,7 +8410,7 @@ static mal_result mal_device__break_main_loop__dsound(mal_device* pDevice) return MAL_SUCCESS; } -static mal_bool32 mal_device__get_current_frame__dsound(mal_device* pDevice, mal_uint32* pCurrentPos) +mal_bool32 mal_device__get_current_frame__dsound(mal_device* pDevice, mal_uint32* pCurrentPos) { mal_assert(pDevice != NULL); mal_assert(pCurrentPos != NULL); @@ -4625,20 +8418,20 @@ static mal_bool32 mal_device__get_current_frame__dsound(mal_device* pDevice, mal DWORD dwCurrentPosition; if (pDevice->type == mal_device_type_playback) { - if (FAILED(IDirectSoundBuffer_GetCurrentPosition((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, NULL, &dwCurrentPosition))) { + if (FAILED(mal_IDirectSoundBuffer_GetCurrentPosition((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, NULL, &dwCurrentPosition))) { return MAL_FALSE; } } else { - if (FAILED(IDirectSoundCaptureBuffer_GetCurrentPosition((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, &dwCurrentPosition, NULL))) { + if (FAILED(mal_IDirectSoundCaptureBuffer_GetCurrentPosition((mal_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &dwCurrentPosition, NULL))) { return MAL_FALSE; } } - *pCurrentPos = (mal_uint32)dwCurrentPosition / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + *pCurrentPos = (mal_uint32)dwCurrentPosition / mal_get_bytes_per_sample(pDevice->format) / pDevice->channels; return MAL_TRUE; } -static mal_uint32 mal_device__get_available_frames__dsound(mal_device* pDevice) +mal_uint32 mal_device__get_available_frames__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -4680,7 +8473,7 @@ static mal_uint32 mal_device__get_available_frames__dsound(mal_device* pDevice) } } -static mal_uint32 mal_device__wait_for_frames__dsound(mal_device* pDevice) +mal_uint32 mal_device__wait_for_frames__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -4709,7 +8502,7 @@ static mal_uint32 mal_device__wait_for_frames__dsound(mal_device* pDevice) return mal_device__get_available_frames__dsound(pDevice); } -static mal_result mal_device__main_loop__dsound(mal_device* pDevice) +mal_result mal_device__main_loop__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -4728,42 +8521,81 @@ static mal_result mal_device__main_loop__dsound(mal_device* pDevice) return MAL_FALSE; } - DWORD lockOffset = pDevice->dsound.lastProcessedFrame * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); - DWORD lockSize = framesAvailable * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); + DWORD lockOffset = pDevice->dsound.lastProcessedFrame * pDevice->channels * mal_get_bytes_per_sample(pDevice->format); + DWORD lockSize = framesAvailable * pDevice->channels * mal_get_bytes_per_sample(pDevice->format); if (pDevice->type == mal_device_type_playback) { void* pLockPtr; DWORD actualLockSize; void* pLockPtr2; DWORD actualLockSize2; - if (FAILED(IDirectSoundBuffer_Lock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, lockOffset, lockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) { - return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); + if (FAILED(mal_IDirectSoundBuffer_Lock((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffset, lockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); } - mal_uint32 frameCount = actualLockSize / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + mal_uint32 frameCount = actualLockSize / mal_get_bytes_per_sample(pDevice->format) / pDevice->channels; mal_device__read_frames_from_client(pDevice, frameCount, pLockPtr); pDevice->dsound.lastProcessedFrame = (pDevice->dsound.lastProcessedFrame + frameCount) % pDevice->bufferSizeInFrames; - IDirectSoundBuffer_Unlock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2); + mal_IDirectSoundBuffer_Unlock((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2); } else { void* pLockPtr; DWORD actualLockSize; void* pLockPtr2; DWORD actualLockSize2; - if (FAILED(IDirectSoundCaptureBuffer_Lock((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, lockOffset, lockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) { - return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); + if (FAILED(mal_IDirectSoundCaptureBuffer_Lock((mal_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffset, lockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); } - mal_uint32 frameCount = actualLockSize / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + mal_uint32 frameCount = actualLockSize / mal_get_bytes_per_sample(pDevice->format) / pDevice->channels; mal_device__send_frames_to_client(pDevice, frameCount, pLockPtr); pDevice->dsound.lastProcessedFrame = (pDevice->dsound.lastProcessedFrame + frameCount) % pDevice->bufferSizeInFrames; - IDirectSoundCaptureBuffer_Unlock((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2); + mal_IDirectSoundCaptureBuffer_Unlock((mal_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2); } } return MAL_SUCCESS; } + + +mal_result mal_context_uninit__dsound(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_dsound); + + mal_dlclose(pContext->dsound.hDSoundDLL); + + return MAL_SUCCESS; +} + +mal_result mal_context_init__dsound(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + pContext->dsound.hDSoundDLL = mal_dlopen("dsound.dll"); + if (pContext->dsound.hDSoundDLL == NULL) { + return MAL_API_NOT_FOUND; + } + + pContext->dsound.DirectSoundCreate = mal_dlsym(pContext->dsound.hDSoundDLL, "DirectSoundCreate"); + pContext->dsound.DirectSoundEnumerateA = mal_dlsym(pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); + pContext->dsound.DirectSoundCaptureCreate = mal_dlsym(pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); + pContext->dsound.DirectSoundCaptureEnumerateA = mal_dlsym(pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); + + pContext->onUninit = mal_context_uninit__dsound; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__dsound; + pContext->onEnumDevices = mal_context_enumerate_devices__dsound; + pContext->onGetDeviceInfo = mal_context_get_device_info__dsound; + pContext->onDeviceInit = mal_device_init__dsound; + pContext->onDeviceUninit = mal_device_uninit__dsound; + pContext->onDeviceStart = mal_device__start_backend__dsound; + pContext->onDeviceStop = mal_device__stop_backend__dsound; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__dsound; + pContext->onDeviceMainLoop = mal_device__main_loop__dsound; + + return MAL_SUCCESS; +} #endif @@ -4774,29 +8606,40 @@ static mal_result mal_device__main_loop__dsound(mal_device* pDevice) // /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_HAS_WINMM -#include -#if !defined(MAXULONG_PTR) -typedef size_t DWORD_PTR; -#endif - -#if !defined(WAVE_FORMAT_44M08) -#define WAVE_FORMAT_44M08 0x00000100 -#define WAVE_FORMAT_44S08 0x00000200 -#define WAVE_FORMAT_44M16 0x00000400 -#define WAVE_FORMAT_44S16 0x00000800 -#define WAVE_FORMAT_48M08 0x00001000 -#define WAVE_FORMAT_48S08 0x00002000 -#define WAVE_FORMAT_48M16 0x00004000 -#define WAVE_FORMAT_48S16 0x00008000 -#define WAVE_FORMAT_96M08 0x00010000 -#define WAVE_FORMAT_96S08 0x00020000 -#define WAVE_FORMAT_96M16 0x00040000 -#define WAVE_FORMAT_96S16 0x00080000 -#endif +// Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures +// are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping +// the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version. +typedef struct +{ + WORD wMid; + WORD wPid; + MMVERSION vDriverVersion; + CHAR szPname[MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + DWORD dwSupport; + GUID ManufacturerGuid; + GUID ProductGuid; + GUID NameGuid; +} MAL_WAVEOUTCAPS2A; +typedef struct +{ + WORD wMid; + WORD wPid; + MMVERSION vDriverVersion; + CHAR szPname[MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + GUID ManufacturerGuid; + GUID ProductGuid; + GUID NameGuid; +} MAL_WAVEINCAPS2A; typedef UINT (WINAPI * MAL_PFN_waveOutGetNumDevs)(void); -typedef MMRESULT (WINAPI * MAL_PFN_waveOutGetDevCapsA)(UINT_PTR uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc); +typedef MMRESULT (WINAPI * MAL_PFN_waveOutGetDevCapsA)(mal_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc); typedef MMRESULT (WINAPI * MAL_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); typedef MMRESULT (WINAPI * MAL_PFN_waveOutClose)(HWAVEOUT hwo); typedef MMRESULT (WINAPI * MAL_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); @@ -4804,7 +8647,7 @@ typedef MMRESULT (WINAPI * MAL_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEH typedef MMRESULT (WINAPI * MAL_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); typedef MMRESULT (WINAPI * MAL_PFN_waveOutReset)(HWAVEOUT hwo); typedef UINT (WINAPI * MAL_PFN_waveInGetNumDevs)(void); -typedef MMRESULT (WINAPI * MAL_PFN_waveInGetDevCapsA)(UINT_PTR uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic); +typedef MMRESULT (WINAPI * MAL_PFN_waveInGetDevCapsA)(mal_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic); typedef MMRESULT (WINAPI * MAL_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); typedef MMRESULT (WINAPI * MAL_PFN_waveInClose)(HWAVEIN hwi); typedef MMRESULT (WINAPI * MAL_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); @@ -4828,90 +8671,286 @@ mal_result mal_result_from_MMRESULT(MMRESULT resultMM) } } -mal_result mal_context_init__winmm(mal_context* pContext) +char* mal_find_last_character(char* str, char ch) { - mal_assert(pContext != NULL); - - pContext->winmm.hWinMM = mal_dlopen("winmm.dll"); - if (pContext->winmm.hWinMM == NULL) { - return MAL_NO_BACKEND; + if (str == NULL) { + return NULL; } - pContext->winmm.waveOutGetNumDevs = mal_dlsym(pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = mal_dlsym(pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = mal_dlsym(pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = mal_dlsym(pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = mal_dlsym(pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = mal_dlsym(pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = mal_dlsym(pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = mal_dlsym(pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = mal_dlsym(pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = mal_dlsym(pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = mal_dlsym(pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = mal_dlsym(pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = mal_dlsym(pContext->winmm.hWinMM, "waveInReset"); + char* last = NULL; + while (*str != '\0') { + if (*str == ch) { + last = str; + } - return MAL_SUCCESS; + str += 1; + } + + return last; } -mal_result mal_context_uninit__winmm(mal_context* pContext) + +// Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so +// we can do things generically and typesafely. Names are being kept the same for consistency. +typedef struct { - mal_assert(pContext != NULL); - mal_assert(pContext->backend == mal_backend_winmm); + CHAR szPname[MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + GUID NameGuid; +} MAL_WAVECAPSA; - mal_dlclose(pContext->winmm.hWinMM); - return MAL_SUCCESS; -} - -static mal_result mal_enumerate_devices__winmm(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +mal_result mal_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) { - (void)pContext; + if (pBitsPerSample) { + *pBitsPerSample = 0; + } + if (pSampleRate) { + *pSampleRate = 0; + } - mal_uint32 infoSize = *pCount; - *pCount = 0; + WORD bitsPerSample = 0; + DWORD sampleRate = 0; - if (type == mal_device_type_playback) { - UINT deviceCount = ((MAL_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); - for (UINT iDevice = 0; iDevice < deviceCount; ++iDevice) { - if (pInfo != NULL) { - if (infoSize > 0) { - WAVEOUTCAPSA caps; - MMRESULT result = ((MAL_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iDevice, &caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { - pInfo->id.winmm = iDevice; - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), caps.szPname, (size_t)-1); - } - - pInfo += 1; - infoSize -= 1; - *pCount += 1; - } + if (channels == 1) { + bitsPerSample = 16; + if ((dwFormats & WAVE_FORMAT_48M16) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((dwFormats & WAVE_FORMAT_48M08) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { + sampleRate = 96000; } else { - *pCount += 1; + return MAL_FORMAT_NOT_SUPPORTED; } } } else { - UINT deviceCount = ((MAL_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); - for (UINT iDevice = 0; iDevice < deviceCount; ++iDevice) { - if (pInfo != NULL) { - if (infoSize > 0) { - WAVEINCAPSA caps; - MMRESULT result = ((MAL_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iDevice, &caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { - pInfo->id.winmm = iDevice; - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), caps.szPname, (size_t)-1); - } - - pInfo += 1; - infoSize -= 1; - *pCount += 1; - } + bitsPerSample = 16; + if ((dwFormats & WAVE_FORMAT_48S16) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((dwFormats & WAVE_FORMAT_48S08) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { + sampleRate = 96000; } else { - *pCount += 1; + return MAL_FORMAT_NOT_SUPPORTED; + } + } + } + + if (pBitsPerSample) { + *pBitsPerSample = bitsPerSample; + } + if (pSampleRate) { + *pSampleRate = sampleRate; + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info_from_WAVECAPS(mal_context* pContext, MAL_WAVECAPSA* pCaps, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + mal_assert(pCaps != NULL); + mal_assert(pDeviceInfo != NULL); + + // Name / Description + // + // Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking + // situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try + // looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. + + // Set the default to begin with. + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); + + // Now try the registry. There's a few things to consider here: + // - The name GUID can be null, in which we case we just need to stick to the original 31 characters. + // - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. + // - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The + // problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), + // but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to + // usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component + // name, and then concatenate the name from the registry. + if (!mal_is_guid_equal(&pCaps->NameGuid, &MAL_GUID_NULL)) { + wchar_t guidStrW[256]; + if (((MAL_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, mal_countof(guidStrW)) > 0) { + char guidStr[256]; + WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); + + char keyStr[1024]; + mal_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); + mal_strcat_s(keyStr, sizeof(keyStr), guidStr); + + HKEY hKey; + LONG result = ((MAL_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey); + if (result == ERROR_SUCCESS) { + BYTE nameFromReg[512]; + DWORD nameFromRegSize = sizeof(nameFromReg); + result = ((MAL_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize); + ((MAL_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); + + if (result == ERROR_SUCCESS) { + // We have the value from the registry, so now we need to construct the name string. + char name[1024]; + if (mal_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { + char* nameBeg = mal_find_last_character(name, '('); + if (nameBeg != NULL) { + size_t leadingLen = (nameBeg - name); + mal_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); + + // The closing ")", if it can fit. + if (leadingLen + nameFromRegSize < sizeof(name)-1) { + mal_strcat_s(name, sizeof(name), ")"); + } + + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); + } + } + } + } + } + } + + + WORD bitsPerSample; + DWORD sampleRate; + mal_result result = mal_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); + if (result != MAL_SUCCESS) { + return result; + } + + pDeviceInfo->minChannels = pCaps->wChannels; + pDeviceInfo->maxChannels = pCaps->wChannels; + pDeviceInfo->minSampleRate = sampleRate; + pDeviceInfo->maxSampleRate = sampleRate; + pDeviceInfo->formatCount = 1; + if (bitsPerSample == 8) { + pDeviceInfo->formats[0] = mal_format_u8; + } else if (bitsPerSample == 16) { + pDeviceInfo->formats[0] = mal_format_s16; + } else if (bitsPerSample == 24) { + pDeviceInfo->formats[0] = mal_format_s24; + } else if (bitsPerSample == 32) { + pDeviceInfo->formats[0] = mal_format_s32; + } else { + return MAL_FORMAT_NOT_SUPPORTED; + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info_from_WAVEOUTCAPS2(mal_context* pContext, MAL_WAVEOUTCAPS2A* pCaps, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + mal_assert(pCaps != NULL); + mal_assert(pDeviceInfo != NULL); + + MAL_WAVECAPSA caps; + mal_copy_memory(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); + caps.dwFormats = pCaps->dwFormats; + caps.wChannels = pCaps->wChannels; + caps.NameGuid = pCaps->NameGuid; + return mal_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); +} + +mal_result mal_context_get_device_info_from_WAVEINCAPS2(mal_context* pContext, MAL_WAVEINCAPS2A* pCaps, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + mal_assert(pCaps != NULL); + mal_assert(pDeviceInfo != NULL); + + MAL_WAVECAPSA caps; + mal_copy_memory(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); + caps.dwFormats = pCaps->dwFormats; + caps.wChannels = pCaps->wChannels; + caps.NameGuid = pCaps->NameGuid; + return mal_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); +} + + +mal_bool32 mal_context_is_device_id_equal__winmm(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return pID0->winmm == pID1->winmm; +} + +mal_result mal_context_enumerate_devices__winmm(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + // Playback. + UINT playbackDeviceCount = ((MAL_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); + for (UINT iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { + MAL_WAVEOUTCAPS2A caps; + mal_zero_object(&caps); + MMRESULT result = ((MAL_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MMSYSERR_NOERROR) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + deviceInfo.id.winmm = iPlaybackDevice; + + if (mal_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MAL_SUCCESS) { + mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + return MAL_SUCCESS; // Enumeration was stopped. + } + } + } + } + + // Capture. + UINT captureDeviceCount = ((MAL_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); + for (UINT iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { + MAL_WAVEINCAPS2A caps; + mal_zero_object(&caps); + MMRESULT result = ((MAL_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MMSYSERR_NOERROR) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + deviceInfo.id.winmm = iCaptureDevice; + + if (mal_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MAL_SUCCESS) { + mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + return MAL_SUCCESS; // Enumeration was stopped. + } } } } @@ -4919,7 +8958,39 @@ static mal_result mal_enumerate_devices__winmm(mal_context* pContext, mal_device return MAL_SUCCESS; } -static void mal_device_uninit__winmm(mal_device* pDevice) +mal_result mal_context_get_device_info__winmm(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + + UINT winMMDeviceID = 0; + if (pDeviceID != NULL) { + winMMDeviceID = (UINT)pDeviceID->winmm; + } + + pDeviceInfo->id.winmm = winMMDeviceID; + + if (deviceType == mal_device_type_playback) { + MAL_WAVEOUTCAPS2A caps; + mal_zero_object(&caps); + MMRESULT result = ((MAL_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MMSYSERR_NOERROR) { + return mal_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); + } + } else { + MAL_WAVEINCAPS2A caps; + mal_zero_object(&caps); + MMRESULT result = ((MAL_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MMSYSERR_NOERROR) { + return mal_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); + } + } + + return MAL_NO_DEVICE; +} + + +void mal_device_uninit__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -4931,18 +9002,15 @@ static void mal_device_uninit__winmm(mal_device* pDevice) mal_free(pDevice->winmm._pHeapData); CloseHandle((HANDLE)pDevice->winmm.hEvent); + + mal_zero_object(&pDevice->winmm); // Safety. } -static mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) { (void)pContext; mal_uint32 heapSize; - mal_uint32 iBit; - - WORD closestBitsPerSample = 0; - WORD closestChannels = 0; - DWORD closestSampleRate = 0; mal_assert(pDevice != NULL); mal_zero_object(&pDevice->winmm); @@ -4954,7 +9022,7 @@ static mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type const char* errorMsg = ""; mal_result errorCode = MAL_ERROR; - + mal_result result = MAL_SUCCESS; // WinMM doesn't seem to have a good way to query the format of the device. Therefore, we'll restrict the formats to the // standard formats documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd743855(v=vs.85).aspx. If @@ -4965,7 +9033,7 @@ static mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = (WORD)pConfig->channels; wf.nSamplesPerSec = (DWORD)pConfig->sampleRate; - wf.wBitsPerSample = (WORD)mal_get_sample_size_in_bytes(pConfig->format)*8; + wf.wBitsPerSample = (WORD)mal_get_bytes_per_sample(pConfig->format)*8; if (wf.nChannels > 2) { wf.nChannels = 2; @@ -4994,210 +9062,40 @@ static mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type // Change the format based on the closest match of the supported standard formats. DWORD dwFormats = 0; + WORD wChannels = 0; if (type == mal_device_type_playback) { WAVEOUTCAPSA caps; if (((MAL_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { dwFormats = caps.dwFormats; + wChannels = caps.wChannels; } else { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MAL_WINMM_FAILED_TO_GET_DEVICE_CAPS; + errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MAL_FORMAT_NOT_SUPPORTED; goto on_error; } } else { WAVEINCAPSA caps; if (((MAL_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { dwFormats = caps.dwFormats; + wChannels = caps.wChannels; } else { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MAL_WINMM_FAILED_TO_GET_DEVICE_CAPS; + errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MAL_FORMAT_NOT_SUPPORTED; goto on_error; } } if (dwFormats == 0) { - errorMsg = "[WinMM] Failed to retrieve the supported formats for the internal device.", errorCode = MAL_WINMM_FAILED_TO_GET_SUPPORTED_FORMATS; + errorMsg = "[WinMM] Failed to retrieve the supported formats for the internal device.", errorCode = MAL_FORMAT_NOT_SUPPORTED; goto on_error; } - for (iBit = 0; iBit < 32; ++iBit) { - WORD formatBitsPerSample = 0; - WORD formatChannels = 0; - DWORD formatSampleRate = 0; + wf.nChannels = wChannels; - DWORD format = (dwFormats & (1 << iBit)); - if (format != 0) { - switch (format) - { - case WAVE_FORMAT_1M08: - { - formatBitsPerSample = 8; - formatChannels = 1; - formatSampleRate = 110025; - } break; - case WAVE_FORMAT_1M16: - { - formatBitsPerSample = 16; - formatChannels = 1; - formatSampleRate = 110025; - } break; - case WAVE_FORMAT_1S08: - { - formatBitsPerSample = 8; - formatChannels = 2; - formatSampleRate = 110025; - } break; - case WAVE_FORMAT_1S16: - { - formatBitsPerSample = 16; - formatChannels = 2; - formatSampleRate = 110025; - } break; - case WAVE_FORMAT_2M08: - { - formatBitsPerSample = 8; - formatChannels = 1; - formatSampleRate = 22050; - } break; - case WAVE_FORMAT_2M16: - { - formatBitsPerSample = 16; - formatChannels = 1; - formatSampleRate = 22050; - } break; - case WAVE_FORMAT_2S08: - { - formatBitsPerSample = 8; - formatChannels = 2; - formatSampleRate = 22050; - } break; - case WAVE_FORMAT_2S16: - { - formatBitsPerSample = 16; - formatChannels = 2; - formatSampleRate = 22050; - } break; - case WAVE_FORMAT_44M08: - { - formatBitsPerSample = 8; - formatChannels = 1; - formatSampleRate = 44100; - } break; - case WAVE_FORMAT_44M16: - { - formatBitsPerSample = 16; - formatChannels = 1; - formatSampleRate = 44100; - } break; - case WAVE_FORMAT_44S08: - { - formatBitsPerSample = 8; - formatChannels = 2; - formatSampleRate = 44100; - } break; - case WAVE_FORMAT_44S16: - { - formatBitsPerSample = 16; - formatChannels = 2; - formatSampleRate = 44100; - } break; - case WAVE_FORMAT_48M08: - { - formatBitsPerSample = 8; - formatChannels = 1; - formatSampleRate = 48000; - } break; - case WAVE_FORMAT_48M16: - { - formatBitsPerSample = 16; - formatChannels = 1; - formatSampleRate = 48000; - } break; - case WAVE_FORMAT_48S08: - { - formatBitsPerSample = 8; - formatChannels = 2; - formatSampleRate = 48000; - } break; - case WAVE_FORMAT_48S16: - { - formatBitsPerSample = 16; - formatChannels = 2; - formatSampleRate = 48000; - } break; - case WAVE_FORMAT_96M08: - { - formatBitsPerSample = 8; - formatChannels = 1; - formatSampleRate = 96000; - } break; - case WAVE_FORMAT_96M16: - { - formatBitsPerSample = 16; - formatChannels = 1; - formatSampleRate = 96000; - } break; - case WAVE_FORMAT_96S08: - { - formatBitsPerSample = 8; - formatChannels = 2; - formatSampleRate = 96000; - } break; - case WAVE_FORMAT_96S16: - { - formatBitsPerSample = 16; - formatChannels = 2; - formatSampleRate = 96000; - } break; - default: - { - errorMsg = "[WinMM] The internal device does not support any of the standard formats.", errorCode = MAL_ERROR; // <-- Should never hit this. - goto on_error; - } break; - } - - if (formatBitsPerSample == wf.wBitsPerSample && formatChannels == wf.nChannels && formatSampleRate == wf.nSamplesPerSec) { - break; // It's an exact match. - } else { - // It's not an exact match. Compare it with the closest match. - if (closestBitsPerSample == 0) { - // This is the first format, so nothing to compare against. - closestBitsPerSample = formatBitsPerSample; - closestChannels = formatChannels; - closestSampleRate = formatSampleRate; - } else { - // Prefer the channel count be the same over the others. - if (formatChannels != closestChannels) { - // Channels aren't equal. Favour the one equal to our desired channel count. - if (formatChannels == wf.nChannels) { - closestBitsPerSample = formatBitsPerSample; - closestChannels = formatChannels; - closestSampleRate = formatSampleRate; - } - } else { - // The channels are equal. Look at the format now. - if (formatBitsPerSample != closestBitsPerSample) { - if (formatBitsPerSample == wf.wBitsPerSample) { - closestBitsPerSample = formatBitsPerSample; - closestChannels = formatChannels; - closestSampleRate = formatSampleRate; - } - } else { - // Both the channels and formats are the same, so now just favour whichever's sample rate is closest to the requested rate. - mal_uint32 closestRateDiff = (closestSampleRate > wf.nSamplesPerSec) ? (closestSampleRate - wf.nSamplesPerSec) : (wf.nSamplesPerSec - closestSampleRate); - mal_uint32 formatRateDiff = (formatSampleRate > wf.nSamplesPerSec) ? (formatSampleRate - wf.nSamplesPerSec) : (wf.nSamplesPerSec - formatSampleRate); - if (formatRateDiff < closestRateDiff) { - closestBitsPerSample = formatBitsPerSample; - closestChannels = formatChannels; - closestSampleRate = formatSampleRate; - } - } - } - } - } - } + result = mal_get_best_info_from_formats_flags__winmm(dwFormats, wChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); + if (result != MAL_SUCCESS) { + errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; + goto on_error; } - wf.wBitsPerSample = closestBitsPerSample; - wf.nChannels = closestChannels; - wf.nSamplesPerSec = closestSampleRate; wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8; wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; @@ -5211,14 +9109,14 @@ static mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type if (type == mal_device_type_playback) { - MMRESULT result = ((MAL_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); - if (result != MMSYSERR_NOERROR) { + MMRESULT resultMM = ((MAL_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); + if (resultMM != MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open playback device.", errorCode = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; } } else { - MMRESULT result = ((MAL_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); - if (result != MMSYSERR_NOERROR) { + MMRESULT resultMM = ((MAL_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); + if (resultMM != MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open capture device.", errorCode = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; } @@ -5232,7 +9130,7 @@ static mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type case 16: pDevice->internalFormat = mal_format_s16; break; case 24: pDevice->internalFormat = mal_format_s24; break; case 32: pDevice->internalFormat = mal_format_s32; break; - default: mal_post_error(pDevice, "[WinMM] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + default: mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); } } else { errorMsg = "[WinMM] The device's internal format is not supported by mini_al.", errorCode = MAL_FORMAT_NOT_SUPPORTED; @@ -5244,21 +9142,20 @@ static mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type // Just use the default channel mapping. WinMM only supports mono or stereo anyway so it'll reliably be left/right order for stereo. - mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, pDevice->internalChannels, pDevice->internalChannelMap); - // Latency with WinMM seems pretty bad from my testing... Need to increase the default buffer size. - if (pDevice->usingDefaultBufferSize) { - if (pDevice->type == mal_device_type_playback) { - pDevice->bufferSizeInFrames *= 4; // <-- Might need to fiddle with this to find a more ideal value. May even be able to just add a fixed amount rather than scaling. - } else { - pDevice->bufferSizeInFrames *= 2; + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + if (pDevice->usingDefaultBufferSize) { + float bufferSizeScaleFactor = 4; // <-- Latency with WinMM seems pretty bad from my testing... + pDevice->bufferSizeInFrames = mal_scale_buffer_size(pDevice->bufferSizeInFrames, bufferSizeScaleFactor); } } // The size of the intermediary buffer needs to be able to fit every fragment. pDevice->winmm.fragmentSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; - pDevice->winmm.fragmentSizeInBytes = pDevice->winmm.fragmentSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + pDevice->winmm.fragmentSizeInBytes = pDevice->winmm.fragmentSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); heapSize = (sizeof(WAVEHDR) * pDevice->periods) + (pDevice->winmm.fragmentSizeInBytes * pDevice->periods); pDevice->winmm._pHeapData = (mal_uint8*)mal_malloc(heapSize); @@ -5283,14 +9180,18 @@ on_error: } mal_free(pDevice->winmm._pHeapData); - return mal_post_error(pDevice, errorMsg, errorCode); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, errorMsg, errorCode); } -static mal_result mal_device__start_backend__winmm(mal_device* pDevice) +mal_result mal_device__start_backend__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); + if (pDevice->winmm.hDevice == NULL) { + return MAL_INVALID_ARGS; + } + if (pDevice->type == mal_device_type_playback) { // Playback. The device is started when we call waveOutWrite() with a block of data. From MSDN: // @@ -5307,7 +9208,7 @@ static mal_result mal_device__start_backend__winmm(mal_device* pDevice) mal_device__read_frames_from_client(pDevice, pDevice->winmm.fragmentSizeInFrames, ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].lpData); if (((MAL_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { - return mal_post_error(pDevice, "[WinMM] Failed to start backend device. Failed to prepare header.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device. Failed to prepare header.", MAL_FAILED_TO_START_BACKEND_DEVICE); } } @@ -5315,7 +9216,7 @@ static mal_result mal_device__start_backend__winmm(mal_device* pDevice) for (i = 0; i < pDevice->periods; ++i) { if (((MAL_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { - return mal_post_error(pDevice, "[WinMM] Failed to start backend device. Failed to send data to the backend device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device. Failed to send data to the backend device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } } } else { @@ -5329,13 +9230,13 @@ static mal_result mal_device__start_backend__winmm(mal_device* pDevice) MMRESULT resultMM = ((MAL_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] Failed to prepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to prepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); break; } resultMM = ((MAL_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] Failed to add new capture buffer to the internal capture device.", mal_result_from_MMRESULT(resultMM)); + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to add new capture buffer to the internal capture device.", mal_result_from_MMRESULT(resultMM)); break; } } @@ -5343,7 +9244,7 @@ static mal_result mal_device__start_backend__winmm(mal_device* pDevice) ResetEvent(pDevice->winmm.hEvent); if (((MAL_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDevice) != MMSYSERR_NOERROR) { - return mal_post_error(pDevice, "[WinMM] Failed to start backend device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } } @@ -5351,34 +9252,38 @@ static mal_result mal_device__start_backend__winmm(mal_device* pDevice) return MAL_SUCCESS; } -static mal_result mal_device__stop_backend__winmm(mal_device* pDevice) +mal_result mal_device__stop_backend__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); + if (pDevice->winmm.hDevice == NULL) { + return MAL_INVALID_ARGS; + } + if (pDevice->type == mal_device_type_playback) { MMRESULT resultMM = ((MAL_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevice); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] WARNING: Failed to reset playback device.", mal_result_from_MMRESULT(resultMM)); + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset playback device.", mal_result_from_MMRESULT(resultMM)); } // Unprepare all WAVEHDR structures. for (mal_uint32 i = 0; i < pDevice->periods; ++i) { resultMM = ((MAL_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] WARNING: Failed to unprepare header for playback device.", mal_result_from_MMRESULT(resultMM)); + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to unprepare header for playback device.", mal_result_from_MMRESULT(resultMM)); } } } else { MMRESULT resultMM = ((MAL_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDevice); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] WARNING: Failed to reset capture device.", mal_result_from_MMRESULT(resultMM)); + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset capture device.", mal_result_from_MMRESULT(resultMM)); } // Unprepare all WAVEHDR structures. for (mal_uint32 i = 0; i < pDevice->periods; ++i) { resultMM = ((MAL_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] WARNING: Failed to unprepare header for playback device.", mal_result_from_MMRESULT(resultMM)); + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to unprepare header for playback device.", mal_result_from_MMRESULT(resultMM)); } } } @@ -5386,7 +9291,7 @@ static mal_result mal_device__stop_backend__winmm(mal_device* pDevice) return MAL_SUCCESS; } -static mal_result mal_device__break_main_loop__winmm(mal_device* pDevice) +mal_result mal_device__break_main_loop__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -5396,7 +9301,7 @@ static mal_result mal_device__break_main_loop__winmm(mal_device* pDevice) return MAL_SUCCESS; } -static mal_result mal_device__main_loop__winmm(mal_device* pDevice) +mal_result mal_device__main_loop__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -5428,8 +9333,8 @@ static mal_result mal_device__main_loop__winmm(mal_device* pDevice) // Playback. MMRESULT resultMM = ((MAL_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] Failed to unprepare header for playback device in preparation for sending a new block of data to the device for playback.", mal_result_from_MMRESULT(resultMM)); - break; + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to unprepare header for playback device in preparation for sending a new block of data to the device for playback.", mal_result_from_MMRESULT(resultMM)); + return MAL_DEVICE_UNAVAILABLE; } mal_zero_object(&((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i]); @@ -5442,20 +9347,20 @@ static mal_result mal_device__main_loop__winmm(mal_device* pDevice) resultMM = ((MAL_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] Failed to prepare header for playback device in preparation for sending a new block of data to the device for playback.", mal_result_from_MMRESULT(resultMM)); - break; + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to prepare header for playback device in preparation for sending a new block of data to the device for playback.", mal_result_from_MMRESULT(resultMM)); + return MAL_DEVICE_UNAVAILABLE; } } else { // Capture. - mal_uint32 framesCaptured = (mal_uint32)(((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwBytesRecorded) / pDevice->internalChannels / mal_get_sample_size_in_bytes(pDevice->internalFormat); + mal_uint32 framesCaptured = (mal_uint32)(((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwBytesRecorded) / pDevice->internalChannels / mal_get_bytes_per_sample(pDevice->internalFormat); if (framesCaptured > 0) { mal_device__send_frames_to_client(pDevice, framesCaptured, ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].lpData); } MMRESULT resultMM = ((MAL_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] Failed to unprepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); - break; + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to unprepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); + return MAL_DEVICE_UNAVAILABLE; } mal_zero_object(&((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i]); @@ -5467,8 +9372,8 @@ static mal_result mal_device__main_loop__winmm(mal_device* pDevice) resultMM = ((MAL_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] Failed to prepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); - break; + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to prepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); + return MAL_DEVICE_UNAVAILABLE; } } @@ -5487,15 +9392,15 @@ static mal_result mal_device__main_loop__winmm(mal_device* pDevice) // Playback. MMRESULT resultMM = ((MAL_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] Failed to write data to the internal playback device.", mal_result_from_MMRESULT(resultMM)); - break; + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to write data to the internal playback device.", mal_result_from_MMRESULT(resultMM)); + return MAL_DEVICE_UNAVAILABLE; } } else { // Capture. MMRESULT resultMM = ((MAL_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { - mal_post_error(pDevice, "[WinMM] Failed to add new capture buffer to the internal capture device.", mal_result_from_MMRESULT(resultMM)); - break; + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to add new capture buffer to the internal capture device.", mal_result_from_MMRESULT(resultMM)); + return MAL_DEVICE_UNAVAILABLE; } } } @@ -5504,30 +9409,337 @@ static mal_result mal_device__main_loop__winmm(mal_device* pDevice) return MAL_SUCCESS; } + + +mal_result mal_context_uninit__winmm(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_winmm); + + mal_dlclose(pContext->winmm.hWinMM); + return MAL_SUCCESS; +} + +mal_result mal_context_init__winmm(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + pContext->winmm.hWinMM = mal_dlopen("winmm.dll"); + if (pContext->winmm.hWinMM == NULL) { + return MAL_NO_BACKEND; + } + + pContext->winmm.waveOutGetNumDevs = mal_dlsym(pContext->winmm.hWinMM, "waveOutGetNumDevs"); + pContext->winmm.waveOutGetDevCapsA = mal_dlsym(pContext->winmm.hWinMM, "waveOutGetDevCapsA"); + pContext->winmm.waveOutOpen = mal_dlsym(pContext->winmm.hWinMM, "waveOutOpen"); + pContext->winmm.waveOutClose = mal_dlsym(pContext->winmm.hWinMM, "waveOutClose"); + pContext->winmm.waveOutPrepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveOutPrepareHeader"); + pContext->winmm.waveOutUnprepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveOutUnprepareHeader"); + pContext->winmm.waveOutWrite = mal_dlsym(pContext->winmm.hWinMM, "waveOutWrite"); + pContext->winmm.waveOutReset = mal_dlsym(pContext->winmm.hWinMM, "waveOutReset"); + pContext->winmm.waveInGetNumDevs = mal_dlsym(pContext->winmm.hWinMM, "waveInGetNumDevs"); + pContext->winmm.waveInGetDevCapsA = mal_dlsym(pContext->winmm.hWinMM, "waveInGetDevCapsA"); + pContext->winmm.waveInOpen = mal_dlsym(pContext->winmm.hWinMM, "waveInOpen"); + pContext->winmm.waveInClose = mal_dlsym(pContext->winmm.hWinMM, "waveInClose"); + pContext->winmm.waveInPrepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveInPrepareHeader"); + pContext->winmm.waveInUnprepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveInUnprepareHeader"); + pContext->winmm.waveInAddBuffer = mal_dlsym(pContext->winmm.hWinMM, "waveInAddBuffer"); + pContext->winmm.waveInStart = mal_dlsym(pContext->winmm.hWinMM, "waveInStart"); + pContext->winmm.waveInReset = mal_dlsym(pContext->winmm.hWinMM, "waveInReset"); + + pContext->onUninit = mal_context_uninit__winmm; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__winmm; + pContext->onEnumDevices = mal_context_enumerate_devices__winmm; + pContext->onGetDeviceInfo = mal_context_get_device_info__winmm; + pContext->onDeviceInit = mal_device_init__winmm; + pContext->onDeviceUninit = mal_device_uninit__winmm; + pContext->onDeviceStart = mal_device__start_backend__winmm; + pContext->onDeviceStop = mal_device__stop_backend__winmm; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__winmm; + pContext->onDeviceMainLoop = mal_device__main_loop__winmm; + + return MAL_SUCCESS; +} #endif + /////////////////////////////////////////////////////////////////////////////// // // ALSA Backend // /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_HAS_ALSA + +#ifdef MAL_NO_RUNTIME_LINKING #include +typedef snd_pcm_uframes_t mal_snd_pcm_uframes_t; +typedef snd_pcm_sframes_t mal_snd_pcm_sframes_t; +typedef snd_pcm_stream_t mal_snd_pcm_stream_t; +typedef snd_pcm_format_t mal_snd_pcm_format_t; +typedef snd_pcm_access_t mal_snd_pcm_access_t; +typedef snd_pcm_t mal_snd_pcm_t; +typedef snd_pcm_hw_params_t mal_snd_pcm_hw_params_t; +typedef snd_pcm_sw_params_t mal_snd_pcm_sw_params_t; +typedef snd_pcm_format_mask_t mal_snd_pcm_format_mask_t; +typedef snd_pcm_info_t mal_snd_pcm_info_t; +typedef snd_pcm_channel_area_t mal_snd_pcm_channel_area_t; +typedef snd_pcm_chmap_t mal_snd_pcm_chmap_t; + +// snd_pcm_stream_t +#define MAL_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK +#define MAL_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE + +// snd_pcm_format_t +#define MAL_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN +#define MAL_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 +#define MAL_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE +#define MAL_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE +#define MAL_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE +#define MAL_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE +#define MAL_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE +#define MAL_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE +#define MAL_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE +#define MAL_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE +#define MAL_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE +#define MAL_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE +#define MAL_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW +#define MAL_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW +#define MAL_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE +#define MAL_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE + +// mal_snd_pcm_access_t +#define MAL_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED +#define MAL_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED +#define MAL_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX +#define MAL_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED +#define MAL_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED + +// Channel positions. +#define MAL_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN +#define MAL_SND_CHMAP_NA SND_CHMAP_NA +#define MAL_SND_CHMAP_MONO SND_CHMAP_MONO +#define MAL_SND_CHMAP_FL SND_CHMAP_FL +#define MAL_SND_CHMAP_FR SND_CHMAP_FR +#define MAL_SND_CHMAP_RL SND_CHMAP_RL +#define MAL_SND_CHMAP_RR SND_CHMAP_RR +#define MAL_SND_CHMAP_FC SND_CHMAP_FC +#define MAL_SND_CHMAP_LFE SND_CHMAP_LFE +#define MAL_SND_CHMAP_SL SND_CHMAP_SL +#define MAL_SND_CHMAP_SR SND_CHMAP_SR +#define MAL_SND_CHMAP_RC SND_CHMAP_RC +#define MAL_SND_CHMAP_FLC SND_CHMAP_FLC +#define MAL_SND_CHMAP_FRC SND_CHMAP_FRC +#define MAL_SND_CHMAP_RLC SND_CHMAP_RLC +#define MAL_SND_CHMAP_RRC SND_CHMAP_RRC +#define MAL_SND_CHMAP_FLW SND_CHMAP_FLW +#define MAL_SND_CHMAP_FRW SND_CHMAP_FRW +#define MAL_SND_CHMAP_FLH SND_CHMAP_FLH +#define MAL_SND_CHMAP_FCH SND_CHMAP_FCH +#define MAL_SND_CHMAP_FRH SND_CHMAP_FRH +#define MAL_SND_CHMAP_TC SND_CHMAP_TC +#define MAL_SND_CHMAP_TFL SND_CHMAP_TFL +#define MAL_SND_CHMAP_TFR SND_CHMAP_TFR +#define MAL_SND_CHMAP_TFC SND_CHMAP_TFC +#define MAL_SND_CHMAP_TRL SND_CHMAP_TRL +#define MAL_SND_CHMAP_TRR SND_CHMAP_TRR +#define MAL_SND_CHMAP_TRC SND_CHMAP_TRC +#define MAL_SND_CHMAP_TFLC SND_CHMAP_TFLC +#define MAL_SND_CHMAP_TFRC SND_CHMAP_TFRC +#define MAL_SND_CHMAP_TSL SND_CHMAP_TSL +#define MAL_SND_CHMAP_TSR SND_CHMAP_TSR +#define MAL_SND_CHMAP_LLFE SND_CHMAP_LLFE +#define MAL_SND_CHMAP_RLFE SND_CHMAP_RLFE +#define MAL_SND_CHMAP_BC SND_CHMAP_BC +#define MAL_SND_CHMAP_BLC SND_CHMAP_BLC +#define MAL_SND_CHMAP_BRC SND_CHMAP_BRC + +// Open mode flags. +#define MAL_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE +#define MAL_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS +#define MAL_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT +#else +#include // For EPIPE, etc. +typedef unsigned long mal_snd_pcm_uframes_t; +typedef long mal_snd_pcm_sframes_t; +typedef int mal_snd_pcm_stream_t; +typedef int mal_snd_pcm_format_t; +typedef int mal_snd_pcm_access_t; +typedef struct mal_snd_pcm_t mal_snd_pcm_t; +typedef struct mal_snd_pcm_hw_params_t mal_snd_pcm_hw_params_t; +typedef struct mal_snd_pcm_sw_params_t mal_snd_pcm_sw_params_t; +typedef struct mal_snd_pcm_format_mask_t mal_snd_pcm_format_mask_t; +typedef struct mal_snd_pcm_info_t mal_snd_pcm_info_t; +typedef struct +{ + void* addr; + unsigned int first; + unsigned int step; +} mal_snd_pcm_channel_area_t; +typedef struct +{ + unsigned int channels; + unsigned int pos[0]; +} mal_snd_pcm_chmap_t; + +// snd_pcm_stream_t +#define MAL_SND_PCM_STREAM_PLAYBACK 0 +#define MAL_SND_PCM_STREAM_CAPTURE 1 + +// snd_pcm_format_t +#define MAL_SND_PCM_FORMAT_UNKNOWN -1 +#define MAL_SND_PCM_FORMAT_U8 1 +#define MAL_SND_PCM_FORMAT_S16_LE 2 +#define MAL_SND_PCM_FORMAT_S16_BE 3 +#define MAL_SND_PCM_FORMAT_S24_LE 6 +#define MAL_SND_PCM_FORMAT_S24_BE 7 +#define MAL_SND_PCM_FORMAT_S32_LE 10 +#define MAL_SND_PCM_FORMAT_S32_BE 11 +#define MAL_SND_PCM_FORMAT_FLOAT_LE 14 +#define MAL_SND_PCM_FORMAT_FLOAT_BE 15 +#define MAL_SND_PCM_FORMAT_FLOAT64_LE 16 +#define MAL_SND_PCM_FORMAT_FLOAT64_BE 17 +#define MAL_SND_PCM_FORMAT_MU_LAW 20 +#define MAL_SND_PCM_FORMAT_A_LAW 21 +#define MAL_SND_PCM_FORMAT_S24_3LE 32 +#define MAL_SND_PCM_FORMAT_S24_3BE 33 + +// snd_pcm_access_t +#define MAL_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 +#define MAL_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 +#define MAL_SND_PCM_ACCESS_MMAP_COMPLEX 2 +#define MAL_SND_PCM_ACCESS_RW_INTERLEAVED 3 +#define MAL_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 + +// Channel positions. +#define MAL_SND_CHMAP_UNKNOWN 0 +#define MAL_SND_CHMAP_NA 1 +#define MAL_SND_CHMAP_MONO 2 +#define MAL_SND_CHMAP_FL 3 +#define MAL_SND_CHMAP_FR 4 +#define MAL_SND_CHMAP_RL 5 +#define MAL_SND_CHMAP_RR 6 +#define MAL_SND_CHMAP_FC 7 +#define MAL_SND_CHMAP_LFE 8 +#define MAL_SND_CHMAP_SL 9 +#define MAL_SND_CHMAP_SR 10 +#define MAL_SND_CHMAP_RC 11 +#define MAL_SND_CHMAP_FLC 12 +#define MAL_SND_CHMAP_FRC 13 +#define MAL_SND_CHMAP_RLC 14 +#define MAL_SND_CHMAP_RRC 15 +#define MAL_SND_CHMAP_FLW 16 +#define MAL_SND_CHMAP_FRW 17 +#define MAL_SND_CHMAP_FLH 18 +#define MAL_SND_CHMAP_FCH 19 +#define MAL_SND_CHMAP_FRH 20 +#define MAL_SND_CHMAP_TC 21 +#define MAL_SND_CHMAP_TFL 22 +#define MAL_SND_CHMAP_TFR 23 +#define MAL_SND_CHMAP_TFC 24 +#define MAL_SND_CHMAP_TRL 25 +#define MAL_SND_CHMAP_TRR 26 +#define MAL_SND_CHMAP_TRC 27 +#define MAL_SND_CHMAP_TFLC 28 +#define MAL_SND_CHMAP_TFRC 29 +#define MAL_SND_CHMAP_TSL 30 +#define MAL_SND_CHMAP_TSR 31 +#define MAL_SND_CHMAP_LLFE 32 +#define MAL_SND_CHMAP_RLFE 33 +#define MAL_SND_CHMAP_BC 34 +#define MAL_SND_CHMAP_BLC 35 +#define MAL_SND_CHMAP_BRC 36 + +// Open mode flags. +#define MAL_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 +#define MAL_SND_PCM_NO_AUTO_CHANNELS 0x00020000 +#define MAL_SND_PCM_NO_AUTO_FORMAT 0x00040000 +#endif + +typedef int (* mal_snd_pcm_open_proc) (mal_snd_pcm_t **pcm, const char *name, mal_snd_pcm_stream_t stream, int mode); +typedef int (* mal_snd_pcm_close_proc) (mal_snd_pcm_t *pcm); +typedef size_t (* mal_snd_pcm_hw_params_sizeof_proc) (void); +typedef int (* mal_snd_pcm_hw_params_any_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params); +typedef int (* mal_snd_pcm_hw_params_set_format_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params, mal_snd_pcm_format_t val); +typedef int (* mal_snd_pcm_hw_params_set_format_first_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params, mal_snd_pcm_format_t *format); +typedef void (* mal_snd_pcm_hw_params_get_format_mask_proc) (mal_snd_pcm_hw_params_t *params, mal_snd_pcm_format_mask_t *mask); +typedef int (* mal_snd_pcm_hw_params_set_channels_near_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* mal_snd_pcm_hw_params_set_rate_resample_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params, unsigned int val); +typedef int (* mal_snd_pcm_hw_params_set_rate_near_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* mal_snd_pcm_hw_params_set_buffer_size_near_proc)(mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params, mal_snd_pcm_uframes_t *val); +typedef int (* mal_snd_pcm_hw_params_set_periods_near_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* mal_snd_pcm_hw_params_set_access_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params, mal_snd_pcm_access_t _access); +typedef int (* mal_snd_pcm_hw_params_get_format_proc) (const mal_snd_pcm_hw_params_t *params, mal_snd_pcm_format_t *format); +typedef int (* mal_snd_pcm_hw_params_get_channels_proc) (const mal_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* mal_snd_pcm_hw_params_get_channels_min_proc) (const mal_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* mal_snd_pcm_hw_params_get_channels_max_proc) (const mal_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* mal_snd_pcm_hw_params_get_rate_proc) (const mal_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* mal_snd_pcm_hw_params_get_rate_min_proc) (const mal_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* mal_snd_pcm_hw_params_get_rate_max_proc) (const mal_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* mal_snd_pcm_hw_params_get_buffer_size_proc) (const mal_snd_pcm_hw_params_t *params, mal_snd_pcm_uframes_t *val); +typedef int (* mal_snd_pcm_hw_params_get_periods_proc) (const mal_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* mal_snd_pcm_hw_params_get_access_proc) (const mal_snd_pcm_hw_params_t *params, mal_snd_pcm_access_t *_access); +typedef int (* mal_snd_pcm_hw_params_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_hw_params_t *params); +typedef size_t (* mal_snd_pcm_sw_params_sizeof_proc) (void); +typedef int (* mal_snd_pcm_sw_params_current_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_sw_params_t *params); +typedef int (* mal_snd_pcm_sw_params_set_avail_min_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_sw_params_t *params, mal_snd_pcm_uframes_t val); +typedef int (* mal_snd_pcm_sw_params_set_start_threshold_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_sw_params_t *params, mal_snd_pcm_uframes_t val); +typedef int (* mal_snd_pcm_sw_params_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_sw_params_t *params); +typedef size_t (* mal_snd_pcm_format_mask_sizeof_proc) (void); +typedef int (* mal_snd_pcm_format_mask_test_proc) (const mal_snd_pcm_format_mask_t *mask, mal_snd_pcm_format_t val); +typedef mal_snd_pcm_chmap_t * (* mal_snd_pcm_get_chmap_proc) (mal_snd_pcm_t *pcm); +typedef int (* mal_snd_pcm_prepare_proc) (mal_snd_pcm_t *pcm); +typedef int (* mal_snd_pcm_start_proc) (mal_snd_pcm_t *pcm); +typedef int (* mal_snd_pcm_drop_proc) (mal_snd_pcm_t *pcm); +typedef int (* mal_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); +typedef char * (* mal_snd_device_name_get_hint_proc) (const void *hint, const char *id); +typedef int (* mal_snd_card_get_index_proc) (const char *name); +typedef int (* mal_snd_device_name_free_hint_proc) (void **hints); +typedef int (* mal_snd_pcm_mmap_begin_proc) (mal_snd_pcm_t *pcm, const mal_snd_pcm_channel_area_t **areas, mal_snd_pcm_uframes_t *offset, mal_snd_pcm_uframes_t *frames); +typedef mal_snd_pcm_sframes_t (* mal_snd_pcm_mmap_commit_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_uframes_t offset, mal_snd_pcm_uframes_t frames); +typedef int (* mal_snd_pcm_recover_proc) (mal_snd_pcm_t *pcm, int err, int silent); +typedef mal_snd_pcm_sframes_t (* mal_snd_pcm_readi_proc) (mal_snd_pcm_t *pcm, void *buffer, mal_snd_pcm_uframes_t size); +typedef mal_snd_pcm_sframes_t (* mal_snd_pcm_writei_proc) (mal_snd_pcm_t *pcm, const void *buffer, mal_snd_pcm_uframes_t size); +typedef mal_snd_pcm_sframes_t (* mal_snd_pcm_avail_proc) (mal_snd_pcm_t *pcm); +typedef mal_snd_pcm_sframes_t (* mal_snd_pcm_avail_update_proc) (mal_snd_pcm_t *pcm); +typedef int (* mal_snd_pcm_wait_proc) (mal_snd_pcm_t *pcm, int timeout); +typedef int (* mal_snd_pcm_info_proc) (mal_snd_pcm_t *pcm, mal_snd_pcm_info_t* info); +typedef size_t (* mal_snd_pcm_info_sizeof_proc) (); +typedef const char* (* mal_snd_pcm_info_get_name_proc) (const mal_snd_pcm_info_t* info); +typedef int (* mal_snd_config_update_free_global_proc) (); + +// This array specifies each of the common devices that can be used for both playback and capture. +const char* g_malCommonDeviceNamesALSA[] = { + "default", + "null", + "pulse", + "jack" +}; + +// This array allows us to blacklist specific playback devices. +const char* g_malBlacklistedPlaybackDeviceNamesALSA[] = { + "" +}; + +// This array allows us to blacklist specific capture devices. +const char* g_malBlacklistedCaptureDeviceNamesALSA[] = { + "" +}; + // This array allows mini_al to control device-specific default buffer sizes. This uses a scaling factor. Order is important. If // any part of the string is present in the device's name, the associated scale will be used. -struct +static struct { const char* name; float scale; } g_malDefaultBufferSizeScalesALSA[] = { - {"bcm2835 IEC958/HDMI", 20}, - {"bcm2835 ALSA", 20} + {"bcm2835 IEC958/HDMI", 2.0f}, + {"bcm2835 ALSA", 2.0f} }; -static float mal_find_default_buffer_size_scale__alsa(const char* deviceName) +float mal_find_default_buffer_size_scale__alsa(const char* deviceName) { if (deviceName == NULL) { return 1; @@ -5542,78 +9754,54 @@ static float mal_find_default_buffer_size_scale__alsa(const char* deviceName) return 1; } - -typedef int (* mal_snd_pcm_open_proc) (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode); -typedef int (* mal_snd_pcm_close_proc) (snd_pcm_t *pcm); -typedef size_t (* mal_snd_pcm_hw_params_sizeof_proc) (void); -typedef int (* mal_snd_pcm_hw_params_any_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -typedef int (* mal_snd_pcm_hw_params_set_format_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); -typedef int (* mal_snd_pcm_hw_params_set_format_first_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t *format); -typedef void (* mal_snd_pcm_hw_params_get_format_mask_proc) (snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); -typedef int (* mal_snd_pcm_hw_params_set_channels_near_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* mal_snd_pcm_hw_params_set_rate_resample_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* mal_snd_pcm_hw_params_set_rate_near_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* mal_snd_pcm_hw_params_set_buffer_size_near_proc)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -typedef int (* mal_snd_pcm_hw_params_set_periods_near_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* mal_snd_pcm_hw_params_set_access_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access); -typedef int (* mal_snd_pcm_hw_params_get_format_proc) (snd_pcm_hw_params_t *params, snd_pcm_format_t *format); -typedef int (* mal_snd_pcm_hw_params_get_channels_proc) (snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* mal_snd_pcm_hw_params_get_rate_proc) (snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* mal_snd_pcm_hw_params_get_buffer_size_proc) (snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); -typedef int (* mal_snd_pcm_hw_params_get_periods_proc) (snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* mal_snd_pcm_hw_params_get_access_proc) (snd_pcm_hw_params_t *params, snd_pcm_access_t *_access); -typedef int (* mal_snd_pcm_hw_params_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -typedef size_t (* mal_snd_pcm_sw_params_sizeof_proc) (void); -typedef int (* mal_snd_pcm_sw_params_current_proc) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params); -typedef int (* mal_snd_pcm_sw_params_set_avail_min_proc) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -typedef int (* mal_snd_pcm_sw_params_set_start_threshold_proc) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); -typedef int (* mal_snd_pcm_sw_params_proc) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params); -typedef size_t (* mal_snd_pcm_format_mask_sizeof_proc) (void); -typedef int (* mal_snd_pcm_format_mask_test_proc) (const snd_pcm_format_mask_t *mask, snd_pcm_format_t val); -typedef snd_pcm_chmap_t * (* mal_snd_pcm_get_chmap_proc) (snd_pcm_t *pcm); -typedef int (* mal_snd_pcm_prepare_proc) (snd_pcm_t *pcm); -typedef int (* mal_snd_pcm_start_proc) (snd_pcm_t *pcm); -typedef int (* mal_snd_pcm_drop_proc) (snd_pcm_t *pcm); -typedef int (* mal_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); -typedef char * (* mal_snd_device_name_get_hint_proc) (const void *hint, const char *id); -typedef int (* mal_snd_card_get_index_proc) (const char *name); -typedef int (* mal_snd_device_name_free_hint_proc) (void **hints); -typedef int (* mal_snd_pcm_mmap_begin_proc) (snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames); -typedef snd_pcm_sframes_t (* mal_snd_pcm_mmap_commit_proc) (snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames); -typedef int (* mal_snd_pcm_recover_proc) (snd_pcm_t *pcm, int err, int silent); -typedef snd_pcm_sframes_t (* mal_snd_pcm_readi_proc) (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size); -typedef snd_pcm_sframes_t (* mal_snd_pcm_writei_proc) (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); -typedef snd_pcm_sframes_t (* mal_snd_pcm_avail_proc) (snd_pcm_t *pcm); -typedef snd_pcm_sframes_t (* mal_snd_pcm_avail_update_proc) (snd_pcm_t *pcm); -typedef int (* mal_snd_pcm_wait_proc) (snd_pcm_t *pcm, int timeout); -typedef int (* mal_snd_pcm_info) (snd_pcm_t *pcm, snd_pcm_info_t* info); -typedef size_t (* mal_snd_pcm_info_sizeof) (); -typedef const char* (* mal_snd_pcm_info_get_name) (const snd_pcm_info_t* info); - -static snd_pcm_format_t g_mal_ALSAFormats[] = { - SND_PCM_FORMAT_UNKNOWN, // mal_format_unknown - SND_PCM_FORMAT_U8, // mal_format_u8 - SND_PCM_FORMAT_S16_LE, // mal_format_s16 - SND_PCM_FORMAT_S24_3LE, // mal_format_s24 - SND_PCM_FORMAT_S32_LE, // mal_format_s32 - SND_PCM_FORMAT_FLOAT_LE // mal_format_f32 -}; - -snd_pcm_format_t mal_convert_mal_format_to_alsa_format(mal_format format) +mal_snd_pcm_format_t mal_convert_mal_format_to_alsa_format(mal_format format) { - return g_mal_ALSAFormats[format]; + mal_snd_pcm_format_t ALSAFormats[] = { + MAL_SND_PCM_FORMAT_UNKNOWN, // mal_format_unknown + MAL_SND_PCM_FORMAT_U8, // mal_format_u8 + MAL_SND_PCM_FORMAT_S16_LE, // mal_format_s16 + MAL_SND_PCM_FORMAT_S24_3LE, // mal_format_s24 + MAL_SND_PCM_FORMAT_S32_LE, // mal_format_s32 + MAL_SND_PCM_FORMAT_FLOAT_LE // mal_format_f32 + }; + + if (mal_is_big_endian()) { + ALSAFormats[0] = MAL_SND_PCM_FORMAT_UNKNOWN; + ALSAFormats[1] = MAL_SND_PCM_FORMAT_U8; + ALSAFormats[2] = MAL_SND_PCM_FORMAT_S16_BE; + ALSAFormats[3] = MAL_SND_PCM_FORMAT_S24_3BE; + ALSAFormats[4] = MAL_SND_PCM_FORMAT_S32_BE; + ALSAFormats[5] = MAL_SND_PCM_FORMAT_FLOAT_BE; + } + + + return ALSAFormats[format]; } -mal_format mal_convert_alsa_format_to_mal_format(snd_pcm_format_t formatALSA) +mal_format mal_convert_alsa_format_to_mal_format(mal_snd_pcm_format_t formatALSA) { - switch (formatALSA) - { - case SND_PCM_FORMAT_U8: return mal_format_u8; - case SND_PCM_FORMAT_S16_LE: return mal_format_s16; - case SND_PCM_FORMAT_S24_3LE: return mal_format_s24; - case SND_PCM_FORMAT_S32_LE: return mal_format_s32; - case SND_PCM_FORMAT_FLOAT_LE: return mal_format_f32; - default: return mal_format_unknown; + if (mal_is_little_endian()) { + switch (formatALSA) { + case MAL_SND_PCM_FORMAT_S16_LE: return mal_format_s16; + case MAL_SND_PCM_FORMAT_S24_3LE: return mal_format_s24; + case MAL_SND_PCM_FORMAT_S32_LE: return mal_format_s32; + case MAL_SND_PCM_FORMAT_FLOAT_LE: return mal_format_f32; + default: break; + } + } else { + switch (formatALSA) { + case MAL_SND_PCM_FORMAT_S16_BE: return mal_format_s16; + case MAL_SND_PCM_FORMAT_S24_3BE: return mal_format_s24; + case MAL_SND_PCM_FORMAT_S32_BE: return mal_format_s32; + case MAL_SND_PCM_FORMAT_FLOAT_BE: return mal_format_f32; + default: break; + } + } + + // Endian agnostic. + switch (formatALSA) { + case MAL_SND_PCM_FORMAT_U8: return mal_format_u8; + default: return mal_format_unknown; } } @@ -5621,106 +9809,83 @@ mal_channel mal_convert_alsa_channel_position_to_mal_channel(unsigned int alsaCh { switch (alsaChannelPos) { - case SND_CHMAP_FL: return MAL_CHANNEL_FRONT_LEFT; - case SND_CHMAP_FR: return MAL_CHANNEL_FRONT_RIGHT; - case SND_CHMAP_RL: return MAL_CHANNEL_BACK_LEFT; - case SND_CHMAP_RR: return MAL_CHANNEL_BACK_RIGHT; - case SND_CHMAP_FC: return MAL_CHANNEL_FRONT_CENTER; - case SND_CHMAP_LFE: return MAL_CHANNEL_LFE; - case SND_CHMAP_SL: return MAL_CHANNEL_SIDE_LEFT; - case SND_CHMAP_SR: return MAL_CHANNEL_SIDE_RIGHT; - case SND_CHMAP_RC: return MAL_CHANNEL_BACK_CENTER; - case SND_CHMAP_FLC: return MAL_CHANNEL_FRONT_LEFT_CENTER; - case SND_CHMAP_FRC: return MAL_CHANNEL_FRONT_RIGHT_CENTER; - case SND_CHMAP_RLC: return 0; - case SND_CHMAP_RRC: return 0; - case SND_CHMAP_FLW: return 0; - case SND_CHMAP_FRW: return 0; - case SND_CHMAP_FLH: return 0; - case SND_CHMAP_FCH: return 0; - case SND_CHMAP_FRH: return 0; - case SND_CHMAP_TC: return MAL_CHANNEL_TOP_CENTER; - case SND_CHMAP_TFL: return MAL_CHANNEL_TOP_FRONT_LEFT; - case SND_CHMAP_TFR: return MAL_CHANNEL_TOP_FRONT_RIGHT; - case SND_CHMAP_TFC: return MAL_CHANNEL_TOP_FRONT_CENTER; - case SND_CHMAP_TRL: return MAL_CHANNEL_TOP_BACK_LEFT; - case SND_CHMAP_TRR: return MAL_CHANNEL_TOP_BACK_RIGHT; - case SND_CHMAP_TRC: return MAL_CHANNEL_TOP_BACK_CENTER; + case MAL_SND_CHMAP_MONO: return MAL_CHANNEL_MONO; + case MAL_SND_CHMAP_FL: return MAL_CHANNEL_FRONT_LEFT; + case MAL_SND_CHMAP_FR: return MAL_CHANNEL_FRONT_RIGHT; + case MAL_SND_CHMAP_RL: return MAL_CHANNEL_BACK_LEFT; + case MAL_SND_CHMAP_RR: return MAL_CHANNEL_BACK_RIGHT; + case MAL_SND_CHMAP_FC: return MAL_CHANNEL_FRONT_CENTER; + case MAL_SND_CHMAP_LFE: return MAL_CHANNEL_LFE; + case MAL_SND_CHMAP_SL: return MAL_CHANNEL_SIDE_LEFT; + case MAL_SND_CHMAP_SR: return MAL_CHANNEL_SIDE_RIGHT; + case MAL_SND_CHMAP_RC: return MAL_CHANNEL_BACK_CENTER; + case MAL_SND_CHMAP_FLC: return MAL_CHANNEL_FRONT_LEFT_CENTER; + case MAL_SND_CHMAP_FRC: return MAL_CHANNEL_FRONT_RIGHT_CENTER; + case MAL_SND_CHMAP_RLC: return 0; + case MAL_SND_CHMAP_RRC: return 0; + case MAL_SND_CHMAP_FLW: return 0; + case MAL_SND_CHMAP_FRW: return 0; + case MAL_SND_CHMAP_FLH: return 0; + case MAL_SND_CHMAP_FCH: return 0; + case MAL_SND_CHMAP_FRH: return 0; + case MAL_SND_CHMAP_TC: return MAL_CHANNEL_TOP_CENTER; + case MAL_SND_CHMAP_TFL: return MAL_CHANNEL_TOP_FRONT_LEFT; + case MAL_SND_CHMAP_TFR: return MAL_CHANNEL_TOP_FRONT_RIGHT; + case MAL_SND_CHMAP_TFC: return MAL_CHANNEL_TOP_FRONT_CENTER; + case MAL_SND_CHMAP_TRL: return MAL_CHANNEL_TOP_BACK_LEFT; + case MAL_SND_CHMAP_TRR: return MAL_CHANNEL_TOP_BACK_RIGHT; + case MAL_SND_CHMAP_TRC: return MAL_CHANNEL_TOP_BACK_CENTER; default: break; } return 0; } -mal_result mal_context_init__alsa(mal_context* pContext) +mal_bool32 mal_is_common_device_name__alsa(const char* name) { - mal_assert(pContext != NULL); - - pContext->alsa.asoundSO = mal_dlopen("libasound.so"); - if (pContext->alsa.asoundSO == NULL) { - return MAL_NO_BACKEND; + for (size_t iName = 0; iName < mal_countof(g_malCommonDeviceNamesALSA); ++iName) { + if (mal_strcmp(name, g_malCommonDeviceNamesALSA[iName]) == 0) { + return MAL_TRUE; + } } - pContext->alsa.snd_pcm_open = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_rate = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_prepare = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_device_name_hint = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_info = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - - return MAL_SUCCESS; + return MAL_FALSE; } -mal_result mal_context_uninit__alsa(mal_context* pContext) + +mal_bool32 mal_is_playback_device_blacklisted__alsa(const char* name) { - mal_assert(pContext != NULL); - mal_assert(pContext->backend == mal_backend_alsa); + for (size_t iName = 0; iName < mal_countof(g_malBlacklistedPlaybackDeviceNamesALSA); ++iName) { + if (mal_strcmp(name, g_malBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { + return MAL_TRUE; + } + } - (void)pContext; - return MAL_SUCCESS; + return MAL_FALSE; } -static const char* mal_find_char(const char* str, char c, int* index) +mal_bool32 mal_is_capture_device_blacklisted__alsa(const char* name) +{ + for (size_t iName = 0; iName < mal_countof(g_malBlacklistedCaptureDeviceNamesALSA); ++iName) { + if (mal_strcmp(name, g_malBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { + return MAL_TRUE; + } + } + + return MAL_FALSE; +} + +mal_bool32 mal_is_device_blacklisted__alsa(mal_device_type deviceType, const char* name) +{ + if (deviceType == mal_device_type_playback) { + return mal_is_playback_device_blacklisted__alsa(name); + } else { + return mal_is_capture_device_blacklisted__alsa(name); + } +} + + +const char* mal_find_char(const char* str, char c, int* index) { int i = 0; for (;;) { @@ -5743,264 +9908,7 @@ static const char* mal_find_char(const char* str, char c, int* index) return NULL; } -// Waits for a number of frames to become available for either capture or playback. The return -// value is the number of frames available. -// -// This will return early if the main loop is broken with mal_device__break_main_loop(). -static mal_uint32 mal_device__wait_for_frames__alsa(mal_device* pDevice, mal_bool32* pRequiresRestart) -{ - mal_assert(pDevice != NULL); - - if (pRequiresRestart) *pRequiresRestart = MAL_FALSE; - - mal_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; - - while (!pDevice->alsa.breakFromMainLoop) { - // Wait for something to become available. The timeout should not affect latency - it's only used to break from the wait - // so we can check whether or not the device has been stopped. - const int timeoutInMilliseconds = 10; - int waitResult = ((mal_snd_pcm_wait_proc)pDevice->pContext->alsa.snd_pcm_wait)((snd_pcm_t*)pDevice->alsa.pPCM, timeoutInMilliseconds); - if (waitResult < 0) { - if (waitResult == -EPIPE) { - if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, waitResult, MAL_TRUE) < 0) { - return 0; - } - - if (pRequiresRestart) *pRequiresRestart = MAL_TRUE; // A device recovery means a restart for mmap mode. - } - } - - if (pDevice->alsa.breakFromMainLoop) { - return 0; - } - - snd_pcm_sframes_t framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((snd_pcm_t*)pDevice->alsa.pPCM); - if (framesAvailable < 0) { - if (framesAvailable == -EPIPE) { - if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, framesAvailable, MAL_TRUE) < 0) { - return 0; - } - - if (pRequiresRestart) *pRequiresRestart = MAL_TRUE; // A device recovery means a restart for mmap mode. - - // Try again, but if it fails this time just return an error. - framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((snd_pcm_t*)pDevice->alsa.pPCM); - if (framesAvailable < 0) { - return 0; - } - } - } - - // Keep the returned number of samples consistent and based on the period size. - if (framesAvailable >= periodSizeInFrames) { - return periodSizeInFrames; - } - } - - // We'll get here if the loop was terminated. Just return whatever's available. - snd_pcm_sframes_t framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((snd_pcm_t*)pDevice->alsa.pPCM); - if (framesAvailable < 0) { - return 0; - } - - return framesAvailable; -} - -static mal_bool32 mal_device_write__alsa(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - if (!mal_device_is_started(pDevice) && mal_device__get_state(pDevice) != MAL_STATE_STARTING) { - return MAL_FALSE; - } - if (pDevice->alsa.breakFromMainLoop) { - return MAL_FALSE; - } - - - if (pDevice->alsa.isUsingMMap) { - // mmap. - mal_bool32 requiresRestart; - mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, &requiresRestart); - if (framesAvailable == 0) { - return MAL_FALSE; - } - - // Don't bother asking the client for more audio data if we're just stopping the device anyway. - if (pDevice->alsa.breakFromMainLoop) { - return MAL_FALSE; - } - - const snd_pcm_channel_area_t* pAreas; - snd_pcm_uframes_t mappedOffset; - snd_pcm_uframes_t mappedFrames = framesAvailable; - while (framesAvailable > 0) { - int result = ((mal_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames); - if (result < 0) { - return MAL_FALSE; - } - - if (mappedFrames > 0) { - void* pBuffer = (mal_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8); - mal_device__read_frames_from_client(pDevice, mappedFrames, pBuffer); - } - - result = ((mal_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames); - if (result < 0 || (snd_pcm_uframes_t)result != mappedFrames) { - ((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, result, MAL_TRUE); - return MAL_FALSE; - } - - if (requiresRestart) { - if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { - return MAL_FALSE; - } - } - - framesAvailable -= mappedFrames; - } - } else { - // readi/writei. - while (!pDevice->alsa.breakFromMainLoop) { - mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, NULL); - if (framesAvailable == 0) { - continue; - } - - // Don't bother asking the client for more audio data if we're just stopping the device anyway. - if (pDevice->alsa.breakFromMainLoop) { - return MAL_FALSE; - } - - mal_device__read_frames_from_client(pDevice, framesAvailable, pDevice->alsa.pIntermediaryBuffer); - - snd_pcm_sframes_t framesWritten = ((mal_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); - if (framesWritten < 0) { - if (framesWritten == -EAGAIN) { - continue; // Just keep trying... - } else if (framesWritten == -EPIPE) { - // Underrun. Just recover and try writing again. - if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, framesWritten, MAL_TRUE) < 0) { - mal_post_error(pDevice, "[ALSA] Failed to recover device after underrun.", MAL_ALSA_FAILED_TO_RECOVER_DEVICE); - return MAL_FALSE; - } - - framesWritten = ((mal_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); - if (framesWritten < 0) { - mal_post_error(pDevice, "[ALSA] Failed to write data to the internal device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); - return MAL_FALSE; - } - - break; // Success. - } else { - mal_post_error(pDevice, "[ALSA] snd_pcm_writei() failed when writing initial data.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); - return MAL_FALSE; - } - } else { - break; // Success. - } - } - } - - return MAL_TRUE; -} - -static mal_bool32 mal_device_read__alsa(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - if (!mal_device_is_started(pDevice)) { - return MAL_FALSE; - } - if (pDevice->alsa.breakFromMainLoop) { - return MAL_FALSE; - } - - mal_uint32 framesToSend = 0; - void* pBuffer = NULL; - if (pDevice->alsa.pIntermediaryBuffer == NULL) { - // mmap. - mal_bool32 requiresRestart; - mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, &requiresRestart); - if (framesAvailable == 0) { - return MAL_FALSE; - } - - const snd_pcm_channel_area_t* pAreas; - snd_pcm_uframes_t mappedOffset; - snd_pcm_uframes_t mappedFrames = framesAvailable; - while (framesAvailable > 0) { - int result = ((mal_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames); - if (result < 0) { - return MAL_FALSE; - } - - if (mappedFrames > 0) { - void* pBuffer = (mal_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8); - mal_device__send_frames_to_client(pDevice, mappedFrames, pBuffer); - } - - result = ((mal_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames); - if (result < 0 || (snd_pcm_uframes_t)result != mappedFrames) { - ((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, result, MAL_TRUE); - return MAL_FALSE; - } - - if (requiresRestart) { - if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { - return MAL_FALSE; - } - } - - framesAvailable -= mappedFrames; - } - } else { - // readi/writei. - snd_pcm_sframes_t framesRead = 0; - while (!pDevice->alsa.breakFromMainLoop) { - mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, NULL); - if (framesAvailable == 0) { - continue; - } - - framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); - if (framesRead < 0) { - if (framesRead == -EAGAIN) { - continue; // Just keep trying... - } else if (framesRead == -EPIPE) { - // Overrun. Just recover and try reading again. - if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, framesRead, MAL_TRUE) < 0) { - mal_post_error(pDevice, "[ALSA] Failed to recover device after overrun.", MAL_ALSA_FAILED_TO_RECOVER_DEVICE); - return MAL_FALSE; - } - - framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); - if (framesRead < 0) { - mal_post_error(pDevice, "[ALSA] Failed to read data from the internal device.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); - return MAL_FALSE; - } - - break; // Success. - } else { - return MAL_FALSE; - } - } else { - break; // Success. - } - } - - framesToSend = framesRead; - pBuffer = pDevice->alsa.pIntermediaryBuffer; - } - - if (framesToSend > 0) { - mal_device__send_frames_to_client(pDevice, framesToSend, pBuffer); - } - - return MAL_TRUE; -} - - - -static mal_bool32 mal_is_device_name_in_hw_format__alsa(const char* hwid) +mal_bool32 mal_is_device_name_in_hw_format__alsa(const char* hwid) { // This function is just checking whether or not hwid is in "hw:%d,%d" format. @@ -6041,7 +9949,7 @@ static mal_bool32 mal_is_device_name_in_hw_format__alsa(const char* hwid) return MAL_TRUE; } -static int mal_convert_device_name_to_hw_format__alsa(mal_context* pContext, char* dst, size_t dstSize, const char* src) // Returns 0 on success, non-0 on error. +int mal_convert_device_name_to_hw_format__alsa(mal_context* pContext, char* dst, size_t dstSize, const char* src) // Returns 0 on success, non-0 on error. { // src should look something like this: "hw:CARD=I82801AAICH,DEV=0" @@ -6098,7 +10006,7 @@ static int mal_convert_device_name_to_hw_format__alsa(mal_context* pContext, cha return 0; } -static mal_bool32 mal_does_id_exist_in_list__alsa(mal_device_id* pUniqueIDs, mal_uint32 count, const char* pHWID) +mal_bool32 mal_does_id_exist_in_list__alsa(mal_device_id* pUniqueIDs, mal_uint32 count, const char* pHWID) { mal_assert(pHWID != NULL); @@ -6111,175 +10019,20 @@ static mal_bool32 mal_does_id_exist_in_list__alsa(mal_device_id* pUniqueIDs, mal return MAL_FALSE; } -static mal_result mal_enumerate_devices__alsa(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) + +mal_result mal_context_open_pcm__alsa(mal_context* pContext, mal_share_mode shareMode, mal_device_type type, const mal_device_id* pDeviceID, mal_snd_pcm_t** ppPCM) { - (void)pContext; + mal_assert(pContext != NULL); + mal_assert(ppPCM != NULL); - mal_uint32 infoSize = *pCount; - *pCount = 0; + *ppPCM = NULL; - char** ppDeviceHints; - if (((mal_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) { - return MAL_NO_BACKEND; - } + mal_snd_pcm_t* pPCM = NULL; - mal_device_id* pUniqueIDs = NULL; - mal_uint32 uniqueIDCount = 0; + - char** ppNextDeviceHint = ppDeviceHints; - while (*ppNextDeviceHint != NULL) { - char* NAME = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); - char* DESC = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); - char* IOID = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); - - // Only include devices if they are of the correct type. Special cases for "default", "null" and "pulse" - these are included for both playback and capture - // regardless of the IOID setting. - mal_bool32 includeThisDevice = MAL_FALSE; - if (strcmp(NAME, "default") == 0 || strcmp(NAME, "pulse") == 0 || strcmp(NAME, "null") == 0) { - includeThisDevice = MAL_TRUE; - - // Exclude the "null" device if requested. - if (strcmp(NAME, "null") == 0 && pContext->config.alsa.excludeNullDevice) { - includeThisDevice = MAL_FALSE; - } - } else { - if ((type == mal_device_type_playback && (IOID == NULL || strcmp(IOID, "Output") == 0)) || - (type == mal_device_type_capture && (IOID != NULL && strcmp(IOID, "Input" ) == 0))) { - includeThisDevice = MAL_TRUE; - } - } - - - - if (includeThisDevice) { -#if 0 - printf("NAME: %s\n", NAME); - printf("DESC: %s\n", DESC); - printf("IOID: %s\n", IOID); - - char hwid2[256]; - mal_convert_device_name_to_hw_format__alsa(pContext, hwid2, sizeof(hwid2), NAME); - printf("DEVICE ID: %s (%d)\n\n", hwid2, *pCount); -#endif - - char hwid[sizeof(pUniqueIDs->alsa)]; - if (NAME != NULL) { - if (pContext->config.alsa.useVerboseDeviceEnumeration) { - // Verbose mode. Use the name exactly as-is. - mal_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } else { - // Simplified mode. Use ":%d,%d" format. - if (mal_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { - // At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the - // plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device - // initialization time and is used as an indicator to try and use the most appropriate plugin depending on the - // device type and sharing mode. - char* dst = hwid; - char* src = hwid+2; - while ((*dst++ = *src++)); - } else { - // Conversion to "hw:%d,%d" failed. Just use the name as-is. - mal_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } - - if (mal_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { - goto next_device; // The device has already been enumerated. Move on to the next one. - } else { - // The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. - mal_device_id* pNewUniqueIDs = mal_realloc(pUniqueIDs, sizeof(*pUniqueIDs) * (uniqueIDCount + 1)); - if (pNewUniqueIDs == NULL) { - goto next_device; // Failed to allocate memory. - } - - pUniqueIDs = pNewUniqueIDs; - mal_copy_memory(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); - uniqueIDCount += 1; - } - } - } else { - mal_zero_memory(hwid, sizeof(hwid)); - } - - if (pInfo != NULL) { - if (infoSize > 0) { - mal_zero_object(pInfo); - mal_strncpy_s(pInfo->id.alsa, sizeof(pInfo->id.alsa), hwid, (size_t)-1); - - // DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose - // device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish - // between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the - // description. - // - // The value in DESC seems to be split into two lines, with the first line being the name of the device and the - // second line being a description of the device. I don't like having the description be across two lines because - // it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line - // being put into parentheses. In simplified mode I'm just stripping the second line entirely. - if (DESC != NULL) { - int lfPos; - const char* line2 = mal_find_char(DESC, '\n', &lfPos); - if (line2 != NULL) { - line2 += 1; // Skip past the new-line character. - - if (pContext->config.alsa.useVerboseDeviceEnumeration) { - // Verbose mode. Put the second line in brackets. - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), DESC, lfPos); - mal_strcat_s (pInfo->name, sizeof(pInfo->name), " ("); - mal_strcat_s (pInfo->name, sizeof(pInfo->name), line2); - mal_strcat_s (pInfo->name, sizeof(pInfo->name), ")"); - } else { - // Simplified mode. Strip the second line entirely. - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), DESC, lfPos); - } - } else { - // There's no second line. Just copy the whole description. - mal_strcpy_s(pInfo->name, sizeof(pInfo->name), DESC); - } - } - - pInfo += 1; - infoSize -= 1; - *pCount += 1; - } - } else { - *pCount += 1; - } - } - - next_device: - free(NAME); - free(DESC); - free(IOID); - ppNextDeviceHint += 1; - } - - mal_free(pUniqueIDs); - - ((mal_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); - return MAL_SUCCESS; -} - -static void mal_device_uninit__alsa(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - if ((snd_pcm_t*)pDevice->alsa.pPCM) { - ((mal_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((snd_pcm_t*)pDevice->alsa.pPCM); - - if (pDevice->alsa.pIntermediaryBuffer != NULL) { - mal_free(pDevice->alsa.pIntermediaryBuffer); - } - } -} - -static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) -{ - (void)pContext; - - mal_assert(pDevice != NULL); - mal_zero_object(&pDevice->alsa); - - snd_pcm_format_t formatALSA = mal_convert_mal_format_to_alsa_format(pConfig->format); - snd_pcm_stream_t stream = (type == mal_device_type_playback) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE; + mal_snd_pcm_stream_t stream = (type == mal_device_type_playback) ? MAL_SND_PCM_STREAM_PLAYBACK : MAL_SND_PCM_STREAM_CAPTURE; + int openMode = MAL_SND_PCM_NO_AUTO_RESAMPLE | MAL_SND_PCM_NO_AUTO_CHANNELS | MAL_SND_PCM_NO_AUTO_FORMAT; if (pDeviceID == NULL) { // We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes @@ -6294,7 +10047,7 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t NULL }; - if (pConfig->preferExclusiveMode) { + if (shareMode == mal_share_mode_exclusive) { defaultDeviceNames[1] = "hw"; defaultDeviceNames[2] = "hw:0"; defaultDeviceNames[3] = "hw:0,0"; @@ -6316,7 +10069,7 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t mal_bool32 isDeviceOpen = MAL_FALSE; for (size_t i = 0; i < mal_countof(defaultDeviceNames); ++i) { if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { - if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((snd_pcm_t**)&pDevice->alsa.pPCM, defaultDeviceNames[i], stream, 0) == 0) { + if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { isDeviceOpen = MAL_TRUE; break; } @@ -6324,8 +10077,7 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t } if (!isDeviceOpen) { - mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.", MAL_ALSA_FAILED_TO_OPEN_DEVICE); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } } else { // We're trying to open a specific device. There's a few things to consider here: @@ -6333,28 +10085,32 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t // mini_al recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When // an ID of this format is specified, it indicates to mini_al that it can try different combinations of plugins ("hw", "dmix", etc.) until it // finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). + + // May end up needing to make small adjustments to the ID, so make a copy. + mal_device_id deviceID = *pDeviceID; + mal_bool32 isDeviceOpen = MAL_FALSE; - if (pDeviceID->alsa[0] != ':') { + if (deviceID.alsa[0] != ':') { // The ID is not in ":0,0" format. Use the ID exactly as-is. - if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((snd_pcm_t**)&pDevice->alsa.pPCM, pDeviceID->alsa, stream, 0) == 0) { + if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode) == 0) { isDeviceOpen = MAL_TRUE; } } else { // The ID is in ":0,0" format. Try different plugins depending on the shared mode. - if (pDeviceID->alsa[1] == '\0') { - pDeviceID->alsa[0] = '\0'; // An ID of ":" should be converted to "". + if (deviceID.alsa[1] == '\0') { + deviceID.alsa[0] = '\0'; // An ID of ":" should be converted to "". } char hwid[256]; - if (!pConfig->preferExclusiveMode) { + if (shareMode == mal_share_mode_shared) { if (type == mal_device_type_playback) { mal_strcpy_s(hwid, sizeof(hwid), "dmix"); } else { mal_strcpy_s(hwid, sizeof(hwid), "dsnoop"); } - if (mal_strcat_s(hwid, sizeof(hwid), pDeviceID->alsa) == 0) { - if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((snd_pcm_t**)&pDevice->alsa.pPCM, hwid, stream, 0) == 0) { + if (mal_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { + if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) { isDeviceOpen = MAL_TRUE; } } @@ -6363,8 +10119,8 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t // If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. if (!isDeviceOpen) { mal_strcpy_s(hwid, sizeof(hwid), "hw"); - if (mal_strcat_s(hwid, sizeof(hwid), pDeviceID->alsa) == 0) { - if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((snd_pcm_t**)&pDevice->alsa.pPCM, hwid, stream, 0) == 0) { + if (mal_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { + if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) { isDeviceOpen = MAL_TRUE; } } @@ -6372,22 +10128,587 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t } if (!isDeviceOpen) { - mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] snd_pcm_open() failed.", MAL_ALSA_FAILED_TO_OPEN_DEVICE); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } } - // We may need to scale the size of the buffer depending on the device. + *ppPCM = pPCM; + return MAL_SUCCESS; +} + + +mal_bool32 mal_context_is_device_id_equal__alsa(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return mal_strcmp(pID0->alsa, pID1->alsa) == 0; +} + +mal_result mal_context_enumerate_devices__alsa(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + mal_bool32 cbResult = MAL_TRUE; + + mal_mutex_lock(&pContext->alsa.internalDeviceEnumLock); + + char** ppDeviceHints; + if (((mal_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) { + mal_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); + return MAL_NO_BACKEND; + } + + mal_device_id* pUniqueIDs = NULL; + mal_uint32 uniqueIDCount = 0; + + char** ppNextDeviceHint = ppDeviceHints; + while (*ppNextDeviceHint != NULL) { + char* NAME = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); + char* DESC = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); + char* IOID = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); + + mal_device_type deviceType = mal_device_type_playback; + if ((IOID == NULL || mal_strcmp(IOID, "Output") == 0)) { + deviceType = mal_device_type_playback; + } + if ((IOID != NULL && mal_strcmp(IOID, "Input" ) == 0)) { + deviceType = mal_device_type_capture; + } + + mal_bool32 stopEnumeration = MAL_FALSE; +#if 0 + printf("NAME: %s\n", NAME); + printf("DESC: %s\n", DESC); + printf("IOID: %s\n", IOID); + + char hwid2[256]; + mal_convert_device_name_to_hw_format__alsa(pContext, hwid2, sizeof(hwid2), NAME); + printf("DEVICE ID: %s\n\n", hwid2); +#endif + + char hwid[sizeof(pUniqueIDs->alsa)]; + if (NAME != NULL) { + if (pContext->config.alsa.useVerboseDeviceEnumeration) { + // Verbose mode. Use the name exactly as-is. + mal_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); + } else { + // Simplified mode. Use ":%d,%d" format. + if (mal_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { + // At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the + // plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device + // initialization time and is used as an indicator to try and use the most appropriate plugin depending on the + // device type and sharing mode. + char* dst = hwid; + char* src = hwid+2; + while ((*dst++ = *src++)); + } else { + // Conversion to "hw:%d,%d" failed. Just use the name as-is. + mal_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); + } + + if (mal_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { + goto next_device; // The device has already been enumerated. Move on to the next one. + } else { + // The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. + mal_device_id* pNewUniqueIDs = (mal_device_id*)mal_realloc(pUniqueIDs, sizeof(*pUniqueIDs) * (uniqueIDCount + 1)); + if (pNewUniqueIDs == NULL) { + goto next_device; // Failed to allocate memory. + } + + pUniqueIDs = pNewUniqueIDs; + mal_copy_memory(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); + uniqueIDCount += 1; + } + } + } else { + mal_zero_memory(hwid, sizeof(hwid)); + } + + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); + + // DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose + // device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish + // between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the + // description. + // + // The value in DESC seems to be split into two lines, with the first line being the name of the device and the + // second line being a description of the device. I don't like having the description be across two lines because + // it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line + // being put into parentheses. In simplified mode I'm just stripping the second line entirely. + if (DESC != NULL) { + int lfPos; + const char* line2 = mal_find_char(DESC, '\n', &lfPos); + if (line2 != NULL) { + line2 += 1; // Skip past the new-line character. + + if (pContext->config.alsa.useVerboseDeviceEnumeration) { + // Verbose mode. Put the second line in brackets. + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); + mal_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); + mal_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); + mal_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); + } else { + // Simplified mode. Strip the second line entirely. + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); + } + } else { + // There's no second line. Just copy the whole description. + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); + } + } + + if (!mal_is_device_blacklisted__alsa(deviceType, NAME)) { + cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); + } + + // Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback + // again for the other device type in this case. We do this for known devices. + if (cbResult) { + if (mal_is_common_device_name__alsa(NAME)) { + if (deviceType == mal_device_type_playback) { + if (!mal_is_capture_device_blacklisted__alsa(NAME)) { + cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } + } else { + if (!mal_is_playback_device_blacklisted__alsa(NAME)) { + cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + } + } + } + + if (cbResult == MAL_FALSE) { + stopEnumeration = MAL_TRUE; + } + + next_device: + free(NAME); + free(DESC); + free(IOID); + ppNextDeviceHint += 1; + + // We need to stop enumeration if the callback returned false. + if (stopEnumeration) { + break; + } + } + + mal_free(pUniqueIDs); + ((mal_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); + + mal_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); + + return MAL_SUCCESS; +} + + +typedef struct +{ + mal_device_type deviceType; + const mal_device_id* pDeviceID; + mal_share_mode shareMode; + mal_device_info* pDeviceInfo; + mal_bool32 foundDevice; +} mal_context_get_device_info_enum_callback_data__alsa; + +mal_bool32 mal_context_get_device_info_enum_callback__alsa(mal_context* pContext, mal_device_type deviceType, const mal_device_info* pDeviceInfo, void* pUserData) +{ + mal_context_get_device_info_enum_callback_data__alsa* pData = (mal_context_get_device_info_enum_callback_data__alsa*)pUserData; + mal_assert(pData != NULL); + + if (pData->pDeviceID == NULL && mal_strcmp(pDeviceInfo->id.alsa, "default") == 0) { + mal_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); + pData->foundDevice = MAL_TRUE; + } else { + if (pData->deviceType == deviceType && mal_context_is_device_id_equal__alsa(pContext, pData->pDeviceID, &pDeviceInfo->id)) { + mal_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); + pData->foundDevice = MAL_TRUE; + } + } + + // Keep enumerating until we have found the device. + return !pData->foundDevice; +} + +mal_result mal_context_get_device_info__alsa(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + + // We just enumerate to find basic information about the device. + mal_context_get_device_info_enum_callback_data__alsa data; + data.deviceType = deviceType; + data.pDeviceID = pDeviceID; + data.shareMode = shareMode; + data.pDeviceInfo = pDeviceInfo; + data.foundDevice = MAL_FALSE; + mal_result result = mal_context_enumerate_devices__alsa(pContext, mal_context_get_device_info_enum_callback__alsa, &data); + if (result != MAL_SUCCESS) { + return result; + } + + if (!data.foundDevice) { + return MAL_NO_DEVICE; + } + + + // For detailed info we need to open the device. + mal_snd_pcm_t* pPCM; + result = mal_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, &pPCM); + if (result != MAL_SUCCESS) { + return result; + } + + // We need to initialize a HW parameters object in order to know what formats are supported. + mal_snd_pcm_hw_params_t* pHWParams = (mal_snd_pcm_hw_params_t*)alloca(((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); + mal_zero_memory(pHWParams, ((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); + + if (((mal_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams) < 0) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE); + } + + int sampleRateDir = 0; + + ((mal_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &pDeviceInfo->minChannels); + ((mal_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &pDeviceInfo->maxChannels); + ((mal_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &pDeviceInfo->minSampleRate, &sampleRateDir); + ((mal_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &pDeviceInfo->maxSampleRate, &sampleRateDir); + + // Formats. + mal_snd_pcm_format_mask_t* pFormatMask = (mal_snd_pcm_format_mask_t*)alloca(((mal_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)()); + mal_zero_memory(pFormatMask, ((mal_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)()); + ((mal_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask); + + pDeviceInfo->formatCount = 0; + if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_U8)) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_u8; + } + if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_S16_LE)) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s16; + } + if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_S24_3LE)) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s24; + } + if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_S32_LE)) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s32; + } + if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MAL_SND_PCM_FORMAT_FLOAT_LE)) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_f32; + } + + ((mal_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + return MAL_SUCCESS; +} + + +// Waits for a number of frames to become available for either capture or playback. The return +// value is the number of frames available. +// +// This will return early if the main loop is broken with mal_device__break_main_loop(). +mal_uint32 mal_device__wait_for_frames__alsa(mal_device* pDevice, mal_bool32* pRequiresRestart) +{ + mal_assert(pDevice != NULL); + + if (pRequiresRestart) *pRequiresRestart = MAL_FALSE; + + // I want it so that this function returns the period size in frames. We just wait until that number of frames are available and then return. + mal_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; + while (!pDevice->alsa.breakFromMainLoop) { + mal_snd_pcm_sframes_t framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((mal_snd_pcm_t*)pDevice->alsa.pPCM); + if (framesAvailable < 0) { + if (framesAvailable == -EPIPE) { + if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, framesAvailable, MAL_TRUE) < 0) { + return 0; + } + + // A device recovery means a restart for mmap mode. + if (pRequiresRestart) { + *pRequiresRestart = MAL_TRUE; + } + + // Try again, but if it fails this time just return an error. + framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((mal_snd_pcm_t*)pDevice->alsa.pPCM); + if (framesAvailable < 0) { + return 0; + } + } + } + + if (framesAvailable >= periodSizeInFrames) { + return periodSizeInFrames; + } + + if (framesAvailable < periodSizeInFrames) { + // Less than a whole period is available so keep waiting. + int waitResult = ((mal_snd_pcm_wait_proc)pDevice->pContext->alsa.snd_pcm_wait)((mal_snd_pcm_t*)pDevice->alsa.pPCM, -1); + if (waitResult < 0) { + if (waitResult == -EPIPE) { + if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, waitResult, MAL_TRUE) < 0) { + return 0; + } + + // A device recovery means a restart for mmap mode. + if (pRequiresRestart) { + *pRequiresRestart = MAL_TRUE; + } + } + } + } + } + + // We'll get here if the loop was terminated. Just return whatever's available. + mal_snd_pcm_sframes_t framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((mal_snd_pcm_t*)pDevice->alsa.pPCM); + if (framesAvailable < 0) { + return 0; + } + + return framesAvailable; +} + +mal_bool32 mal_device_write__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + if (!mal_device_is_started(pDevice) && mal_device__get_state(pDevice) != MAL_STATE_STARTING) { + return MAL_FALSE; + } + if (pDevice->alsa.breakFromMainLoop) { + return MAL_FALSE; + } + + if (pDevice->alsa.isUsingMMap) { + // mmap. + mal_bool32 requiresRestart; + mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, &requiresRestart); + if (framesAvailable == 0) { + return MAL_FALSE; + } + + // Don't bother asking the client for more audio data if we're just stopping the device anyway. + if (pDevice->alsa.breakFromMainLoop) { + return MAL_FALSE; + } + + const mal_snd_pcm_channel_area_t* pAreas; + mal_snd_pcm_uframes_t mappedOffset; + mal_snd_pcm_uframes_t mappedFrames = framesAvailable; + while (framesAvailable > 0) { + int result = ((mal_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((mal_snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames); + if (result < 0) { + return MAL_FALSE; + } + + if (mappedFrames > 0) { + void* pBuffer = (mal_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8); + mal_device__read_frames_from_client(pDevice, mappedFrames, pBuffer); + } + + result = ((mal_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((mal_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames); + if (result < 0 || (mal_snd_pcm_uframes_t)result != mappedFrames) { + ((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, result, MAL_TRUE); + return MAL_FALSE; + } + + if (requiresRestart) { + if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((mal_snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return MAL_FALSE; + } + } + + if (framesAvailable >= mappedFrames) { + framesAvailable -= mappedFrames; + } else { + framesAvailable = 0; + } + } + } else { + // readi/writei. + while (!pDevice->alsa.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, NULL); + if (framesAvailable == 0) { + continue; + } + + // Don't bother asking the client for more audio data if we're just stopping the device anyway. + if (pDevice->alsa.breakFromMainLoop) { + return MAL_FALSE; + } + + mal_device__read_frames_from_client(pDevice, framesAvailable, pDevice->alsa.pIntermediaryBuffer); + + mal_snd_pcm_sframes_t framesWritten = ((mal_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); + if (framesWritten < 0) { + if (framesWritten == -EAGAIN) { + continue; // Just keep trying... + } else if (framesWritten == -EPIPE) { + // Underrun. Just recover and try writing again. + if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, framesWritten, MAL_TRUE) < 0) { + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return MAL_FALSE; + } + + framesWritten = ((mal_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); + if (framesWritten < 0) { + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to the internal device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + return MAL_FALSE; + } + + break; // Success. + } else { + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_writei() failed when writing initial data.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + return MAL_FALSE; + } + } else { + break; // Success. + } + } + } + + return MAL_TRUE; +} + +mal_bool32 mal_device_read__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + if (!mal_device_is_started(pDevice)) { + return MAL_FALSE; + } + if (pDevice->alsa.breakFromMainLoop) { + return MAL_FALSE; + } + + mal_uint32 framesToSend = 0; + void* pBuffer = NULL; + if (pDevice->alsa.pIntermediaryBuffer == NULL) { + // mmap. + mal_bool32 requiresRestart; + mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, &requiresRestart); + if (framesAvailable == 0) { + return MAL_FALSE; + } + + const mal_snd_pcm_channel_area_t* pAreas; + mal_snd_pcm_uframes_t mappedOffset; + mal_snd_pcm_uframes_t mappedFrames = framesAvailable; + while (framesAvailable > 0) { + int result = ((mal_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((mal_snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames); + if (result < 0) { + return MAL_FALSE; + } + + if (mappedFrames > 0) { + void* pBuffer = (mal_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8); + mal_device__send_frames_to_client(pDevice, mappedFrames, pBuffer); + } + + result = ((mal_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((mal_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames); + if (result < 0 || (mal_snd_pcm_uframes_t)result != mappedFrames) { + ((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, result, MAL_TRUE); + return MAL_FALSE; + } + + if (requiresRestart) { + if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((mal_snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return MAL_FALSE; + } + } + + if (framesAvailable >= mappedFrames) { + framesAvailable -= mappedFrames; + } else { + framesAvailable = 0; + } + } + } else { + // readi/writei. + mal_snd_pcm_sframes_t framesRead = 0; + while (!pDevice->alsa.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, NULL); + if (framesAvailable == 0) { + continue; + } + + framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); + if (framesRead < 0) { + if (framesRead == -EAGAIN) { + continue; // Just keep trying... + } else if (framesRead == -EPIPE) { + // Overrun. Just recover and try reading again. + if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, framesRead, MAL_TRUE) < 0) { + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return MAL_FALSE; + } + + framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); + if (framesRead < 0) { + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + return MAL_FALSE; + } + + break; // Success. + } else { + return MAL_FALSE; + } + } else { + break; // Success. + } + } + + framesToSend = framesRead; + pBuffer = pDevice->alsa.pIntermediaryBuffer; + } + + if (framesToSend > 0) { + mal_device__send_frames_to_client(pDevice, framesToSend, pBuffer); + } + + return MAL_TRUE; +} + +void mal_device_uninit__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if ((mal_snd_pcm_t*)pDevice->alsa.pPCM) { + ((mal_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((mal_snd_pcm_t*)pDevice->alsa.pPCM); + + if (pDevice->alsa.pIntermediaryBuffer != NULL) { + mal_free(pDevice->alsa.pIntermediaryBuffer); + } + } +} + +mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->alsa); + + mal_snd_pcm_format_t formatALSA = mal_convert_mal_format_to_alsa_format(pConfig->format); + + mal_result result = mal_context_open_pcm__alsa(pContext, pConfig->shareMode, type, pDeviceID, (mal_snd_pcm_t**)&pDevice->alsa.pPCM); + if (result != MAL_SUCCESS) { + return result; + } + + // We may be scaling the size of the buffer. + float bufferSizeScaleFactor = 1; + + // If using the default buffer size we may want to apply some device-specific scaling for known devices that have peculiar latency characteristics (looking at you Raspberry Pi!). if (pDevice->usingDefaultBufferSize) { - float bufferSizeScale = 1; + mal_snd_pcm_info_t* pInfo = (mal_snd_pcm_info_t*)alloca(((mal_snd_pcm_info_sizeof_proc)pContext->alsa.snd_pcm_info_sizeof)()); + mal_zero_memory(pInfo, ((mal_snd_pcm_info_sizeof_proc)pContext->alsa.snd_pcm_info_sizeof)()); - snd_pcm_info_t* pInfo = (snd_pcm_info_t*)alloca(((mal_snd_pcm_info_sizeof)pContext->alsa.snd_pcm_info_sizeof)()); - mal_zero_memory(pInfo, ((mal_snd_pcm_info_sizeof)pContext->alsa.snd_pcm_info_sizeof)()); - - if (((mal_snd_pcm_info)pContext->alsa.snd_pcm_info)((snd_pcm_t*)pDevice->alsa.pPCM, pInfo) == 0) { - const char* deviceName = ((mal_snd_pcm_info_get_name)pContext->alsa.snd_pcm_info_get_name)(pInfo); + // We may need to scale the size of the buffer depending on the device. + if (((mal_snd_pcm_info_proc)pContext->alsa.snd_pcm_info)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pInfo) == 0) { + const char* deviceName = ((mal_snd_pcm_info_get_name_proc)pContext->alsa.snd_pcm_info_get_name)(pInfo); if (deviceName != NULL) { - if (strcmp(deviceName, "default") == 0) { + if (mal_strcmp(deviceName, "default") == 0) { // It's the default device. We need to use DESC from snd_device_name_hint(). char** ppDeviceHints; if (((mal_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) { @@ -6401,10 +10722,10 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t char* IOID = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); mal_bool32 foundDevice = MAL_FALSE; - if ((type == mal_device_type_playback && (IOID == NULL || strcmp(IOID, "Output") == 0)) || - (type == mal_device_type_capture && (IOID != NULL && strcmp(IOID, "Input" ) == 0))) { - if (strcmp(NAME, deviceName) == 0) { - bufferSizeScale = mal_find_default_buffer_size_scale__alsa(DESC); + if ((type == mal_device_type_playback && (IOID == NULL || mal_strcmp(IOID, "Output") == 0)) || + (type == mal_device_type_capture && (IOID != NULL && mal_strcmp(IOID, "Input" ) == 0))) { + if (mal_strcmp(NAME, deviceName) == 0) { + bufferSizeScaleFactor = mal_find_default_buffer_size_scale__alsa(DESC); foundDevice = MAL_TRUE; } } @@ -6421,22 +10742,20 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t ((mal_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); } else { - bufferSizeScale = mal_find_default_buffer_size_scale__alsa(deviceName); + bufferSizeScaleFactor = mal_find_default_buffer_size_scale__alsa(deviceName); } } - - pDevice->bufferSizeInFrames = (mal_uint32)(pDevice->bufferSizeInFrames * bufferSizeScale); } } // Hardware parameters. - snd_pcm_hw_params_t* pHWParams = (snd_pcm_hw_params_t*)alloca(((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); + mal_snd_pcm_hw_params_t* pHWParams = (mal_snd_pcm_hw_params_t*)alloca(((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); mal_zero_memory(pHWParams, ((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); - if (((mal_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) { + if (((mal_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MAL_ALSA_FAILED_TO_SET_HW_PARAMS); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE); } @@ -6444,16 +10763,16 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t // // Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. pDevice->alsa.isUsingMMap = MAL_FALSE; - if (!pConfig->alsa.noMMap && pDevice->type != mal_device_type_capture) { // <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it so I can test it... Contributions welcome. - if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { + if (!pConfig->alsa.noMMap && pDevice->type != mal_device_type_capture) { // <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. + if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, MAL_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { pDevice->alsa.isUsingMMap = MAL_TRUE; } } if (!pDevice->alsa.isUsingMMap) { - if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {; + if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, MAL_SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {; mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", MAL_FORMAT_NOT_SUPPORTED); } } @@ -6463,7 +10782,7 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t // Format. // Try getting every supported format. - snd_pcm_format_mask_t* pFormatMask = (snd_pcm_format_mask_t*)alloca(((mal_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)()); + mal_snd_pcm_format_mask_t* pFormatMask = (mal_snd_pcm_format_mask_t*)alloca(((mal_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)()); mal_zero_memory(pFormatMask, ((mal_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)()); ((mal_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask); @@ -6472,15 +10791,24 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t // supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. if (!((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, formatALSA)) { // The requested format is not supported so now try running through the list of formats and return the best one. - snd_pcm_format_t preferredFormatsALSA[] = { - SND_PCM_FORMAT_FLOAT_LE, // mal_format_f32 - SND_PCM_FORMAT_S32_LE, // mal_format_s32 - SND_PCM_FORMAT_S24_3LE, // mal_format_s24 - SND_PCM_FORMAT_S16_LE, // mal_format_s16 - SND_PCM_FORMAT_U8 // mal_format_u8 + mal_snd_pcm_format_t preferredFormatsALSA[] = { + MAL_SND_PCM_FORMAT_S16_LE, // mal_format_s16 + MAL_SND_PCM_FORMAT_FLOAT_LE, // mal_format_f32 + MAL_SND_PCM_FORMAT_S32_LE, // mal_format_s32 + MAL_SND_PCM_FORMAT_S24_3LE, // mal_format_s24 + MAL_SND_PCM_FORMAT_U8 // mal_format_u8 }; - formatALSA = SND_PCM_FORMAT_UNKNOWN; + if (mal_is_big_endian()) { + preferredFormatsALSA[0] = MAL_SND_PCM_FORMAT_S16_BE; + preferredFormatsALSA[1] = MAL_SND_PCM_FORMAT_FLOAT_BE; + preferredFormatsALSA[2] = MAL_SND_PCM_FORMAT_S32_BE; + preferredFormatsALSA[3] = MAL_SND_PCM_FORMAT_S24_3BE; + preferredFormatsALSA[4] = MAL_SND_PCM_FORMAT_U8; + } + + + formatALSA = MAL_SND_PCM_FORMAT_UNKNOWN; for (size_t i = 0; i < (sizeof(preferredFormatsALSA) / sizeof(preferredFormatsALSA[0])); ++i) { if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, preferredFormatsALSA[i])) { formatALSA = preferredFormatsALSA[i]; @@ -6488,30 +10816,31 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t } } - if (formatALSA == SND_PCM_FORMAT_UNKNOWN) { + if (formatALSA == MAL_SND_PCM_FORMAT_UNKNOWN) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Format not supported. The device does not support any mini_al formats.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any mini_al formats.", MAL_FORMAT_NOT_SUPPORTED); } } - if (((mal_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, formatALSA) < 0) { + if (((mal_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, formatALSA) < 0) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", MAL_FORMAT_NOT_SUPPORTED); } pDevice->internalFormat = mal_convert_alsa_format_to_mal_format(formatALSA); if (pDevice->internalFormat == mal_format_unknown) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] The chosen format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); } + // Channels. - mal_uint32 channels = pConfig->channels; - if (((mal_snd_pcm_hw_params_set_channels_near_proc)pContext->alsa.snd_pcm_hw_params_set_channels_near)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &channels) < 0) { + unsigned int channels = pConfig->channels; + if (((mal_snd_pcm_hw_params_set_channels_near_proc)pContext->alsa.snd_pcm_hw_params_set_channels_near)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &channels) < 0) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", MAL_FORMAT_NOT_SUPPORTED); } - pDevice->internalChannels = channels; + pDevice->internalChannels = (mal_uint32)channels; // Sample Rate. It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling @@ -6529,85 +10858,86 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t // // I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce // this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. - ((mal_snd_pcm_hw_params_set_rate_resample_proc)pContext->alsa.snd_pcm_hw_params_set_rate_resample)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, 0); + ((mal_snd_pcm_hw_params_set_rate_resample_proc)pContext->alsa.snd_pcm_hw_params_set_rate_resample)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, 0); - mal_uint32 sampleRate = pConfig->sampleRate; - if (((mal_snd_pcm_hw_params_set_rate_near_proc)pContext->alsa.snd_pcm_hw_params_set_rate_near)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &sampleRate, 0) < 0) { + unsigned int sampleRate = pConfig->sampleRate; + if (((mal_snd_pcm_hw_params_set_rate_near_proc)pContext->alsa.snd_pcm_hw_params_set_rate_near)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &sampleRate, 0) < 0) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", MAL_FORMAT_NOT_SUPPORTED); } - pDevice->internalSampleRate = sampleRate; + pDevice->internalSampleRate = (mal_uint32)sampleRate; - // Periods. - mal_uint32 periods = pConfig->periods; - int dir = 0; - if (((mal_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &periods, &dir) < 0) { - mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", MAL_FORMAT_NOT_SUPPORTED); + // At this point we know the internal sample rate which means we can calculate the buffer size in frames. + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_scale_buffer_size(mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate), bufferSizeScaleFactor); } - pDevice->periods = periods; // Buffer Size - snd_pcm_uframes_t actualBufferSize = pDevice->bufferSizeInFrames; - if (((mal_snd_pcm_hw_params_set_buffer_size_near_proc)pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &actualBufferSize) < 0) { + mal_snd_pcm_uframes_t actualBufferSize = pDevice->bufferSizeInFrames; + if (((mal_snd_pcm_hw_params_set_buffer_size_near_proc)pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &actualBufferSize) < 0) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", MAL_FORMAT_NOT_SUPPORTED); } pDevice->bufferSizeInFrames = actualBufferSize; + // Periods. + mal_uint32 periods = pConfig->periods; + if (((mal_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &periods, NULL) < 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", MAL_FORMAT_NOT_SUPPORTED); + } + pDevice->periods = periods; + // Apply hardware parameters. - if (((mal_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) { + if (((mal_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", MAL_ALSA_FAILED_TO_SET_HW_PARAMS); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE); } - // Software parameters. - snd_pcm_sw_params_t* pSWParams = (snd_pcm_sw_params_t*)alloca(((mal_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)()); + mal_snd_pcm_sw_params_t* pSWParams = (mal_snd_pcm_sw_params_t*)alloca(((mal_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)()); mal_zero_memory(pSWParams, ((mal_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)()); - if (((mal_snd_pcm_sw_params_current_proc)pContext->alsa.snd_pcm_sw_params_current)((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) { + if (((mal_snd_pcm_sw_params_current_proc)pContext->alsa.snd_pcm_sw_params_current)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE); } - if (((mal_snd_pcm_sw_params_set_avail_min_proc)pContext->alsa.snd_pcm_sw_params_set_avail_min)((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, (pDevice->sampleRate/1000) * 1) != 0) { + if (((mal_snd_pcm_sw_params_set_avail_min_proc)pContext->alsa.snd_pcm_sw_params_set_avail_min)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, /*(pDevice->sampleRate/1000) * 1*/ mal_prev_power_of_2(pDevice->bufferSizeInFrames/pDevice->periods)) != 0) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", MAL_FORMAT_NOT_SUPPORTED); } if (type == mal_device_type_playback && !pDevice->alsa.isUsingMMap) { // Only playback devices in writei/readi mode need a start threshold. - if (((mal_snd_pcm_sw_params_set_start_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_start_threshold)((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, (pDevice->sampleRate/1000) * 1) != 0) { //mal_prev_power_of_2(pDevice->bufferSizeInFrames/pDevice->periods) + if (((mal_snd_pcm_sw_params_set_start_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_start_threshold)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, /*(pDevice->sampleRate/1000) * 1*/ pDevice->bufferSizeInFrames/pDevice->periods) != 0) { //mal_prev_power_of_2(pDevice->bufferSizeInFrames/pDevice->periods) mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE); } } - if (((mal_snd_pcm_sw_params_proc)pContext->alsa.snd_pcm_sw_params)((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) { + if (((mal_snd_pcm_sw_params_proc)pContext->alsa.snd_pcm_sw_params)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE); } // If we're _not_ using mmap we need to use an intermediary buffer. if (!pDevice->alsa.isUsingMMap) { - pDevice->alsa.pIntermediaryBuffer = mal_malloc(pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format)); + pDevice->alsa.pIntermediaryBuffer = mal_malloc(pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat)); if (pDevice->alsa.pIntermediaryBuffer == NULL) { mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, "[ALSA] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); } } - - // Grab the internal channel map. For now we're not going to bother trying to change the channel map and // instead just do it ourselves. - snd_pcm_chmap_t* pChmap = ((mal_snd_pcm_get_chmap_proc)pContext->alsa.snd_pcm_get_chmap)((snd_pcm_t*)pDevice->alsa.pPCM); + mal_snd_pcm_chmap_t* pChmap = ((mal_snd_pcm_get_chmap_proc)pContext->alsa.snd_pcm_get_chmap)((mal_snd_pcm_t*)pDevice->alsa.pPCM); if (pChmap != NULL) { // There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). if (pChmap->channels >= pDevice->internalChannels) { @@ -6620,7 +10950,7 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t // channels. If validation fails, fall back to defaults. // Fill with defaults. - mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); + mal_get_standard_channel_map(mal_standard_channel_map_alsa, pDevice->internalChannels, pDevice->internalChannelMap); // Overwrite first pChmap->channels channels. for (mal_uint32 iChannel = 0; iChannel < pChmap->channels; ++iChannel) { @@ -6640,7 +10970,7 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t // If our channel map is invalid, fall back to defaults. if (!isValid) { - mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); + mal_get_standard_channel_map(mal_standard_channel_map_alsa, pDevice->internalChannels, pDevice->internalChannelMap); } } @@ -6648,63 +10978,66 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t pChmap = NULL; } else { // Could not retrieve the channel map. Fall back to a hard-coded assumption. - mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); + mal_get_standard_channel_map(mal_standard_channel_map_alsa, pDevice->internalChannels, pDevice->internalChannelMap); } return MAL_SUCCESS; } -static mal_result mal_device__start_backend__alsa(mal_device* pDevice) +mal_result mal_device__start_backend__alsa(mal_device* pDevice) { mal_assert(pDevice != NULL); // Prepare the device first... - if (((mal_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { - return mal_post_error(pDevice, "[ALSA] Failed to prepare device.", MAL_ALSA_FAILED_TO_PREPARE_DEVICE); + if (((mal_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((mal_snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } // ... and then grab an initial chunk from the client. After this is done, the device should // automatically start playing, since that's how we configured the software parameters. if (pDevice->type == mal_device_type_playback) { if (!mal_device_write__alsa(pDevice)) { - return mal_post_error(pDevice, "[ALSA] Failed to write initial chunk of data to the playback device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to write initial chunk of data to the playback device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); } // mmap mode requires an explicit start. if (pDevice->alsa.isUsingMMap) { - if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { - return mal_post_error(pDevice, "[ALSA] Failed to start capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((mal_snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } } } else { - if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { - return mal_post_error(pDevice, "[ALSA] Failed to start capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((mal_snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } } return MAL_SUCCESS; } -static mal_result mal_device__stop_backend__alsa(mal_device* pDevice) +mal_result mal_device__stop_backend__alsa(mal_device* pDevice) { mal_assert(pDevice != NULL); - ((mal_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((snd_pcm_t*)pDevice->alsa.pPCM); + ((mal_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((mal_snd_pcm_t*)pDevice->alsa.pPCM); return MAL_SUCCESS; } -static mal_result mal_device__break_main_loop__alsa(mal_device* pDevice) +mal_result mal_device__break_main_loop__alsa(mal_device* pDevice) { mal_assert(pDevice != NULL); - // Fallback. We just set a variable to tell the worker thread to terminate after handling the - // next bunch of frames. This is a slow way of handling this. + // First we tell the main loop that we're breaking... pDevice->alsa.breakFromMainLoop = MAL_TRUE; + + // Then we need to force snd_pcm_wait() to return. + //((mal_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((mal_snd_pcm_t*)pDevice->alsa.pPCM); + return MAL_SUCCESS; } -static mal_result mal_device__main_loop__alsa(mal_device* pDevice) +mal_result mal_device__main_loop__alsa(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -6721,9 +11054,6084 @@ static mal_result mal_device__main_loop__alsa(mal_device* pDevice) return MAL_SUCCESS; } + + +mal_result mal_context_uninit__alsa(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_alsa); + + // Clean up memory for memory leak checkers. + ((mal_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); + +#ifndef MAL_NO_RUNTIME_LINKING + mal_dlclose(pContext->alsa.asoundSO); +#endif + + mal_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); + + return MAL_SUCCESS; +} + +mal_result mal_context_init__alsa(mal_context* pContext) +{ + mal_assert(pContext != NULL); + +#ifndef MAL_NO_RUNTIME_LINKING + pContext->alsa.asoundSO = mal_dlopen("libasound.so"); + if (pContext->alsa.asoundSO == NULL) { + return MAL_NO_BACKEND; + } + + pContext->alsa.snd_pcm_open = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_open"); + pContext->alsa.snd_pcm_close = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_close"); + pContext->alsa.snd_pcm_hw_params_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); + pContext->alsa.snd_pcm_hw_params_any = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); + pContext->alsa.snd_pcm_hw_params_set_format = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); + pContext->alsa.snd_pcm_hw_params_set_format_first = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); + pContext->alsa.snd_pcm_hw_params_get_format_mask = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); + pContext->alsa.snd_pcm_hw_params_set_channels_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); + pContext->alsa.snd_pcm_hw_params_set_rate_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); + pContext->alsa.snd_pcm_hw_params_set_periods_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); + pContext->alsa.snd_pcm_hw_params_set_access = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); + pContext->alsa.snd_pcm_hw_params_get_format = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); + pContext->alsa.snd_pcm_hw_params_get_channels = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); + pContext->alsa.snd_pcm_hw_params_get_channels_min = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); + pContext->alsa.snd_pcm_hw_params_get_channels_max = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); + pContext->alsa.snd_pcm_hw_params_get_rate = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); + pContext->alsa.snd_pcm_hw_params_get_rate_min = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); + pContext->alsa.snd_pcm_hw_params_get_rate_max = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); + pContext->alsa.snd_pcm_hw_params_get_periods = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); + pContext->alsa.snd_pcm_hw_params_get_access = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); + pContext->alsa.snd_pcm_hw_params = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params"); + pContext->alsa.snd_pcm_sw_params_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); + pContext->alsa.snd_pcm_sw_params_current = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); + pContext->alsa.snd_pcm_sw_params_set_avail_min = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); + pContext->alsa.snd_pcm_sw_params = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params"); + pContext->alsa.snd_pcm_format_mask_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); + pContext->alsa.snd_pcm_format_mask_test = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); + pContext->alsa.snd_pcm_get_chmap = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_get_chmap"); + pContext->alsa.snd_pcm_prepare = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_prepare"); + pContext->alsa.snd_pcm_start = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_start"); + pContext->alsa.snd_pcm_drop = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_drop"); + pContext->alsa.snd_device_name_hint = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_device_name_hint"); + pContext->alsa.snd_device_name_get_hint = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_device_name_get_hint"); + pContext->alsa.snd_card_get_index = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_card_get_index"); + pContext->alsa.snd_device_name_free_hint = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_device_name_free_hint"); + pContext->alsa.snd_pcm_mmap_begin = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); + pContext->alsa.snd_pcm_mmap_commit = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); + pContext->alsa.snd_pcm_recover = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_recover"); + pContext->alsa.snd_pcm_readi = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_readi"); + pContext->alsa.snd_pcm_writei = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_writei"); + pContext->alsa.snd_pcm_avail = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail"); + pContext->alsa.snd_pcm_avail_update = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail_update"); + pContext->alsa.snd_pcm_wait = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_wait"); + pContext->alsa.snd_pcm_info = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info"); + pContext->alsa.snd_pcm_info_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); + pContext->alsa.snd_pcm_info_get_name = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_get_name"); + pContext->alsa.snd_config_update_free_global = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_config_update_free_global"); +#else + // The system below is just for type safety. + mal_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; + mal_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; + mal_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; + mal_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; + mal_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; + mal_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; + mal_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; + mal_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; + mal_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; + mal_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; + mal_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; + mal_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; + mal_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; + mal_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; + mal_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; + mal_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; + mal_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; + mal_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; + mal_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; + mal_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; + mal_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; + mal_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; + mal_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; + mal_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; + mal_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; + mal_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; + mal_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; + mal_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; + mal_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; + mal_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; + mal_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; + mal_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; + mal_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; + mal_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; + mal_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; + mal_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; + mal_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; + mal_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; + mal_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; + mal_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; + mal_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; + mal_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; + mal_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; + mal_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; + mal_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; + mal_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; + mal_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; + mal_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; + mal_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; + mal_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; + mal_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; + + pContext->alsa.snd_pcm_open = (mal_proc)_snd_pcm_open; + pContext->alsa.snd_pcm_close = (mal_proc)_snd_pcm_close; + pContext->alsa.snd_pcm_hw_params_sizeof = (mal_proc)_snd_pcm_hw_params_sizeof; + pContext->alsa.snd_pcm_hw_params_any = (mal_proc)_snd_pcm_hw_params_any; + pContext->alsa.snd_pcm_hw_params_set_format = (mal_proc)_snd_pcm_hw_params_set_format; + pContext->alsa.snd_pcm_hw_params_set_format_first = (mal_proc)_snd_pcm_hw_params_set_format_first; + pContext->alsa.snd_pcm_hw_params_get_format_mask = (mal_proc)_snd_pcm_hw_params_get_format_mask; + pContext->alsa.snd_pcm_hw_params_set_channels_near = (mal_proc)_snd_pcm_hw_params_set_channels_near; + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (mal_proc)_snd_pcm_hw_params_set_rate_resample; + pContext->alsa.snd_pcm_hw_params_set_rate_near = (mal_proc)_snd_pcm_hw_params_set_rate_near; + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (mal_proc)_snd_pcm_hw_params_set_buffer_size_near; + pContext->alsa.snd_pcm_hw_params_set_periods_near = (mal_proc)_snd_pcm_hw_params_set_periods_near; + pContext->alsa.snd_pcm_hw_params_set_access = (mal_proc)_snd_pcm_hw_params_set_access; + pContext->alsa.snd_pcm_hw_params_get_format = (mal_proc)_snd_pcm_hw_params_get_format; + pContext->alsa.snd_pcm_hw_params_get_channels = (mal_proc)_snd_pcm_hw_params_get_channels; + pContext->alsa.snd_pcm_hw_params_get_channels_min = (mal_proc)_snd_pcm_hw_params_get_channels_min; + pContext->alsa.snd_pcm_hw_params_get_channels_max = (mal_proc)_snd_pcm_hw_params_get_channels_max; + pContext->alsa.snd_pcm_hw_params_get_rate = (mal_proc)_snd_pcm_hw_params_get_rate; + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (mal_proc)_snd_pcm_hw_params_get_buffer_size; + pContext->alsa.snd_pcm_hw_params_get_periods = (mal_proc)_snd_pcm_hw_params_get_periods; + pContext->alsa.snd_pcm_hw_params_get_access = (mal_proc)_snd_pcm_hw_params_get_access; + pContext->alsa.snd_pcm_hw_params = (mal_proc)_snd_pcm_hw_params; + pContext->alsa.snd_pcm_sw_params_sizeof = (mal_proc)_snd_pcm_sw_params_sizeof; + pContext->alsa.snd_pcm_sw_params_current = (mal_proc)_snd_pcm_sw_params_current; + pContext->alsa.snd_pcm_sw_params_set_avail_min = (mal_proc)_snd_pcm_sw_params_set_avail_min; + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (mal_proc)_snd_pcm_sw_params_set_start_threshold; + pContext->alsa.snd_pcm_sw_params = (mal_proc)_snd_pcm_sw_params; + pContext->alsa.snd_pcm_format_mask_sizeof = (mal_proc)_snd_pcm_format_mask_sizeof; + pContext->alsa.snd_pcm_format_mask_test = (mal_proc)_snd_pcm_format_mask_test; + pContext->alsa.snd_pcm_get_chmap = (mal_proc)_snd_pcm_get_chmap; + pContext->alsa.snd_pcm_prepare = (mal_proc)_snd_pcm_prepare; + pContext->alsa.snd_pcm_start = (mal_proc)_snd_pcm_start; + pContext->alsa.snd_pcm_drop = (mal_proc)_snd_pcm_drop; + pContext->alsa.snd_device_name_hint = (mal_proc)_snd_device_name_hint; + pContext->alsa.snd_device_name_get_hint = (mal_proc)_snd_device_name_get_hint; + pContext->alsa.snd_card_get_index = (mal_proc)_snd_card_get_index; + pContext->alsa.snd_device_name_free_hint = (mal_proc)_snd_device_name_free_hint; + pContext->alsa.snd_pcm_mmap_begin = (mal_proc)_snd_pcm_mmap_begin; + pContext->alsa.snd_pcm_mmap_commit = (mal_proc)_snd_pcm_mmap_commit; + pContext->alsa.snd_pcm_recover = (mal_proc)_snd_pcm_recover; + pContext->alsa.snd_pcm_readi = (mal_proc)_snd_pcm_readi; + pContext->alsa.snd_pcm_writei = (mal_proc)_snd_pcm_writei; + pContext->alsa.snd_pcm_avail = (mal_proc)_snd_pcm_avail; + pContext->alsa.snd_pcm_avail_update = (mal_proc)_snd_pcm_avail_update; + pContext->alsa.snd_pcm_wait = (mal_proc)_snd_pcm_wait; + pContext->alsa.snd_pcm_info = (mal_proc)_snd_pcm_info; + pContext->alsa.snd_pcm_info_sizeof = (mal_proc)_snd_pcm_info_sizeof; + pContext->alsa.snd_pcm_info_get_name = (mal_proc)_snd_pcm_info_get_name; + pContext->alsa.snd_config_update_free_global = (mal_proc)_snd_config_update_free_global; +#endif + + if (mal_mutex_init(pContext, &pContext->alsa.internalDeviceEnumLock) != MAL_SUCCESS) { + mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.", MAL_ERROR); + } + + pContext->onUninit = mal_context_uninit__alsa; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__alsa; + pContext->onEnumDevices = mal_context_enumerate_devices__alsa; + pContext->onGetDeviceInfo = mal_context_get_device_info__alsa; + pContext->onDeviceInit = mal_device_init__alsa; + pContext->onDeviceUninit = mal_device_uninit__alsa; + pContext->onDeviceStart = mal_device__start_backend__alsa; + pContext->onDeviceStop = mal_device__stop_backend__alsa; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__alsa; + pContext->onDeviceMainLoop = mal_device__main_loop__alsa; + + return MAL_SUCCESS; +} #endif // ALSA + +/////////////////////////////////////////////////////////////////////////////// +// +// PulseAudio Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_PULSEAUDIO + +// It is assumed pulseaudio.h is available when compile-time linking is being used. We use this for type safety when using +// compile time linking (we don't have this luxury when using runtime linking without headers). +// +// When using compile time linking, each of our mal_* equivalents should use the sames types as defined by the header. The +// reason for this is that it allow us to take advantage of proper type safety. +#ifdef MAL_NO_RUNTIME_LINKING +#include + +#define MAL_PA_OK PA_OK +#define MAL_PA_ERR_ACCESS PA_ERR_ACCESS +#define MAL_PA_ERR_INVALID PA_ERR_INVALID +#define MAL_PA_ERR_NOENTITY PA_ERR_NOENTITY + +#define MAL_PA_CHANNELS_MAX PA_CHANNELS_MAX +#define MAL_PA_RATE_MAX PA_RATE_MAX + +typedef pa_context_flags_t mal_pa_context_flags_t; +#define MAL_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS +#define MAL_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN +#define MAL_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL + +typedef pa_stream_flags_t mal_pa_stream_flags_t; +#define MAL_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS +#define MAL_PA_STREAM_START_CORKED PA_STREAM_START_CORKED +#define MAL_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING +#define MAL_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC +#define MAL_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE +#define MAL_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS +#define MAL_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS +#define MAL_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT +#define MAL_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE +#define MAL_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS +#define MAL_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE +#define MAL_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE +#define MAL_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT +#define MAL_PA_STREAM_START_MUTED PA_STREAM_START_MUTED +#define MAL_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY +#define MAL_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS +#define MAL_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND +#define MAL_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED +#define MAL_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND +#define MAL_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME +#define MAL_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH + +typedef pa_sink_flags_t mal_pa_sink_flags_t; +#define MAL_PA_SINK_NOFLAGS PA_SINK_NOFLAGS +#define MAL_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL +#define MAL_PA_SINK_LATENCY PA_SINK_LATENCY +#define MAL_PA_SINK_HARDWARE PA_SINK_HARDWARE +#define MAL_PA_SINK_NETWORK PA_SINK_NETWORK +#define MAL_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL +#define MAL_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME +#define MAL_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME +#define MAL_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY +#define MAL_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS + +typedef pa_source_flags_t mal_pa_source_flags_t; +#define MAL_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS +#define MAL_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL +#define MAL_PA_SOURCE_LATENCY PA_SOURCE_LATENCY +#define MAL_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE +#define MAL_PA_SOURCE_NETWORK PA_SOURCE_NETWORK +#define MAL_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL +#define MAL_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME +#define MAL_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY +#define MAL_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME + +typedef pa_context_state_t mal_pa_context_state_t; +#define MAL_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED +#define MAL_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING +#define MAL_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING +#define MAL_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME +#define MAL_PA_CONTEXT_READY PA_CONTEXT_READY +#define MAL_PA_CONTEXT_FAILED PA_CONTEXT_FAILED +#define MAL_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED + +typedef pa_stream_state_t mal_pa_stream_state_t; +#define MAL_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED +#define MAL_PA_STREAM_CREATING PA_STREAM_CREATING +#define MAL_PA_STREAM_READY PA_STREAM_READY +#define MAL_PA_STREAM_FAILED PA_STREAM_FAILED +#define MAL_PA_STREAM_TERMINATED PA_STREAM_TERMINATED + +typedef pa_operation_state_t mal_pa_operation_state_t; +#define MAL_PA_OPERATION_RUNNING PA_OPERATION_RUNNING +#define MAL_PA_OPERATION_DONE PA_OPERATION_DONE +#define MAL_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED + +typedef pa_sink_state_t mal_pa_sink_state_t; +#define MAL_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE +#define MAL_PA_SINK_RUNNING PA_SINK_RUNNING +#define MAL_PA_SINK_IDLE PA_SINK_IDLE +#define MAL_PA_SINK_SUSPENDED PA_SINK_SUSPENDED + +typedef pa_source_state_t mal_pa_source_state_t; +#define MAL_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE +#define MAL_PA_SOURCE_RUNNING PA_SOURCE_RUNNING +#define MAL_PA_SOURCE_IDLE PA_SOURCE_IDLE +#define MAL_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED + +typedef pa_seek_mode_t mal_pa_seek_mode_t; +#define MAL_PA_SEEK_RELATIVE PA_SEEK_RELATIVE +#define MAL_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE +#define MAL_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ +#define MAL_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END + +typedef pa_channel_position_t mal_pa_channel_position_t; +#define MAL_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID +#define MAL_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO +#define MAL_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT +#define MAL_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT +#define MAL_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER +#define MAL_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER +#define MAL_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT +#define MAL_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT +#define MAL_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE +#define MAL_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER +#define MAL_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER +#define MAL_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT +#define MAL_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT +#define MAL_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 +#define MAL_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 +#define MAL_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 +#define MAL_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 +#define MAL_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 +#define MAL_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 +#define MAL_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 +#define MAL_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 +#define MAL_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 +#define MAL_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 +#define MAL_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 +#define MAL_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 +#define MAL_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 +#define MAL_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 +#define MAL_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 +#define MAL_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 +#define MAL_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 +#define MAL_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 +#define MAL_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 +#define MAL_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 +#define MAL_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 +#define MAL_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 +#define MAL_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 +#define MAL_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 +#define MAL_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 +#define MAL_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 +#define MAL_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 +#define MAL_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 +#define MAL_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 +#define MAL_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 +#define MAL_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 +#define MAL_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 +#define MAL_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER +#define MAL_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT +#define MAL_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT +#define MAL_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER +#define MAL_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT +#define MAL_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT +#define MAL_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER +#define MAL_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT +#define MAL_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT +#define MAL_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER +#define MAL_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER + +typedef pa_channel_map_def_t mal_pa_channel_map_def_t; +#define MAL_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF +#define MAL_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA +#define MAL_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX +#define MAL_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX +#define MAL_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS +#define MAL_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT + +typedef pa_sample_format_t mal_pa_sample_format_t; +#define MAL_PA_SAMPLE_INVALID PA_SAMPLE_INVALID +#define MAL_PA_SAMPLE_U8 PA_SAMPLE_U8 +#define MAL_PA_SAMPLE_ALAW PA_SAMPLE_ALAW +#define MAL_PA_SAMPLE_ULAW PA_SAMPLE_ULAW +#define MAL_PA_SAMPLE_S16LE PA_SAMPLE_S16LE +#define MAL_PA_SAMPLE_S16BE PA_SAMPLE_S16BE +#define MAL_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE +#define MAL_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE +#define MAL_PA_SAMPLE_S32LE PA_SAMPLE_S32LE +#define MAL_PA_SAMPLE_S32BE PA_SAMPLE_S32BE +#define MAL_PA_SAMPLE_S24LE PA_SAMPLE_S24LE +#define MAL_PA_SAMPLE_S24BE PA_SAMPLE_S24BE +#define MAL_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE +#define MAL_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE + +typedef pa_mainloop mal_pa_mainloop; +typedef pa_mainloop_api mal_pa_mainloop_api; +typedef pa_context mal_pa_context; +typedef pa_operation mal_pa_operation; +typedef pa_stream mal_pa_stream; +typedef pa_spawn_api mal_pa_spawn_api; +typedef pa_buffer_attr mal_pa_buffer_attr; +typedef pa_channel_map mal_pa_channel_map; +typedef pa_cvolume mal_pa_cvolume; +typedef pa_sample_spec mal_pa_sample_spec; +typedef pa_sink_info mal_pa_sink_info; +typedef pa_source_info mal_pa_source_info; + +typedef pa_context_notify_cb_t mal_pa_context_notify_cb_t; +typedef pa_sink_info_cb_t mal_pa_sink_info_cb_t; +typedef pa_source_info_cb_t mal_pa_source_info_cb_t; +typedef pa_stream_success_cb_t mal_pa_stream_success_cb_t; +typedef pa_stream_request_cb_t mal_pa_stream_request_cb_t; +typedef pa_free_cb_t mal_pa_free_cb_t; +#else +#define MAL_PA_OK 0 +#define MAL_PA_ERR_ACCESS 1 +#define MAL_PA_ERR_INVALID 2 +#define MAL_PA_ERR_NOENTITY 5 + +#define MAL_PA_CHANNELS_MAX 32 +#define MAL_PA_RATE_MAX 384000 + +typedef int mal_pa_context_flags_t; +#define MAL_PA_CONTEXT_NOFLAGS 0x00000000 +#define MAL_PA_CONTEXT_NOAUTOSPAWN 0x00000001 +#define MAL_PA_CONTEXT_NOFAIL 0x00000002 + +typedef int mal_pa_stream_flags_t; +#define MAL_PA_STREAM_NOFLAGS 0x00000000 +#define MAL_PA_STREAM_START_CORKED 0x00000001 +#define MAL_PA_STREAM_INTERPOLATE_TIMING 0x00000002 +#define MAL_PA_STREAM_NOT_MONOTONIC 0x00000004 +#define MAL_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 +#define MAL_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 +#define MAL_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 +#define MAL_PA_STREAM_FIX_FORMAT 0x00000040 +#define MAL_PA_STREAM_FIX_RATE 0x00000080 +#define MAL_PA_STREAM_FIX_CHANNELS 0x00000100 +#define MAL_PA_STREAM_DONT_MOVE 0x00000200 +#define MAL_PA_STREAM_VARIABLE_RATE 0x00000400 +#define MAL_PA_STREAM_PEAK_DETECT 0x00000800 +#define MAL_PA_STREAM_START_MUTED 0x00001000 +#define MAL_PA_STREAM_ADJUST_LATENCY 0x00002000 +#define MAL_PA_STREAM_EARLY_REQUESTS 0x00004000 +#define MAL_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 +#define MAL_PA_STREAM_START_UNMUTED 0x00010000 +#define MAL_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 +#define MAL_PA_STREAM_RELATIVE_VOLUME 0x00040000 +#define MAL_PA_STREAM_PASSTHROUGH 0x00080000 + +typedef int mal_pa_sink_flags_t; +#define MAL_PA_SINK_NOFLAGS 0x00000000 +#define MAL_PA_SINK_HW_VOLUME_CTRL 0x00000001 +#define MAL_PA_SINK_LATENCY 0x00000002 +#define MAL_PA_SINK_HARDWARE 0x00000004 +#define MAL_PA_SINK_NETWORK 0x00000008 +#define MAL_PA_SINK_HW_MUTE_CTRL 0x00000010 +#define MAL_PA_SINK_DECIBEL_VOLUME 0x00000020 +#define MAL_PA_SINK_FLAT_VOLUME 0x00000040 +#define MAL_PA_SINK_DYNAMIC_LATENCY 0x00000080 +#define MAL_PA_SINK_SET_FORMATS 0x00000100 + +typedef int mal_pa_source_flags_t; +#define MAL_PA_SOURCE_NOFLAGS 0x00000000 +#define MAL_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 +#define MAL_PA_SOURCE_LATENCY 0x00000002 +#define MAL_PA_SOURCE_HARDWARE 0x00000004 +#define MAL_PA_SOURCE_NETWORK 0x00000008 +#define MAL_PA_SOURCE_HW_MUTE_CTRL 0x00000010 +#define MAL_PA_SOURCE_DECIBEL_VOLUME 0x00000020 +#define MAL_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 +#define MAL_PA_SOURCE_FLAT_VOLUME 0x00000080 + +typedef int mal_pa_context_state_t; +#define MAL_PA_CONTEXT_UNCONNECTED 0 +#define MAL_PA_CONTEXT_CONNECTING 1 +#define MAL_PA_CONTEXT_AUTHORIZING 2 +#define MAL_PA_CONTEXT_SETTING_NAME 3 +#define MAL_PA_CONTEXT_READY 4 +#define MAL_PA_CONTEXT_FAILED 5 +#define MAL_PA_CONTEXT_TERMINATED 6 + +typedef int mal_pa_stream_state_t; +#define MAL_PA_STREAM_UNCONNECTED 0 +#define MAL_PA_STREAM_CREATING 1 +#define MAL_PA_STREAM_READY 2 +#define MAL_PA_STREAM_FAILED 3 +#define MAL_PA_STREAM_TERMINATED 4 + +typedef int mal_pa_operation_state_t; +#define MAL_PA_OPERATION_RUNNING 0 +#define MAL_PA_OPERATION_DONE 1 +#define MAL_PA_OPERATION_CANCELLED 2 + +typedef int mal_pa_sink_state_t; +#define MAL_PA_SINK_INVALID_STATE -1 +#define MAL_PA_SINK_RUNNING 0 +#define MAL_PA_SINK_IDLE 1 +#define MAL_PA_SINK_SUSPENDED 2 + +typedef int mal_pa_source_state_t; +#define MAL_PA_SOURCE_INVALID_STATE -1 +#define MAL_PA_SOURCE_RUNNING 0 +#define MAL_PA_SOURCE_IDLE 1 +#define MAL_PA_SOURCE_SUSPENDED 2 + +typedef int mal_pa_seek_mode_t; +#define MAL_PA_SEEK_RELATIVE 0 +#define MAL_PA_SEEK_ABSOLUTE 1 +#define MAL_PA_SEEK_RELATIVE_ON_READ 2 +#define MAL_PA_SEEK_RELATIVE_END 3 + +typedef int mal_pa_channel_position_t; +#define MAL_PA_CHANNEL_POSITION_INVALID -1 +#define MAL_PA_CHANNEL_POSITION_MONO 0 +#define MAL_PA_CHANNEL_POSITION_FRONT_LEFT 1 +#define MAL_PA_CHANNEL_POSITION_FRONT_RIGHT 2 +#define MAL_PA_CHANNEL_POSITION_FRONT_CENTER 3 +#define MAL_PA_CHANNEL_POSITION_REAR_CENTER 4 +#define MAL_PA_CHANNEL_POSITION_REAR_LEFT 5 +#define MAL_PA_CHANNEL_POSITION_REAR_RIGHT 6 +#define MAL_PA_CHANNEL_POSITION_LFE 7 +#define MAL_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 +#define MAL_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 +#define MAL_PA_CHANNEL_POSITION_SIDE_LEFT 10 +#define MAL_PA_CHANNEL_POSITION_SIDE_RIGHT 11 +#define MAL_PA_CHANNEL_POSITION_AUX0 12 +#define MAL_PA_CHANNEL_POSITION_AUX1 13 +#define MAL_PA_CHANNEL_POSITION_AUX2 14 +#define MAL_PA_CHANNEL_POSITION_AUX3 15 +#define MAL_PA_CHANNEL_POSITION_AUX4 16 +#define MAL_PA_CHANNEL_POSITION_AUX5 17 +#define MAL_PA_CHANNEL_POSITION_AUX6 18 +#define MAL_PA_CHANNEL_POSITION_AUX7 19 +#define MAL_PA_CHANNEL_POSITION_AUX8 20 +#define MAL_PA_CHANNEL_POSITION_AUX9 21 +#define MAL_PA_CHANNEL_POSITION_AUX10 22 +#define MAL_PA_CHANNEL_POSITION_AUX11 23 +#define MAL_PA_CHANNEL_POSITION_AUX12 24 +#define MAL_PA_CHANNEL_POSITION_AUX13 25 +#define MAL_PA_CHANNEL_POSITION_AUX14 26 +#define MAL_PA_CHANNEL_POSITION_AUX15 27 +#define MAL_PA_CHANNEL_POSITION_AUX16 28 +#define MAL_PA_CHANNEL_POSITION_AUX17 29 +#define MAL_PA_CHANNEL_POSITION_AUX18 30 +#define MAL_PA_CHANNEL_POSITION_AUX19 31 +#define MAL_PA_CHANNEL_POSITION_AUX20 32 +#define MAL_PA_CHANNEL_POSITION_AUX21 33 +#define MAL_PA_CHANNEL_POSITION_AUX22 34 +#define MAL_PA_CHANNEL_POSITION_AUX23 35 +#define MAL_PA_CHANNEL_POSITION_AUX24 36 +#define MAL_PA_CHANNEL_POSITION_AUX25 37 +#define MAL_PA_CHANNEL_POSITION_AUX26 38 +#define MAL_PA_CHANNEL_POSITION_AUX27 39 +#define MAL_PA_CHANNEL_POSITION_AUX28 40 +#define MAL_PA_CHANNEL_POSITION_AUX29 41 +#define MAL_PA_CHANNEL_POSITION_AUX30 42 +#define MAL_PA_CHANNEL_POSITION_AUX31 43 +#define MAL_PA_CHANNEL_POSITION_TOP_CENTER 44 +#define MAL_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 +#define MAL_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 +#define MAL_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 +#define MAL_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 +#define MAL_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 +#define MAL_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 +#define MAL_PA_CHANNEL_POSITION_LEFT MAL_PA_CHANNEL_POSITION_FRONT_LEFT +#define MAL_PA_CHANNEL_POSITION_RIGHT MAL_PA_CHANNEL_POSITION_FRONT_RIGHT +#define MAL_PA_CHANNEL_POSITION_CENTER MAL_PA_CHANNEL_POSITION_FRONT_CENTER +#define MAL_PA_CHANNEL_POSITION_SUBWOOFER MAL_PA_CHANNEL_POSITION_LFE + +typedef int mal_pa_channel_map_def_t; +#define MAL_PA_CHANNEL_MAP_AIFF 0 +#define MAL_PA_CHANNEL_MAP_ALSA 1 +#define MAL_PA_CHANNEL_MAP_AUX 2 +#define MAL_PA_CHANNEL_MAP_WAVEEX 3 +#define MAL_PA_CHANNEL_MAP_OSS 4 +#define MAL_PA_CHANNEL_MAP_DEFAULT MAL_PA_CHANNEL_MAP_AIFF + +typedef int mal_pa_sample_format_t; +#define MAL_PA_SAMPLE_INVALID -1 +#define MAL_PA_SAMPLE_U8 0 +#define MAL_PA_SAMPLE_ALAW 1 +#define MAL_PA_SAMPLE_ULAW 2 +#define MAL_PA_SAMPLE_S16LE 3 +#define MAL_PA_SAMPLE_S16BE 4 +#define MAL_PA_SAMPLE_FLOAT32LE 5 +#define MAL_PA_SAMPLE_FLOAT32BE 6 +#define MAL_PA_SAMPLE_S32LE 7 +#define MAL_PA_SAMPLE_S32BE 8 +#define MAL_PA_SAMPLE_S24LE 9 +#define MAL_PA_SAMPLE_S24BE 10 +#define MAL_PA_SAMPLE_S24_32LE 11 +#define MAL_PA_SAMPLE_S24_32BE 12 + +typedef struct mal_pa_mainloop mal_pa_mainloop; +typedef struct mal_pa_mainloop_api mal_pa_mainloop_api; +typedef struct mal_pa_context mal_pa_context; +typedef struct mal_pa_operation mal_pa_operation; +typedef struct mal_pa_stream mal_pa_stream; +typedef struct mal_pa_spawn_api mal_pa_spawn_api; + +typedef struct +{ + mal_uint32 maxlength; + mal_uint32 tlength; + mal_uint32 prebuf; + mal_uint32 minreq; + mal_uint32 fragsize; +} mal_pa_buffer_attr; + +typedef struct +{ + mal_uint8 channels; + mal_pa_channel_position_t map[MAL_PA_CHANNELS_MAX]; +} mal_pa_channel_map; + +typedef struct +{ + mal_uint8 channels; + mal_uint32 values[MAL_PA_CHANNELS_MAX]; +} mal_pa_cvolume; + +typedef struct +{ + mal_pa_sample_format_t format; + mal_uint32 rate; + mal_uint8 channels; +} mal_pa_sample_spec; + +typedef struct +{ + const char* name; + mal_uint32 index; + const char* description; + mal_pa_sample_spec sample_spec; + mal_pa_channel_map channel_map; + mal_uint32 owner_module; + mal_pa_cvolume volume; + int mute; + mal_uint32 monitor_source; + const char* monitor_source_name; + mal_uint64 latency; + const char* driver; + mal_pa_sink_flags_t flags; + void* proplist; + mal_uint64 configured_latency; + mal_uint32 base_volume; + mal_pa_sink_state_t state; + mal_uint32 n_volume_steps; + mal_uint32 card; + mal_uint32 n_ports; + void** ports; + void* active_port; + mal_uint8 n_formats; + void** formats; +} mal_pa_sink_info; + +typedef struct +{ + const char *name; + mal_uint32 index; + const char *description; + mal_pa_sample_spec sample_spec; + mal_pa_channel_map channel_map; + mal_uint32 owner_module; + mal_pa_cvolume volume; + int mute; + mal_uint32 monitor_of_sink; + const char *monitor_of_sink_name; + mal_uint64 latency; + const char *driver; + mal_pa_source_flags_t flags; + void* proplist; + mal_uint64 configured_latency; + mal_uint32 base_volume; + mal_pa_source_state_t state; + mal_uint32 n_volume_steps; + mal_uint32 card; + mal_uint32 n_ports; + void** ports; + void* active_port; + mal_uint8 n_formats; + void** formats; +} mal_pa_source_info; + +typedef void (* mal_pa_context_notify_cb_t)(mal_pa_context* c, void* userdata); +typedef void (* mal_pa_sink_info_cb_t) (mal_pa_context* c, const mal_pa_sink_info* i, int eol, void* userdata); +typedef void (* mal_pa_source_info_cb_t) (mal_pa_context* c, const mal_pa_source_info* i, int eol, void* userdata); +typedef void (* mal_pa_stream_success_cb_t)(mal_pa_stream* s, int success, void* userdata); +typedef void (* mal_pa_stream_request_cb_t)(mal_pa_stream* s, size_t nbytes, void* userdata); +typedef void (* mal_pa_free_cb_t) (void* p); +#endif + + +typedef mal_pa_mainloop* (* mal_pa_mainloop_new_proc) (); +typedef void (* mal_pa_mainloop_free_proc) (mal_pa_mainloop* m); +typedef mal_pa_mainloop_api* (* mal_pa_mainloop_get_api_proc) (mal_pa_mainloop* m); +typedef int (* mal_pa_mainloop_iterate_proc) (mal_pa_mainloop* m, int block, int* retval); +typedef void (* mal_pa_mainloop_wakeup_proc) (mal_pa_mainloop* m); +typedef mal_pa_context* (* mal_pa_context_new_proc) (mal_pa_mainloop_api* mainloop, const char* name); +typedef void (* mal_pa_context_unref_proc) (mal_pa_context* c); +typedef int (* mal_pa_context_connect_proc) (mal_pa_context* c, const char* server, mal_pa_context_flags_t flags, const mal_pa_spawn_api* api); +typedef void (* mal_pa_context_disconnect_proc) (mal_pa_context* c); +typedef void (* mal_pa_context_set_state_callback_proc) (mal_pa_context* c, mal_pa_context_notify_cb_t cb, void* userdata); +typedef mal_pa_context_state_t (* mal_pa_context_get_state_proc) (mal_pa_context* c); +typedef mal_pa_operation* (* mal_pa_context_get_sink_info_list_proc) (mal_pa_context* c, mal_pa_sink_info_cb_t cb, void* userdata); +typedef mal_pa_operation* (* mal_pa_context_get_source_info_list_proc) (mal_pa_context* c, mal_pa_source_info_cb_t cb, void* userdata); +typedef mal_pa_operation* (* mal_pa_context_get_sink_info_by_name_proc) (mal_pa_context* c, const char* name, mal_pa_sink_info_cb_t cb, void* userdata); +typedef mal_pa_operation* (* mal_pa_context_get_source_info_by_name_proc)(mal_pa_context* c, const char* name, mal_pa_source_info_cb_t cb, void* userdata); +typedef void (* mal_pa_operation_unref_proc) (mal_pa_operation* o); +typedef mal_pa_operation_state_t (* mal_pa_operation_get_state_proc) (mal_pa_operation* o); +typedef mal_pa_channel_map* (* mal_pa_channel_map_init_extend_proc) (mal_pa_channel_map* m, unsigned channels, mal_pa_channel_map_def_t def); +typedef int (* mal_pa_channel_map_valid_proc) (const mal_pa_channel_map* m); +typedef int (* mal_pa_channel_map_compatible_proc) (const mal_pa_channel_map* m, const mal_pa_sample_spec* ss); +typedef mal_pa_stream* (* mal_pa_stream_new_proc) (mal_pa_context* c, const char* name, const mal_pa_sample_spec* ss, const mal_pa_channel_map* map); +typedef void (* mal_pa_stream_unref_proc) (mal_pa_stream* s); +typedef int (* mal_pa_stream_connect_playback_proc) (mal_pa_stream* s, const char* dev, const mal_pa_buffer_attr* attr, mal_pa_stream_flags_t flags, const mal_pa_cvolume* volume, mal_pa_stream* sync_stream); +typedef int (* mal_pa_stream_connect_record_proc) (mal_pa_stream* s, const char* dev, const mal_pa_buffer_attr* attr, mal_pa_stream_flags_t flags); +typedef int (* mal_pa_stream_disconnect_proc) (mal_pa_stream* s); +typedef mal_pa_stream_state_t (* mal_pa_stream_get_state_proc) (mal_pa_stream* s); +typedef const mal_pa_sample_spec* (* mal_pa_stream_get_sample_spec_proc) (mal_pa_stream* s); +typedef const mal_pa_channel_map* (* mal_pa_stream_get_channel_map_proc) (mal_pa_stream* s); +typedef const mal_pa_buffer_attr* (* mal_pa_stream_get_buffer_attr_proc) (mal_pa_stream* s); +typedef const char* (* mal_pa_stream_get_device_name_proc) (mal_pa_stream* s); +typedef void (* mal_pa_stream_set_write_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata); +typedef void (* mal_pa_stream_set_read_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata); +typedef mal_pa_operation* (* mal_pa_stream_flush_proc) (mal_pa_stream* s, mal_pa_stream_success_cb_t cb, void* userdata); +typedef mal_pa_operation* (* mal_pa_stream_drain_proc) (mal_pa_stream* s, mal_pa_stream_success_cb_t cb, void* userdata); +typedef mal_pa_operation* (* mal_pa_stream_cork_proc) (mal_pa_stream* s, int b, mal_pa_stream_success_cb_t cb, void* userdata); +typedef mal_pa_operation* (* mal_pa_stream_trigger_proc) (mal_pa_stream* s, mal_pa_stream_success_cb_t cb, void* userdata); +typedef int (* mal_pa_stream_begin_write_proc) (mal_pa_stream* s, void** data, size_t* nbytes); +typedef int (* mal_pa_stream_write_proc) (mal_pa_stream* s, const void* data, size_t nbytes, mal_pa_free_cb_t free_cb, int64_t offset, mal_pa_seek_mode_t seek); +typedef int (* mal_pa_stream_peek_proc) (mal_pa_stream* s, const void** data, size_t* nbytes); +typedef int (* mal_pa_stream_drop_proc) (mal_pa_stream* s); + +typedef struct +{ + mal_uint32 count; + mal_uint32 capacity; + mal_device_info* pInfo; +} mal_pulse_device_enum_data; + +mal_result mal_result_from_pulse(int result) +{ + switch (result) { + case MAL_PA_OK: return MAL_SUCCESS; + case MAL_PA_ERR_ACCESS: return MAL_ACCESS_DENIED; + case MAL_PA_ERR_INVALID: return MAL_INVALID_ARGS; + case MAL_PA_ERR_NOENTITY: return MAL_NO_DEVICE; + default: return MAL_ERROR; + } +} + +#if 0 +mal_pa_sample_format_t mal_format_to_pulse(mal_format format) +{ + if (mal_is_little_endian()) { + switch (format) { + case mal_format_s16: return MAL_PA_SAMPLE_S16LE; + case mal_format_s24: return MAL_PA_SAMPLE_S24LE; + case mal_format_s32: return MAL_PA_SAMPLE_S32LE; + case mal_format_f32: return MAL_PA_SAMPLE_FLOAT32LE; + default: break; + } + } else { + switch (format) { + case mal_format_s16: return MAL_PA_SAMPLE_S16BE; + case mal_format_s24: return MAL_PA_SAMPLE_S24BE; + case mal_format_s32: return MAL_PA_SAMPLE_S32BE; + case mal_format_f32: return MAL_PA_SAMPLE_FLOAT32BE; + default: break; + } + } + + // Endian agnostic. + switch (format) { + case mal_format_u8: return MAL_PA_SAMPLE_U8; + default: return MAL_PA_SAMPLE_INVALID; + } +} +#endif + +mal_format mal_format_from_pulse(mal_pa_sample_format_t format) +{ + if (mal_is_little_endian()) { + switch (format) { + case MAL_PA_SAMPLE_S16LE: return mal_format_s16; + case MAL_PA_SAMPLE_S24LE: return mal_format_s24; + case MAL_PA_SAMPLE_S32LE: return mal_format_s32; + case MAL_PA_SAMPLE_FLOAT32LE: return mal_format_f32; + default: break; + } + } else { + switch (format) { + case MAL_PA_SAMPLE_S16BE: return mal_format_s16; + case MAL_PA_SAMPLE_S24BE: return mal_format_s24; + case MAL_PA_SAMPLE_S32BE: return mal_format_s32; + case MAL_PA_SAMPLE_FLOAT32BE: return mal_format_f32; + default: break; + } + } + + // Endian agnostic. + switch (format) { + case MAL_PA_SAMPLE_U8: return mal_format_u8; + default: return mal_format_unknown; + } +} + +mal_channel mal_channel_position_from_pulse(mal_pa_channel_position_t position) +{ + switch (position) + { + case MAL_PA_CHANNEL_POSITION_INVALID: return MAL_CHANNEL_NONE; + case MAL_PA_CHANNEL_POSITION_MONO: return MAL_CHANNEL_MONO; + case MAL_PA_CHANNEL_POSITION_FRONT_LEFT: return MAL_CHANNEL_FRONT_LEFT; + case MAL_PA_CHANNEL_POSITION_FRONT_RIGHT: return MAL_CHANNEL_FRONT_RIGHT; + case MAL_PA_CHANNEL_POSITION_FRONT_CENTER: return MAL_CHANNEL_FRONT_CENTER; + case MAL_PA_CHANNEL_POSITION_REAR_CENTER: return MAL_CHANNEL_BACK_CENTER; + case MAL_PA_CHANNEL_POSITION_REAR_LEFT: return MAL_CHANNEL_BACK_LEFT; + case MAL_PA_CHANNEL_POSITION_REAR_RIGHT: return MAL_CHANNEL_BACK_RIGHT; + case MAL_PA_CHANNEL_POSITION_LFE: return MAL_CHANNEL_LFE; + case MAL_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MAL_CHANNEL_FRONT_LEFT_CENTER; + case MAL_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MAL_CHANNEL_FRONT_RIGHT_CENTER; + case MAL_PA_CHANNEL_POSITION_SIDE_LEFT: return MAL_CHANNEL_SIDE_LEFT; + case MAL_PA_CHANNEL_POSITION_SIDE_RIGHT: return MAL_CHANNEL_SIDE_RIGHT; + case MAL_PA_CHANNEL_POSITION_AUX0: return MAL_CHANNEL_AUX_0; + case MAL_PA_CHANNEL_POSITION_AUX1: return MAL_CHANNEL_AUX_1; + case MAL_PA_CHANNEL_POSITION_AUX2: return MAL_CHANNEL_AUX_2; + case MAL_PA_CHANNEL_POSITION_AUX3: return MAL_CHANNEL_AUX_3; + case MAL_PA_CHANNEL_POSITION_AUX4: return MAL_CHANNEL_AUX_4; + case MAL_PA_CHANNEL_POSITION_AUX5: return MAL_CHANNEL_AUX_5; + case MAL_PA_CHANNEL_POSITION_AUX6: return MAL_CHANNEL_AUX_6; + case MAL_PA_CHANNEL_POSITION_AUX7: return MAL_CHANNEL_AUX_7; + case MAL_PA_CHANNEL_POSITION_AUX8: return MAL_CHANNEL_AUX_8; + case MAL_PA_CHANNEL_POSITION_AUX9: return MAL_CHANNEL_AUX_9; + case MAL_PA_CHANNEL_POSITION_AUX10: return MAL_CHANNEL_AUX_10; + case MAL_PA_CHANNEL_POSITION_AUX11: return MAL_CHANNEL_AUX_11; + case MAL_PA_CHANNEL_POSITION_AUX12: return MAL_CHANNEL_AUX_12; + case MAL_PA_CHANNEL_POSITION_AUX13: return MAL_CHANNEL_AUX_13; + case MAL_PA_CHANNEL_POSITION_AUX14: return MAL_CHANNEL_AUX_14; + case MAL_PA_CHANNEL_POSITION_AUX15: return MAL_CHANNEL_AUX_15; + case MAL_PA_CHANNEL_POSITION_AUX16: return MAL_CHANNEL_AUX_16; + case MAL_PA_CHANNEL_POSITION_AUX17: return MAL_CHANNEL_AUX_17; + case MAL_PA_CHANNEL_POSITION_AUX18: return MAL_CHANNEL_AUX_18; + case MAL_PA_CHANNEL_POSITION_AUX19: return MAL_CHANNEL_AUX_19; + case MAL_PA_CHANNEL_POSITION_AUX20: return MAL_CHANNEL_AUX_20; + case MAL_PA_CHANNEL_POSITION_AUX21: return MAL_CHANNEL_AUX_21; + case MAL_PA_CHANNEL_POSITION_AUX22: return MAL_CHANNEL_AUX_22; + case MAL_PA_CHANNEL_POSITION_AUX23: return MAL_CHANNEL_AUX_23; + case MAL_PA_CHANNEL_POSITION_AUX24: return MAL_CHANNEL_AUX_24; + case MAL_PA_CHANNEL_POSITION_AUX25: return MAL_CHANNEL_AUX_25; + case MAL_PA_CHANNEL_POSITION_AUX26: return MAL_CHANNEL_AUX_26; + case MAL_PA_CHANNEL_POSITION_AUX27: return MAL_CHANNEL_AUX_27; + case MAL_PA_CHANNEL_POSITION_AUX28: return MAL_CHANNEL_AUX_28; + case MAL_PA_CHANNEL_POSITION_AUX29: return MAL_CHANNEL_AUX_29; + case MAL_PA_CHANNEL_POSITION_AUX30: return MAL_CHANNEL_AUX_30; + case MAL_PA_CHANNEL_POSITION_AUX31: return MAL_CHANNEL_AUX_31; + case MAL_PA_CHANNEL_POSITION_TOP_CENTER: return MAL_CHANNEL_TOP_CENTER; + case MAL_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MAL_CHANNEL_TOP_FRONT_LEFT; + case MAL_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MAL_CHANNEL_TOP_FRONT_RIGHT; + case MAL_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MAL_CHANNEL_TOP_FRONT_CENTER; + case MAL_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MAL_CHANNEL_TOP_BACK_LEFT; + case MAL_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MAL_CHANNEL_TOP_BACK_RIGHT; + case MAL_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MAL_CHANNEL_TOP_BACK_CENTER; + default: return MAL_CHANNEL_NONE; + } +} + +#if 0 +mal_pa_channel_position_t mal_channel_position_to_pulse(mal_channel position) +{ + switch (position) + { + case MAL_CHANNEL_NONE: return MAL_PA_CHANNEL_POSITION_INVALID; + case MAL_CHANNEL_FRONT_LEFT: return MAL_PA_CHANNEL_POSITION_FRONT_LEFT; + case MAL_CHANNEL_FRONT_RIGHT: return MAL_PA_CHANNEL_POSITION_FRONT_RIGHT; + case MAL_CHANNEL_FRONT_CENTER: return MAL_PA_CHANNEL_POSITION_FRONT_CENTER; + case MAL_CHANNEL_LFE: return MAL_PA_CHANNEL_POSITION_LFE; + case MAL_CHANNEL_BACK_LEFT: return MAL_PA_CHANNEL_POSITION_REAR_LEFT; + case MAL_CHANNEL_BACK_RIGHT: return MAL_PA_CHANNEL_POSITION_REAR_RIGHT; + case MAL_CHANNEL_FRONT_LEFT_CENTER: return MAL_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; + case MAL_CHANNEL_FRONT_RIGHT_CENTER: return MAL_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; + case MAL_CHANNEL_BACK_CENTER: return MAL_PA_CHANNEL_POSITION_REAR_CENTER; + case MAL_CHANNEL_SIDE_LEFT: return MAL_PA_CHANNEL_POSITION_SIDE_LEFT; + case MAL_CHANNEL_SIDE_RIGHT: return MAL_PA_CHANNEL_POSITION_SIDE_RIGHT; + case MAL_CHANNEL_TOP_CENTER: return MAL_PA_CHANNEL_POSITION_TOP_CENTER; + case MAL_CHANNEL_TOP_FRONT_LEFT: return MAL_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; + case MAL_CHANNEL_TOP_FRONT_CENTER: return MAL_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; + case MAL_CHANNEL_TOP_FRONT_RIGHT: return MAL_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; + case MAL_CHANNEL_TOP_BACK_LEFT: return MAL_PA_CHANNEL_POSITION_TOP_REAR_LEFT; + case MAL_CHANNEL_TOP_BACK_CENTER: return MAL_PA_CHANNEL_POSITION_TOP_REAR_CENTER; + case MAL_CHANNEL_TOP_BACK_RIGHT: return MAL_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; + case MAL_CHANNEL_19: return MAL_PA_CHANNEL_POSITION_AUX18; + case MAL_CHANNEL_20: return MAL_PA_CHANNEL_POSITION_AUX19; + case MAL_CHANNEL_21: return MAL_PA_CHANNEL_POSITION_AUX20; + case MAL_CHANNEL_22: return MAL_PA_CHANNEL_POSITION_AUX21; + case MAL_CHANNEL_23: return MAL_PA_CHANNEL_POSITION_AUX22; + case MAL_CHANNEL_24: return MAL_PA_CHANNEL_POSITION_AUX23; + case MAL_CHANNEL_25: return MAL_PA_CHANNEL_POSITION_AUX24; + case MAL_CHANNEL_26: return MAL_PA_CHANNEL_POSITION_AUX25; + case MAL_CHANNEL_27: return MAL_PA_CHANNEL_POSITION_AUX26; + case MAL_CHANNEL_28: return MAL_PA_CHANNEL_POSITION_AUX27; + case MAL_CHANNEL_29: return MAL_PA_CHANNEL_POSITION_AUX28; + case MAL_CHANNEL_30: return MAL_PA_CHANNEL_POSITION_AUX29; + case MAL_CHANNEL_31: return MAL_PA_CHANNEL_POSITION_AUX30; + case MAL_CHANNEL_32: return MAL_PA_CHANNEL_POSITION_AUX31; + default: return (mal_pa_channel_position_t)position; + } +} +#endif + + +mal_result mal_wait_for_operation__pulse(mal_context* pContext, mal_pa_mainloop* pMainLoop, mal_pa_operation* pOP) +{ + mal_assert(pContext != NULL); + mal_assert(pMainLoop != NULL); + mal_assert(pOP != NULL); + + while (((mal_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP) != MAL_PA_OPERATION_DONE) { + int error = ((mal_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL); + if (error < 0) { + return mal_result_from_pulse(error); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_device__wait_for_operation__pulse(mal_device* pDevice, mal_pa_operation* pOP) +{ + mal_assert(pDevice != NULL); + mal_assert(pOP != NULL); + + return mal_wait_for_operation__pulse(pDevice->pContext, (mal_pa_mainloop*)pDevice->pulse.pMainLoop, pOP); +} + + +mal_bool32 mal_context_is_device_id_equal__pulse(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return mal_strcmp(pID0->pulse, pID1->pulse) == 0; +} + + +typedef struct +{ + mal_context* pContext; + mal_enum_devices_callback_proc callback; + void* pUserData; + mal_bool32 isTerminated; +} mal_context_enumerate_devices_callback_data__pulse; + +void mal_context_enumerate_devices_sink_callback__pulse(mal_pa_context* pPulseContext, const mal_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) +{ + mal_context_enumerate_devices_callback_data__pulse* pData = (mal_context_enumerate_devices_callback_data__pulse*)pUserData; + mal_assert(pData != NULL); + + if (endOfList || pData->isTerminated) { + return; + } + + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + + // The name from PulseAudio is the ID for mini_al. + if (pSinkInfo->name != NULL) { + mal_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); + } + + // The description from PulseAudio is the name for mini_al. + if (pSinkInfo->description != NULL) { + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); + } + + pData->isTerminated = !pData->callback(pData->pContext, mal_device_type_playback, &deviceInfo, pData->pUserData); +} + +void mal_context_enumerate_devices_source_callback__pulse(mal_pa_context* pPulseContext, const mal_pa_source_info* pSinkInfo, int endOfList, void* pUserData) +{ + mal_context_enumerate_devices_callback_data__pulse* pData = (mal_context_enumerate_devices_callback_data__pulse*)pUserData; + mal_assert(pData != NULL); + + if (endOfList || pData->isTerminated) { + return; + } + + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + + // The name from PulseAudio is the ID for mini_al. + if (pSinkInfo->name != NULL) { + mal_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); + } + + // The description from PulseAudio is the name for mini_al. + if (pSinkInfo->description != NULL) { + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); + } + + pData->isTerminated = !pData->callback(pData->pContext, mal_device_type_capture, &deviceInfo, pData->pUserData); +} + +mal_result mal_context_enumerate_devices__pulse(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + mal_result result = MAL_SUCCESS; + + mal_context_enumerate_devices_callback_data__pulse callbackData; + callbackData.pContext = pContext; + callbackData.callback = callback; + callbackData.pUserData = pUserData; + callbackData.isTerminated = MAL_FALSE; + + mal_pa_operation* pOP = NULL; + + mal_pa_mainloop* pMainLoop = ((mal_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); + if (pMainLoop == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + mal_pa_mainloop_api* pAPI = ((mal_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop); + if (pAPI == NULL) { + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return MAL_FAILED_TO_INIT_BACKEND; + } + + mal_pa_context* pPulseContext = ((mal_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->config.pulse.pApplicationName); + if (pPulseContext == NULL) { + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return MAL_FAILED_TO_INIT_BACKEND; + } + + int error = ((mal_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->config.pulse.pServerName, 0, NULL); + if (error != MAL_PA_OK) { + ((mal_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext); + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return mal_result_from_pulse(error); + } + + while (((mal_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext) != MAL_PA_CONTEXT_READY) { + error = ((mal_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL); + if (error < 0) { + result = mal_result_from_pulse(error); + goto done; + } + } + + + // Playback. + if (!callbackData.isTerminated) { + pOP = ((mal_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)(pPulseContext, mal_context_enumerate_devices_sink_callback__pulse, &callbackData); + if (pOP == NULL) { + result = MAL_ERROR; + goto done; + } + + result = mal_wait_for_operation__pulse(pContext, pMainLoop, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + if (result != MAL_SUCCESS) { + goto done; + } + } + + + // Capture. + if (!callbackData.isTerminated) { + pOP = ((mal_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)(pPulseContext, mal_context_enumerate_devices_source_callback__pulse, &callbackData); + if (pOP == NULL) { + result = MAL_ERROR; + goto done; + } + + result = mal_wait_for_operation__pulse(pContext, pMainLoop, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + if (result != MAL_SUCCESS) { + goto done; + } + } + +done: + ((mal_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext); + ((mal_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext); + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return result; +} + + +typedef struct +{ + mal_device_info* pDeviceInfo; + mal_bool32 foundDevice; +} mal_context_get_device_info_callback_data__pulse; + +void mal_context_get_device_info_sink_callback__pulse(mal_pa_context* pPulseContext, const mal_pa_sink_info* pInfo, int endOfList, void* pUserData) +{ + if (endOfList > 0) { + return; + } + + mal_context_get_device_info_callback_data__pulse* pData = (mal_context_get_device_info_callback_data__pulse*)pUserData; + mal_assert(pData != NULL); + pData->foundDevice = MAL_TRUE; + + if (pInfo->name != NULL) { + mal_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); + } + + if (pInfo->description != NULL) { + mal_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); + } + + pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels; + pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels; + pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate; + pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate; + pData->pDeviceInfo->formatCount = 1; + pData->pDeviceInfo->formats[0] = mal_format_from_pulse(pInfo->sample_spec.format); +} + +void mal_context_get_device_info_source_callback__pulse(mal_pa_context* pPulseContext, const mal_pa_source_info* pInfo, int endOfList, void* pUserData) +{ + if (endOfList > 0) { + return; + } + + mal_context_get_device_info_callback_data__pulse* pData = (mal_context_get_device_info_callback_data__pulse*)pUserData; + mal_assert(pData != NULL); + pData->foundDevice = MAL_TRUE; + + if (pInfo->name != NULL) { + mal_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); + } + + if (pInfo->description != NULL) { + mal_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); + } + + pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels; + pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels; + pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate; + pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate; + pData->pDeviceInfo->formatCount = 1; + pData->pDeviceInfo->formats[0] = mal_format_from_pulse(pInfo->sample_spec.format); +} + +mal_result mal_context_get_device_info__pulse(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + + mal_result result = MAL_SUCCESS; + + mal_context_get_device_info_callback_data__pulse callbackData; + callbackData.pDeviceInfo = pDeviceInfo; + callbackData.foundDevice = MAL_FALSE; + + mal_pa_operation* pOP = NULL; + + mal_pa_mainloop* pMainLoop = ((mal_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); + if (pMainLoop == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + mal_pa_mainloop_api* pAPI = ((mal_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop); + if (pAPI == NULL) { + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return MAL_FAILED_TO_INIT_BACKEND; + } + + mal_pa_context* pPulseContext = ((mal_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->config.pulse.pApplicationName); + if (pPulseContext == NULL) { + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return MAL_FAILED_TO_INIT_BACKEND; + } + + int error = ((mal_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->config.pulse.pServerName, 0, NULL); + if (error != MAL_PA_OK) { + ((mal_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext); + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return mal_result_from_pulse(error); + } + + while (((mal_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext) != MAL_PA_CONTEXT_READY) { + error = ((mal_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL); + if (error < 0) { + result = mal_result_from_pulse(error); + goto done; + } + } + + if (deviceType == mal_device_type_playback) { + pOP = ((mal_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)(pPulseContext, pDeviceID->pulse, mal_context_get_device_info_sink_callback__pulse, &callbackData); + } else { + pOP = ((mal_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)(pPulseContext, pDeviceID->pulse, mal_context_get_device_info_source_callback__pulse, &callbackData); + } + + if (pOP != NULL) { + mal_wait_for_operation__pulse(pContext, pMainLoop, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + } else { + result = MAL_ERROR; + goto done; + } + + if (!callbackData.foundDevice) { + result = MAL_NO_DEVICE; + goto done; + } + + +done: + ((mal_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext); + ((mal_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext); + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return result; +} + + +void mal_pulse_device_state_callback(mal_pa_context* pPulseContext, void* pUserData) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + pDevice->pulse.pulseContextState = ((mal_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext); +} + +void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes, void* pUserData) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + size_t bytesRemaining = sizeInBytes; + while (bytesRemaining > 0) { + size_t bytesToReadFromClient = bytesRemaining; + if (bytesToReadFromClient > 0xFFFFFFFF) { + bytesToReadFromClient = 0xFFFFFFFF; + } + + void* pBuffer = NULL; + int error = ((mal_pa_stream_begin_write_proc)pContext->pulse.pa_stream_begin_write)((mal_pa_stream*)pDevice->pulse.pStream, &pBuffer, &bytesToReadFromClient); + if (error < 0) { + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve write buffer for sending data to the device.", mal_result_from_pulse(error)); + return; + } + + if (pBuffer != NULL && bytesToReadFromClient > 0) { + mal_uint32 framesToReadFromClient = (mal_uint32)bytesToReadFromClient / (pDevice->internalChannels*mal_get_bytes_per_sample(pDevice->internalFormat)); + if (framesToReadFromClient > 0) { + mal_device__read_frames_from_client(pDevice, framesToReadFromClient, pBuffer); + + error = ((mal_pa_stream_write_proc)pContext->pulse.pa_stream_write)((mal_pa_stream*)pDevice->pulse.pStream, pBuffer, bytesToReadFromClient, NULL, 0, MAL_PA_SEEK_RELATIVE); + if (error < 0) { + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to write data to the PulseAudio stream.", mal_result_from_pulse(error)); + return; + } + } + + bytesRemaining -= bytesToReadFromClient; + } + } +} + +void mal_pulse_device_read_callback(mal_pa_stream* pStream, size_t sizeInBytes, void* pUserData) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + size_t bytesRemaining = sizeInBytes; + while (bytesRemaining > 0) { + size_t bytesToSendToClient = bytesRemaining; + if (bytesToSendToClient > 0xFFFFFFFF) { + bytesToSendToClient = 0xFFFFFFFF; + } + + const void* pBuffer = NULL; + int error = ((mal_pa_stream_peek_proc)pContext->pulse.pa_stream_peek)((mal_pa_stream*)pDevice->pulse.pStream, &pBuffer, &sizeInBytes); + if (error < 0) { + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve read buffer for reading data from the device.", mal_result_from_pulse(error)); + return; + } + + if (pBuffer != NULL) { + mal_uint32 framesToSendToClient = (mal_uint32)bytesToSendToClient / (pDevice->internalChannels*mal_get_bytes_per_sample(pDevice->internalFormat)); + if (framesToSendToClient > 0) { + mal_device__send_frames_to_client(pDevice, framesToSendToClient, pBuffer); + } + } + + error = ((mal_pa_stream_drop_proc)pContext->pulse.pa_stream_drop)((mal_pa_stream*)pDevice->pulse.pStream); + if (error < 0) { + mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to drop fragment from the PulseAudio stream.", mal_result_from_pulse(error)); + } + + bytesRemaining -= bytesToSendToClient; + } +} + +void mal_device_sink_info_callback(mal_pa_context* pPulseContext, const mal_pa_sink_info* pInfo, int endOfList, void* pUserData) +{ + if (endOfList > 0) { + return; + } + + mal_pa_sink_info* pInfoOut = (mal_pa_sink_info*)pUserData; + mal_assert(pInfoOut != NULL); + + *pInfoOut = *pInfo; +} + +void mal_device_source_info_callback(mal_pa_context* pPulseContext, const mal_pa_source_info* pInfo, int endOfList, void* pUserData) +{ + if (endOfList > 0) { + return; + } + + mal_pa_source_info* pInfoOut = (mal_pa_source_info*)pUserData; + mal_assert(pInfoOut != NULL); + + *pInfoOut = *pInfo; +} + +void mal_device_sink_name_callback(mal_pa_context* pPulseContext, const mal_pa_sink_info* pInfo, int endOfList, void* pUserData) +{ + if (endOfList > 0) { + return; + } + + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), pInfo->description, (size_t)-1); +} + +void mal_device_source_name_callback(mal_pa_context* pPulseContext, const mal_pa_source_info* pInfo, int endOfList, void* pUserData) +{ + if (endOfList > 0) { + return; + } + + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), pInfo->description, (size_t)-1); +} + +void mal_device_uninit__pulse(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + ((mal_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((mal_pa_stream*)pDevice->pulse.pStream); + ((mal_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((mal_pa_stream*)pDevice->pulse.pStream); + ((mal_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((mal_pa_context*)pDevice->pulse.pPulseContext); + ((mal_pa_context_unref_proc)pContext->pulse.pa_context_unref)((mal_pa_context*)pDevice->pulse.pPulseContext); + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((mal_pa_mainloop*)pDevice->pulse.pMainLoop); +} + +mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->pulse); + + mal_result result = MAL_SUCCESS; + int error = 0; + + const char* dev = NULL; + if (pDeviceID != NULL) { + dev = pDeviceID->pulse; + } + + mal_uint32 bufferSizeInFrames = pConfig->bufferSizeInFrames; + + mal_pa_sink_info sinkInfo; + mal_pa_source_info sourceInfo; + mal_pa_operation* pOP = NULL; + + mal_pa_sample_spec ss; + mal_pa_channel_map cmap; + mal_pa_buffer_attr attr; + + const mal_pa_sample_spec* pActualSS = NULL; + const mal_pa_channel_map* pActualCMap = NULL; + const mal_pa_buffer_attr* pActualAttr = NULL; + + + + pDevice->pulse.pMainLoop = ((mal_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); + if (pDevice->pulse.pMainLoop == NULL) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create main loop for device.", MAL_FAILED_TO_INIT_BACKEND); + goto on_error0; + } + + pDevice->pulse.pAPI = ((mal_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((mal_pa_mainloop*)pDevice->pulse.pMainLoop); + if (pDevice->pulse.pAPI == NULL) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve PulseAudio main loop.", MAL_FAILED_TO_INIT_BACKEND); + goto on_error1; + } + + pDevice->pulse.pPulseContext = ((mal_pa_context_new_proc)pContext->pulse.pa_context_new)((mal_pa_mainloop_api*)pDevice->pulse.pAPI, pContext->config.pulse.pApplicationName); + if (pDevice->pulse.pPulseContext == NULL) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context for device.", MAL_FAILED_TO_INIT_BACKEND); + goto on_error1; + } + + error = ((mal_pa_context_connect_proc)pContext->pulse.pa_context_connect)((mal_pa_context*)pDevice->pulse.pPulseContext, pContext->config.pulse.pServerName, (pContext->config.pulse.tryAutoSpawn) ? 0 : MAL_PA_CONTEXT_NOAUTOSPAWN, NULL); + if (error != MAL_PA_OK) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", mal_result_from_pulse(error)); + goto on_error2; + } + + + pDevice->pulse.pulseContextState = MAL_PA_CONTEXT_UNCONNECTED; + ((mal_pa_context_set_state_callback_proc)pContext->pulse.pa_context_set_state_callback)((mal_pa_context*)pDevice->pulse.pPulseContext, mal_pulse_device_state_callback, pDevice); + + // Wait for PulseAudio to get itself ready before returning. + for (;;) { + if (pDevice->pulse.pulseContextState == MAL_PA_CONTEXT_READY) { + break; + } else { + error = ((mal_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((mal_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); // 1 = block. + if (error < 0) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio context.", mal_result_from_pulse(error)); + goto on_error3; + } + continue; + } + + // An error may have occurred. + if (pDevice->pulse.pulseContextState == MAL_PA_CONTEXT_FAILED || pDevice->pulse.pulseContextState == MAL_PA_CONTEXT_TERMINATED) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MAL_ERROR); + goto on_error3; + } + + error = ((mal_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((mal_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); + if (error < 0) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio context.", mal_result_from_pulse(error)); + goto on_error3; + } + } + + + if (type == mal_device_type_playback) { + pOP = ((mal_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, dev, mal_device_sink_info_callback, &sinkInfo); + } else { + pOP = ((mal_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, dev, mal_device_source_info_callback, &sourceInfo); + } + + if (pOP != NULL) { + mal_device__wait_for_operation__pulse(pDevice, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + } + + +#if 0 + mal_pa_sample_spec deviceSS; + mal_pa_channel_map deviceCMap; + if (type == mal_device_type_playback) { + deviceSS = sinkInfo.sample_spec; + deviceCMap = sinkInfo.channel_map; + } else { + deviceSS = sourceInfo.sample_spec; + deviceCMap = sourceInfo.channel_map; + } + + if (pDevice->usingDefaultFormat) { + ss.format = deviceSS.format; + } else { + ss.format = mal_format_to_pulse(pConfig->format); + } + if (ss.format == MAL_PA_SAMPLE_INVALID) { + ss.format = MAL_PA_SAMPLE_S16LE; + } + + if (pDevice->usingDefaultChannels) { + ss.channels = deviceSS.channels; + } else { + ss.channels = pConfig->channels; + } + + if (pDevice->usingDefaultSampleRate) { + ss.rate = deviceSS.rate; + } else { + ss.rate = pConfig->sampleRate; + } + + + if (pDevice->usingDefaultChannelMap) { + cmap = deviceCMap; + } else { + cmap.channels = pConfig->channels; + for (mal_uint32 iChannel = 0; iChannel < pConfig->channels; ++iChannel) { + cmap.map[iChannel] = mal_channel_position_to_pulse(pConfig->channelMap[iChannel]); + } + + if (((mal_pa_channel_map_valid_proc)pContext->pulse.pa_channel_map_valid)(&cmap) == 0 || ((mal_pa_channel_map_compatible_proc)pContext->pulse.pa_channel_map_compatible)(&cmap, &ss) == 0) { + ((mal_pa_channel_map_init_extend_proc)pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MAL_PA_CHANNEL_MAP_DEFAULT); // The channel map is invalid, so just fall back to the default. + } + } +#else + if (type == mal_device_type_playback) { + ss = sinkInfo.sample_spec; + cmap = sinkInfo.channel_map; + } else { + ss = sourceInfo.sample_spec; + cmap = sourceInfo.channel_map; + } +#endif + + // Buffer size. + bufferSizeInFrames = pDevice->bufferSizeInFrames; + if (bufferSizeInFrames == 0) { + bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, ss.rate); + + // PulseAudio seems to need a bit of an bit of size to the buffer to be reliable. + if (pDevice->usingDefaultBufferSize) { + float bufferSizeScaleFactor = 1.0f; + if (type == mal_device_type_capture) { + bufferSizeScaleFactor = 2.0f; + } + + bufferSizeInFrames = mal_scale_buffer_size(bufferSizeInFrames, bufferSizeScaleFactor); + } + } + + attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(ss.format))*ss.channels; + attr.tlength = attr.maxlength / pConfig->periods; + attr.prebuf = (mal_uint32)-1; + attr.minreq = attr.tlength; + attr.fragsize = attr.tlength; + + char streamName[256]; + if (pConfig->pulse.pStreamName != NULL) { + mal_strncpy_s(streamName, sizeof(streamName), pConfig->pulse.pStreamName, (size_t)-1); + } else { + static int g_StreamCounter = 0; + mal_strcpy_s(streamName, sizeof(streamName), "mini_al:"); + mal_itoa_s(g_StreamCounter, streamName + 8, sizeof(streamName)-8, 10); // 8 = strlen("mini_al:") + g_StreamCounter += 1; + } + + pDevice->pulse.pStream = ((mal_pa_stream_new_proc)pContext->pulse.pa_stream_new)((mal_pa_context*)pDevice->pulse.pPulseContext, streamName, &ss, &cmap); + if (pDevice->pulse.pStream == NULL) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio stream.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + goto on_error3; + } + + + + if (type == mal_device_type_playback) { + error = ((mal_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, MAL_PA_STREAM_START_CORKED, NULL, NULL); + } else { + error = ((mal_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, MAL_PA_STREAM_START_CORKED); + } + + if (error != MAL_PA_OK) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio stream.", mal_result_from_pulse(error)); + goto on_error4; + } + + while (((mal_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((mal_pa_stream*)pDevice->pulse.pStream) != MAL_PA_STREAM_READY) { + error = ((mal_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((mal_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); + if (error < 0) { + result = mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio stream.", mal_result_from_pulse(error)); + goto on_error5; + } + } + + + // Internal format. + pActualSS = ((mal_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((mal_pa_stream*)pDevice->pulse.pStream); + if (pActualSS != NULL) { + ss = *pActualSS; + } + + pDevice->internalFormat = mal_format_from_pulse(ss.format); + pDevice->internalChannels = ss.channels; + pDevice->internalSampleRate = ss.rate; + + + // Internal channel map. + pActualCMap = ((mal_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((mal_pa_stream*)pDevice->pulse.pStream); + if (pActualCMap != NULL) { + cmap = *pActualCMap; + } + + for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) { + pDevice->internalChannelMap[iChannel] = mal_channel_position_from_pulse(cmap.map[iChannel]); + } + + + // Buffer size. + pActualAttr = ((mal_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((mal_pa_stream*)pDevice->pulse.pStream); + if (pActualAttr != NULL) { + attr = *pActualAttr; + } + + pDevice->bufferSizeInFrames = attr.maxlength / (mal_get_bytes_per_sample(pDevice->internalFormat)*pDevice->internalChannels); + pDevice->periods = attr.maxlength / attr.tlength; + + + // Grab the name of the device if we can. + dev = ((mal_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((mal_pa_stream*)pDevice->pulse.pStream); + if (dev != NULL) { + mal_pa_operation* pOP = NULL; + if (type == mal_device_type_playback) { + pOP = ((mal_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, dev, mal_device_sink_name_callback, pDevice); + } else { + pOP = ((mal_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((mal_pa_context*)pDevice->pulse.pPulseContext, dev, mal_device_source_name_callback, pDevice); + } + + if (pOP != NULL) { + mal_device__wait_for_operation__pulse(pDevice, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + } + } + + + // Set callbacks for reading and writing data to/from the PulseAudio stream. + if (type == mal_device_type_playback) { + ((mal_pa_stream_set_write_callback_proc)pContext->pulse.pa_stream_set_write_callback)((mal_pa_stream*)pDevice->pulse.pStream, mal_pulse_device_write_callback, pDevice); + } else { + ((mal_pa_stream_set_read_callback_proc)pContext->pulse.pa_stream_set_read_callback)((mal_pa_stream*)pDevice->pulse.pStream, mal_pulse_device_read_callback, pDevice); + } + + + pDevice->pulse.fragmentSizeInBytes = attr.tlength; + + return MAL_SUCCESS; + + +on_error5: ((mal_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((mal_pa_stream*)pDevice->pulse.pStream); +on_error4: ((mal_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((mal_pa_stream*)pDevice->pulse.pStream); +on_error3: ((mal_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((mal_pa_context*)pDevice->pulse.pPulseContext); +on_error2: ((mal_pa_context_unref_proc)pContext->pulse.pa_context_unref)((mal_pa_context*)pDevice->pulse.pPulseContext); +on_error1: ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((mal_pa_mainloop*)pDevice->pulse.pMainLoop); +on_error0: + return result; +} + + +void mal_pulse_operation_complete_callback(mal_pa_stream* pStream, int success, void* pUserData) +{ + mal_bool32* pIsSuccessful = (mal_bool32*)pUserData; + mal_assert(pIsSuccessful != NULL); + + *pIsSuccessful = (mal_bool32)success; +} + +mal_result mal_device__cork_stream__pulse(mal_device* pDevice, int cork) +{ + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + mal_bool32 wasSuccessful = MAL_FALSE; + mal_pa_operation* pOP = ((mal_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)((mal_pa_stream*)pDevice->pulse.pStream, cork, mal_pulse_operation_complete_callback, &wasSuccessful); + if (pOP == NULL) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.", (cork == 0) ? MAL_FAILED_TO_START_BACKEND_DEVICE : MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } + + mal_result result = mal_device__wait_for_operation__pulse(pDevice, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + + if (result != MAL_SUCCESS) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.", result); + } + + if (!wasSuccessful) { + if (cork) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to stop PulseAudio stream.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } else { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start PulseAudio stream.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_device__start_backend__pulse(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + // For both playback and capture we need to uncork the stream. Afterwards, for playback we need to fill in an initial chunk + // of data, equal to the trigger length. That should then start actual playback. + mal_result result = mal_device__cork_stream__pulse(pDevice, 0); + if (result != MAL_SUCCESS) { + return result; + } + + // A playback device is started by simply writing data to it. For capture we do nothing. + if (pDevice->type == mal_device_type_playback) { + // Playback. + mal_pulse_device_write_callback((mal_pa_stream*)pDevice->pulse.pStream, pDevice->pulse.fragmentSizeInBytes, pDevice); + + // Force an immediate start of the device just to be sure. + mal_pa_operation* pOP = ((mal_pa_stream_trigger_proc)pContext->pulse.pa_stream_trigger)((mal_pa_stream*)pDevice->pulse.pStream, NULL, NULL); + if (pOP != NULL) { + mal_device__wait_for_operation__pulse(pDevice, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + } + } else { + // Capture. Do nothing. + } + + return MAL_SUCCESS; +} + +mal_result mal_device__stop_backend__pulse(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + mal_result result = mal_device__cork_stream__pulse(pDevice, 1); + if (result != MAL_SUCCESS) { + return result; + } + + // For playback, buffers need to be flushed. For capture they need to be drained. + mal_bool32 wasSuccessful; + mal_pa_operation* pOP = NULL; + if (pDevice->type == mal_device_type_playback) { + pOP = ((mal_pa_stream_flush_proc)pContext->pulse.pa_stream_flush)((mal_pa_stream*)pDevice->pulse.pStream, mal_pulse_operation_complete_callback, &wasSuccessful); + } else { + pOP = ((mal_pa_stream_drain_proc)pContext->pulse.pa_stream_drain)((mal_pa_stream*)pDevice->pulse.pStream, mal_pulse_operation_complete_callback, &wasSuccessful); + } + + if (pOP == NULL) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to flush buffers after stopping PulseAudio stream.", MAL_ERROR); + } + + result = mal_device__wait_for_operation__pulse(pDevice, pOP); + ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + + if (result != MAL_SUCCESS) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to flush.", result); + } + + if (!wasSuccessful) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to flush buffers after stopping PulseAudio stream.", MAL_ERROR); + } + + return MAL_SUCCESS; +} + +mal_result mal_device__break_main_loop__pulse(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + pDevice->pulse.breakFromMainLoop = MAL_TRUE; + ((mal_pa_mainloop_wakeup_proc)pContext->pulse.pa_mainloop_wakeup)((mal_pa_mainloop*)pDevice->pulse.pMainLoop); + + return MAL_SUCCESS; +} + +mal_result mal_device__main_loop__pulse(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + pDevice->pulse.breakFromMainLoop = MAL_FALSE; + while (!pDevice->pulse.breakFromMainLoop) { + // Break from the main loop if the device isn't started anymore. Likely what's happened is the application + // has requested that the device be stopped. + if (!mal_device_is_started(pDevice)) { + break; + } + + int resultPA = ((mal_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((mal_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); + if (resultPA < 0) { + break; // Some error occurred. + } + } + + return MAL_SUCCESS; +} + + +mal_result mal_context_uninit__pulse(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_pulseaudio); + +#ifndef MAL_NO_RUNTIME_LINKING + mal_dlclose(pContext->pulse.pulseSO); +#endif + + return MAL_SUCCESS; +} + +mal_result mal_context_init__pulse(mal_context* pContext) +{ + mal_assert(pContext != NULL); + +#ifndef MAL_NO_RUNTIME_LINKING + // libpulse.so + const char* libpulseNames[] = { + "libpulse.so", + "libpulse.so.0" + }; + + for (size_t i = 0; i < mal_countof(libpulseNames); ++i) { + pContext->pulse.pulseSO = mal_dlopen(libpulseNames[i]); + if (pContext->pulse.pulseSO != NULL) { + break; + } + } + + if (pContext->pulse.pulseSO == NULL) { + return MAL_NO_BACKEND; + } + + pContext->pulse.pa_mainloop_new = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_mainloop_new"); + pContext->pulse.pa_mainloop_free = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_mainloop_free"); + pContext->pulse.pa_mainloop_get_api = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_mainloop_get_api"); + pContext->pulse.pa_mainloop_iterate = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_mainloop_iterate"); + pContext->pulse.pa_mainloop_wakeup = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_mainloop_wakeup"); + pContext->pulse.pa_context_new = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_new"); + pContext->pulse.pa_context_unref = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_unref"); + pContext->pulse.pa_context_connect = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_connect"); + pContext->pulse.pa_context_disconnect = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_disconnect"); + pContext->pulse.pa_context_set_state_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_set_state_callback"); + pContext->pulse.pa_context_get_state = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_get_state"); + pContext->pulse.pa_context_get_sink_info_list = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); + pContext->pulse.pa_context_get_source_info_list = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_get_source_info_list"); + pContext->pulse.pa_context_get_sink_info_by_name = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); + pContext->pulse.pa_context_get_source_info_by_name = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); + pContext->pulse.pa_operation_unref = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_operation_unref"); + pContext->pulse.pa_operation_get_state = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_operation_get_state"); + pContext->pulse.pa_channel_map_init_extend = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_channel_map_init_extend"); + pContext->pulse.pa_channel_map_valid = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_channel_map_valid"); + pContext->pulse.pa_channel_map_compatible = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_channel_map_compatible"); + pContext->pulse.pa_stream_new = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_new"); + pContext->pulse.pa_stream_unref = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_unref"); + pContext->pulse.pa_stream_connect_playback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_connect_playback"); + pContext->pulse.pa_stream_connect_record = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_connect_record"); + pContext->pulse.pa_stream_disconnect = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_disconnect"); + pContext->pulse.pa_stream_get_state = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_state"); + pContext->pulse.pa_stream_get_sample_spec = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); + pContext->pulse.pa_stream_get_channel_map = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_channel_map"); + pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); + pContext->pulse.pa_stream_get_device_name = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_device_name"); + pContext->pulse.pa_stream_set_write_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_write_callback"); + pContext->pulse.pa_stream_set_read_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_read_callback"); + pContext->pulse.pa_stream_flush = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_flush"); + pContext->pulse.pa_stream_drain = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_drain"); + pContext->pulse.pa_stream_cork = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_cork"); + pContext->pulse.pa_stream_trigger = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_trigger"); + pContext->pulse.pa_stream_begin_write = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_begin_write"); + pContext->pulse.pa_stream_write = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_write"); + pContext->pulse.pa_stream_peek = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_peek"); + pContext->pulse.pa_stream_drop = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_drop"); +#else + // This strange assignment system is just for type safety. + mal_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; + mal_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; + mal_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; + mal_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; + mal_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; + mal_pa_context_new_proc _pa_context_new = pa_context_new; + mal_pa_context_unref_proc _pa_context_unref = pa_context_unref; + mal_pa_context_connect_proc _pa_context_connect = pa_context_connect; + mal_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; + mal_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; + mal_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; + mal_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; + mal_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; + mal_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; + mal_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; + mal_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; + mal_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; + mal_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; + mal_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; + mal_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; + mal_pa_stream_new_proc _pa_stream_new = pa_stream_new; + mal_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; + mal_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; + mal_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; + mal_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; + mal_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; + mal_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; + mal_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; + mal_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; + mal_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; + mal_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; + mal_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; + mal_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; + mal_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; + mal_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; + mal_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; + mal_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; + mal_pa_stream_write_proc _pa_stream_write = pa_stream_write; + mal_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; + mal_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; + + pContext->pulse.pa_mainloop_new = (mal_proc)_pa_mainloop_new; + pContext->pulse.pa_mainloop_free = (mal_proc)_pa_mainloop_free; + pContext->pulse.pa_mainloop_get_api = (mal_proc)_pa_mainloop_get_api; + pContext->pulse.pa_mainloop_iterate = (mal_proc)_pa_mainloop_iterate; + pContext->pulse.pa_mainloop_wakeup = (mal_proc)_pa_mainloop_wakeup; + pContext->pulse.pa_context_new = (mal_proc)_pa_context_new; + pContext->pulse.pa_context_unref = (mal_proc)_pa_context_unref; + pContext->pulse.pa_context_connect = (mal_proc)_pa_context_connect; + pContext->pulse.pa_context_disconnect = (mal_proc)_pa_context_disconnect; + pContext->pulse.pa_context_set_state_callback = (mal_proc)_pa_context_set_state_callback; + pContext->pulse.pa_context_get_state = (mal_proc)_pa_context_get_state; + pContext->pulse.pa_context_get_sink_info_list = (mal_proc)_pa_context_get_sink_info_list; + pContext->pulse.pa_context_get_source_info_list = (mal_proc)_pa_context_get_source_info_list; + pContext->pulse.pa_context_get_sink_info_by_name = (mal_proc)_pa_context_get_sink_info_by_name; + pContext->pulse.pa_context_get_source_info_by_name = (mal_proc)_pa_context_get_source_info_by_name; + pContext->pulse.pa_operation_unref = (mal_proc)_pa_operation_unref; + pContext->pulse.pa_operation_get_state = (mal_proc)_pa_operation_get_state; + pContext->pulse.pa_channel_map_init_extend = (mal_proc)_pa_channel_map_init_extend; + pContext->pulse.pa_channel_map_valid = (mal_proc)_pa_channel_map_valid; + pContext->pulse.pa_channel_map_compatible = (mal_proc)_pa_channel_map_compatible; + pContext->pulse.pa_stream_new = (mal_proc)_pa_stream_new; + pContext->pulse.pa_stream_unref = (mal_proc)_pa_stream_unref; + pContext->pulse.pa_stream_connect_playback = (mal_proc)_pa_stream_connect_playback; + pContext->pulse.pa_stream_connect_record = (mal_proc)_pa_stream_connect_record; + pContext->pulse.pa_stream_disconnect = (mal_proc)_pa_stream_disconnect; + pContext->pulse.pa_stream_get_state = (mal_proc)_pa_stream_get_state; + pContext->pulse.pa_stream_get_sample_spec = (mal_proc)_pa_stream_get_sample_spec; + pContext->pulse.pa_stream_get_channel_map = (mal_proc)_pa_stream_get_channel_map; + pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)_pa_stream_get_buffer_attr; + pContext->pulse.pa_stream_get_device_name = (mal_proc)_pa_stream_get_device_name; + pContext->pulse.pa_stream_set_write_callback = (mal_proc)_pa_stream_set_write_callback; + pContext->pulse.pa_stream_set_read_callback = (mal_proc)_pa_stream_set_read_callback; + pContext->pulse.pa_stream_flush = (mal_proc)_pa_stream_flush; + pContext->pulse.pa_stream_drain = (mal_proc)_pa_stream_drain; + pContext->pulse.pa_stream_cork = (mal_proc)_pa_stream_cork; + pContext->pulse.pa_stream_trigger = (mal_proc)_pa_stream_trigger; + pContext->pulse.pa_stream_begin_write = (mal_proc)_pa_stream_begin_write; + pContext->pulse.pa_stream_write = (mal_proc)_pa_stream_write; + pContext->pulse.pa_stream_peek = (mal_proc)_pa_stream_peek; + pContext->pulse.pa_stream_drop = (mal_proc)_pa_stream_drop; +#endif + + pContext->onUninit = mal_context_uninit__pulse; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__pulse; + pContext->onEnumDevices = mal_context_enumerate_devices__pulse; + pContext->onGetDeviceInfo = mal_context_get_device_info__pulse; + pContext->onDeviceInit = mal_device_init__pulse; + pContext->onDeviceUninit = mal_device_uninit__pulse; + pContext->onDeviceStart = mal_device__start_backend__pulse; + pContext->onDeviceStop = mal_device__stop_backend__pulse; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__pulse; + pContext->onDeviceMainLoop = mal_device__main_loop__pulse; + + + // Although we have found the libpulse library, it doesn't necessarily mean PulseAudio is useable. We need to initialize + // and connect a dummy PulseAudio context to test PulseAudio's usability. + mal_pa_mainloop* pMainLoop = ((mal_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); + if (pMainLoop == NULL) { + return MAL_NO_BACKEND; + } + + mal_pa_mainloop_api* pAPI = ((mal_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop); + if (pAPI == NULL) { + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return MAL_NO_BACKEND; + } + + mal_pa_context* pPulseContext = ((mal_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->config.pulse.pApplicationName); + if (pPulseContext == NULL) { + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return MAL_NO_BACKEND; + } + + int error = ((mal_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->config.pulse.pServerName, 0, NULL); + if (error != MAL_PA_OK) { + ((mal_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext); + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return MAL_NO_BACKEND; + } + + ((mal_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext); + ((mal_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext); + ((mal_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop); + return MAL_SUCCESS; +} +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// +// JACK Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_JACK + +// It is assumed jack.h is available when compile-time linking is being used. +#ifdef MAL_NO_RUNTIME_LINKING +#include + +typedef jack_nframes_t mal_jack_nframes_t; +typedef jack_options_t mal_jack_options_t; +typedef jack_status_t mal_jack_status_t; +typedef jack_client_t mal_jack_client_t; +typedef jack_port_t mal_jack_port_t; +typedef JackProcessCallback mal_JackProcessCallback; +typedef JackBufferSizeCallback mal_JackBufferSizeCallback; +typedef JackShutdownCallback mal_JackShutdownCallback; +#define MAL_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE +#define mal_JackNoStartServer JackNoStartServer +#define mal_JackPortIsInput JackPortIsInput +#define mal_JackPortIsOutput JackPortIsOutput +#define mal_JackPortIsPhysical JackPortIsPhysical +#else +typedef mal_uint32 mal_jack_nframes_t; +typedef int mal_jack_options_t; +typedef int mal_jack_status_t; +typedef struct mal_jack_client_t mal_jack_client_t; +typedef struct mal_jack_port_t mal_jack_port_t; +typedef int (* mal_JackProcessCallback) (mal_jack_nframes_t nframes, void* arg); +typedef int (* mal_JackBufferSizeCallback)(mal_jack_nframes_t nframes, void* arg); +typedef void (* mal_JackShutdownCallback) (void* arg); +#define MAL_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" +#define mal_JackNoStartServer 1 +#define mal_JackPortIsInput 1 +#define mal_JackPortIsOutput 2 +#define mal_JackPortIsPhysical 4 +#endif + +typedef mal_jack_client_t* (* mal_jack_client_open_proc) (const char* client_name, mal_jack_options_t options, mal_jack_status_t* status, ...); +typedef int (* mal_jack_client_close_proc) (mal_jack_client_t* client); +typedef int (* mal_jack_client_name_size_proc) (); +typedef int (* mal_jack_set_process_callback_proc) (mal_jack_client_t* client, mal_JackProcessCallback process_callback, void* arg); +typedef int (* mal_jack_set_buffer_size_callback_proc)(mal_jack_client_t* client, mal_JackBufferSizeCallback bufsize_callback, void* arg); +typedef void (* mal_jack_on_shutdown_proc) (mal_jack_client_t* client, mal_JackShutdownCallback function, void* arg); +typedef mal_jack_nframes_t (* mal_jack_get_sample_rate_proc) (mal_jack_client_t* client); +typedef mal_jack_nframes_t (* mal_jack_get_buffer_size_proc) (mal_jack_client_t* client); +typedef const char** (* mal_jack_get_ports_proc) (mal_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); +typedef int (* mal_jack_activate_proc) (mal_jack_client_t* client); +typedef int (* mal_jack_deactivate_proc) (mal_jack_client_t* client); +typedef int (* mal_jack_connect_proc) (mal_jack_client_t* client, const char* source_port, const char* destination_port); +typedef mal_jack_port_t* (* mal_jack_port_register_proc) (mal_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); +typedef const char* (* mal_jack_port_name_proc) (const mal_jack_port_t* port); +typedef void* (* mal_jack_port_get_buffer_proc) (mal_jack_port_t* port, mal_jack_nframes_t nframes); +typedef void (* mal_jack_free_proc) (void* ptr); + +mal_result mal_context_open_client__jack(mal_context* pContext, mal_jack_client_t** ppClient) +{ + mal_assert(pContext != NULL); + mal_assert(ppClient != NULL); + + if (ppClient) { + *ppClient = NULL; + } + + size_t maxClientNameSize = ((mal_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); // Includes null terminator. + + char clientName[256]; + mal_strncpy_s(clientName, mal_min(sizeof(clientName), maxClientNameSize), (pContext->config.jack.pClientName != NULL) ? pContext->config.jack.pClientName : "mini_al", (size_t)-1); + + mal_jack_status_t status; + mal_jack_client_t* pClient = ((mal_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->config.jack.tryStartServer) ? 0 : mal_JackNoStartServer, &status, NULL); + if (pClient == NULL) { + return MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + if (ppClient) { + *ppClient = pClient; + } + + return MAL_SUCCESS; +} + +mal_bool32 mal_context_is_device_id_equal__jack(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return pID0->jack == pID1->jack; +} + +mal_result mal_context_enumerate_devices__jack(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + mal_bool32 cbResult = MAL_TRUE; + + // Playback. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + + // Capture. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__jack(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + + (void)pContext; + (void)shareMode; + + if (pDeviceID != NULL && pDeviceID->jack != 0) { + return MAL_NO_DEVICE; // Don't know the device. + } + + // Name / Description + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + // Jack only supports f32 and has a specific channel count and sample rate. + pDeviceInfo->formatCount = 1; + pDeviceInfo->formats[0] = mal_format_f32; + + // The channel count and sample rate can only be determined by opening the device. + mal_jack_client_t* pClient; + mal_result result = mal_context_open_client__jack(pContext, &pClient); + if (result != MAL_SUCCESS) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + pDeviceInfo->minSampleRate = ((mal_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((mal_jack_client_t*)pClient); + pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate; + + pDeviceInfo->minChannels = 0; + pDeviceInfo->maxChannels = 0; + + const char** ppPorts = ((mal_jack_get_ports_proc)pContext->jack.jack_get_ports)((mal_jack_client_t*)pClient, NULL, NULL, mal_JackPortIsPhysical | ((deviceType == mal_device_type_playback) ? mal_JackPortIsInput : mal_JackPortIsOutput)); + if (ppPorts == NULL) { + ((mal_jack_client_close_proc)pContext->jack.jack_client_close)((mal_jack_client_t*)pClient); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + while (ppPorts[pDeviceInfo->minChannels] != NULL) { + pDeviceInfo->minChannels += 1; + pDeviceInfo->maxChannels += 1; + } + + ((mal_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); + ((mal_jack_client_close_proc)pContext->jack.jack_client_close)((mal_jack_client_t*)pClient); + + return MAL_SUCCESS; +} + + +void mal_device_uninit__jack(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + if (pDevice->jack.pClient != NULL) { + ((mal_jack_client_close_proc)pContext->jack.jack_client_close)((mal_jack_client_t*)pDevice->jack.pClient); + } +} + +void mal_device__jack_shutdown_callback(void* pUserData) +{ + // JACK died. Stop the device. + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_device_stop(pDevice); +} + +int mal_device__jack_buffer_size_callback(mal_jack_nframes_t frameCount, void* pUserData) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + float* pNewBuffer = (float*)mal_realloc(pDevice->jack.pIntermediaryBuffer, frameCount * (pDevice->internalChannels*mal_get_bytes_per_sample(pDevice->internalFormat))); + if (pNewBuffer == NULL) { + return MAL_OUT_OF_MEMORY; + } + + pDevice->jack.pIntermediaryBuffer = pNewBuffer; + pDevice->bufferSizeInFrames = frameCount * pDevice->periods; + + return 0; +} + +int mal_device__jack_process_callback(mal_jack_nframes_t frameCount, void* pUserData) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + if (pDevice->type == mal_device_type_playback) { + mal_device__read_frames_from_client(pDevice, frameCount, pDevice->jack.pIntermediaryBuffer); + + // Channels need to be deinterleaved. + for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) { + float* pDst = (float*)((mal_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((mal_jack_port_t*)pDevice->jack.pPorts[iChannel], frameCount); + if (pDst != NULL) { + const float* pSrc = pDevice->jack.pIntermediaryBuffer + iChannel; + for (mal_jack_nframes_t iFrame = 0; iFrame < frameCount; ++iFrame) { + *pDst = *pSrc; + + pDst += 1; + pSrc += pDevice->internalChannels; + } + } + } + } else { + // Channels need to be interleaved. + for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) { + const float* pSrc = (const float*)((mal_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((mal_jack_port_t*)pDevice->jack.pPorts[iChannel], frameCount); + if (pSrc != NULL) { + float* pDst = pDevice->jack.pIntermediaryBuffer + iChannel; + for (mal_jack_nframes_t iFrame = 0; iFrame < frameCount; ++iFrame) { + *pDst = *pSrc; + + pDst += pDevice->internalChannels; + pSrc += 1; + } + } + } + + mal_device__send_frames_to_client(pDevice, frameCount, pDevice->jack.pIntermediaryBuffer); + } + + return 0; +} + +mal_result mal_device_init__jack(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + mal_assert(pContext != NULL); + mal_assert(pConfig != NULL); + mal_assert(pDevice != NULL); + + (void)pContext; + (void)pConfig; + + // Only supporting default devices with JACK. + if (pDeviceID != NULL && pDeviceID->jack != 0) { + return MAL_NO_DEVICE; + } + + + // Open the client. + mal_result result = mal_context_open_client__jack(pContext, (mal_jack_client_t**)&pDevice->jack.pClient); + if (result != MAL_SUCCESS) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + // Callbacks. + if (((mal_jack_set_process_callback_proc)pContext->jack.jack_set_process_callback)((mal_jack_client_t*)pDevice->jack.pClient, mal_device__jack_process_callback, pDevice) != 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + if (((mal_jack_set_buffer_size_callback_proc)pContext->jack.jack_set_buffer_size_callback)((mal_jack_client_t*)pDevice->jack.pClient, mal_device__jack_buffer_size_callback, pDevice) != 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + ((mal_jack_on_shutdown_proc)pContext->jack.jack_on_shutdown)((mal_jack_client_t*)pDevice->jack.pClient, mal_device__jack_shutdown_callback, pDevice); + + + // The format is always f32. + pDevice->internalFormat = mal_format_f32; + + // A port is a channel. + unsigned long serverPortFlags; + unsigned long clientPortFlags; + if (type == mal_device_type_playback) { + serverPortFlags = mal_JackPortIsInput; + clientPortFlags = mal_JackPortIsOutput; + } else { + serverPortFlags = mal_JackPortIsOutput; + clientPortFlags = mal_JackPortIsInput; + } + + const char** ppPorts = ((mal_jack_get_ports_proc)pContext->jack.jack_get_ports)((mal_jack_client_t*)pDevice->jack.pClient, NULL, NULL, mal_JackPortIsPhysical | serverPortFlags); + if (ppPorts == NULL) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + pDevice->internalChannels = 0; + while (ppPorts[pDevice->internalChannels] != NULL) { + char name[64]; + if (type == mal_device_type_playback) { + mal_strcpy_s(name, sizeof(name), "playback"); + mal_itoa_s((int)pDevice->internalChannels, name+8, sizeof(name)-8, 10); // 8 = length of "playback" + } else { + mal_strcpy_s(name, sizeof(name), "capture"); + mal_itoa_s((int)pDevice->internalChannels, name+7, sizeof(name)-7, 10); // 7 = length of "capture" + } + + pDevice->jack.pPorts[pDevice->internalChannels] = ((mal_jack_port_register_proc)pContext->jack.jack_port_register)((mal_jack_client_t*)pDevice->jack.pClient, name, MAL_JACK_DEFAULT_AUDIO_TYPE, clientPortFlags, 0); + if (pDevice->jack.pPorts[pDevice->internalChannels] == NULL) { + ((mal_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); + mal_device_uninit__jack(pDevice); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + pDevice->internalChannels += 1; + } + + ((mal_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); + ppPorts = NULL; + + // We set the sample rate here, but apparently this can change. This is incompatible with mini_al, so changing sample rates will not be supported. + pDevice->internalSampleRate = ((mal_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((mal_jack_client_t*)pDevice->jack.pClient); + + // I don't think the channel map can be queried, so just use defaults for now. + mal_get_standard_channel_map(mal_standard_channel_map_alsa, pDevice->internalChannels, pDevice->internalChannelMap); + + // The buffer size in frames can change. + pDevice->periods = 2; + pDevice->bufferSizeInFrames = ((mal_jack_get_buffer_size_proc)pContext->jack.jack_get_buffer_size)((mal_jack_client_t*)pDevice->jack.pClient) * pDevice->periods; + + // Initial allocation for the intermediary buffer. + pDevice->jack.pIntermediaryBuffer = (float*)mal_malloc((pDevice->bufferSizeInFrames/pDevice->periods)*(pDevice->internalChannels*mal_get_bytes_per_sample(pDevice->internalFormat))); + if (pDevice->jack.pIntermediaryBuffer == NULL) { + mal_device_uninit__jack(pDevice); + return MAL_OUT_OF_MEMORY; + } + + return MAL_SUCCESS; +} + + +mal_result mal_device__start_backend__jack(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + int resultJACK = ((mal_jack_activate_proc)pContext->jack.jack_activate)((mal_jack_client_t*)pDevice->jack.pClient); + if (resultJACK != 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + + const char** ppServerPorts; + if (pDevice->type == mal_device_type_playback) { + ppServerPorts = ((mal_jack_get_ports_proc)pContext->jack.jack_get_ports)((mal_jack_client_t*)pDevice->jack.pClient, NULL, NULL, mal_JackPortIsPhysical | mal_JackPortIsInput); + } else { + ppServerPorts = ((mal_jack_get_ports_proc)pContext->jack.jack_get_ports)((mal_jack_client_t*)pDevice->jack.pClient, NULL, NULL, mal_JackPortIsPhysical | mal_JackPortIsOutput); + } + + if (ppServerPorts == NULL) { + ((mal_jack_deactivate_proc)pContext->jack.jack_deactivate)((mal_jack_client_t*)pDevice->jack.pClient); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MAL_ERROR); + } + + for (size_t i = 0; ppServerPorts[i] != NULL; ++i) { + const char* pServerPort = ppServerPorts[i]; + mal_assert(pServerPort != NULL); + + const char* pClientPort = ((mal_jack_port_name_proc)pContext->jack.jack_port_name)((mal_jack_port_t*)pDevice->jack.pPorts[i]); + mal_assert(pClientPort != NULL); + + if (pDevice->type == mal_device_type_playback) { + resultJACK = ((mal_jack_connect_proc)pContext->jack.jack_connect)((mal_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); + } else { + resultJACK = ((mal_jack_connect_proc)pContext->jack.jack_connect)((mal_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); + } + + if (resultJACK != 0) { + ((mal_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + ((mal_jack_deactivate_proc)pContext->jack.jack_deactivate)((mal_jack_client_t*)pDevice->jack.pClient); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MAL_ERROR); + } + } + + ((mal_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + + return MAL_SUCCESS; +} + +mal_result mal_device__stop_backend__jack(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_context* pContext = pDevice->pContext; + mal_assert(pContext != NULL); + + if (((mal_jack_deactivate_proc)pContext->jack.jack_deactivate)((mal_jack_client_t*)pDevice->jack.pClient) != 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MAL_ERROR); + } + + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + mal_stop_proc onStop = pDevice->onStop; + if (onStop) { + onStop(pDevice); + } + + return MAL_SUCCESS; +} + + +mal_result mal_context_uninit__jack(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_jack); + +#ifndef MAL_NO_RUNTIME_LINKING + mal_dlclose(pContext->jack.jackSO); +#endif + + return MAL_SUCCESS; +} + +mal_result mal_context_init__jack(mal_context* pContext) +{ + mal_assert(pContext != NULL); + +#ifndef MAL_NO_RUNTIME_LINKING + // libjack.so + const char* libjackNames[] = { +#ifdef MAL_WIN32 + "libjack.dll" +#else + "libjack.so", + "libjack.so.0" +#endif + }; + + for (size_t i = 0; i < mal_countof(libjackNames); ++i) { + pContext->jack.jackSO = mal_dlopen(libjackNames[i]); + if (pContext->jack.jackSO != NULL) { + break; + } + } + + if (pContext->jack.jackSO == NULL) { + return MAL_NO_BACKEND; + } + + pContext->jack.jack_client_open = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_client_open"); + pContext->jack.jack_client_close = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_client_close"); + pContext->jack.jack_client_name_size = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_client_name_size"); + pContext->jack.jack_set_process_callback = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_set_process_callback"); + pContext->jack.jack_set_buffer_size_callback = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_set_buffer_size_callback"); + pContext->jack.jack_on_shutdown = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_on_shutdown"); + pContext->jack.jack_get_sample_rate = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_get_sample_rate"); + pContext->jack.jack_get_buffer_size = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_get_buffer_size"); + pContext->jack.jack_get_ports = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_get_ports"); + pContext->jack.jack_activate = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_activate"); + pContext->jack.jack_deactivate = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_deactivate"); + pContext->jack.jack_connect = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_connect"); + pContext->jack.jack_port_register = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_port_register"); + pContext->jack.jack_port_name = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_port_name"); + pContext->jack.jack_port_get_buffer = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_port_get_buffer"); + pContext->jack.jack_free = (mal_proc)mal_dlsym(pContext->jack.jackSO, "jack_free"); +#else + // This strange assignment system is here just to ensure type safety of mini_al's function pointer + // types. If anything differs slightly the compiler should throw a warning. + mal_jack_client_open_proc _jack_client_open = jack_client_open; + mal_jack_client_close_proc _jack_client_close = jack_client_close; + mal_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; + mal_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; + mal_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; + mal_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; + mal_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; + mal_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; + mal_jack_get_ports_proc _jack_get_ports = jack_get_ports; + mal_jack_activate_proc _jack_activate = jack_activate; + mal_jack_deactivate_proc _jack_deactivate = jack_deactivate; + mal_jack_connect_proc _jack_connect = jack_connect; + mal_jack_port_register_proc _jack_port_register = jack_port_register; + mal_jack_port_name_proc _jack_port_name = jack_port_name; + mal_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; + mal_jack_free_proc _jack_free = jack_free; + + pContext->jack.jack_client_open = (mal_proc)_jack_client_open; + pContext->jack.jack_client_close = (mal_proc)_jack_client_close; + pContext->jack.jack_client_name_size = (mal_proc)_jack_client_name_size; + pContext->jack.jack_set_process_callback = (mal_proc)_jack_set_process_callback; + pContext->jack.jack_set_buffer_size_callback = (mal_proc)_jack_set_buffer_size_callback; + pContext->jack.jack_on_shutdown = (mal_proc)_jack_on_shutdown; + pContext->jack.jack_get_sample_rate = (mal_proc)_jack_get_sample_rate; + pContext->jack.jack_get_buffer_size = (mal_proc)_jack_get_buffer_size; + pContext->jack.jack_get_ports = (mal_proc)_jack_get_ports; + pContext->jack.jack_activate = (mal_proc)_jack_activate; + pContext->jack.jack_deactivate = (mal_proc)_jack_deactivate; + pContext->jack.jack_connect = (mal_proc)_jack_connect; + pContext->jack.jack_port_register = (mal_proc)_jack_port_register; + pContext->jack.jack_port_name = (mal_proc)_jack_port_name; + pContext->jack.jack_port_get_buffer = (mal_proc)_jack_port_get_buffer; + pContext->jack.jack_free = (mal_proc)_jack_free; +#endif + + pContext->isBackendAsynchronous = MAL_TRUE; + + pContext->onUninit = mal_context_uninit__jack; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__jack; + pContext->onEnumDevices = mal_context_enumerate_devices__jack; + pContext->onGetDeviceInfo = mal_context_get_device_info__jack; + pContext->onDeviceInit = mal_device_init__jack; + pContext->onDeviceUninit = mal_device_uninit__jack; + pContext->onDeviceStart = mal_device__start_backend__jack; + pContext->onDeviceStop = mal_device__stop_backend__jack; + + + // Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting + // a temporary client. + mal_jack_client_t* pDummyClient; + mal_result result = mal_context_open_client__jack(pContext, &pDummyClient); + if (result != MAL_SUCCESS) { + return MAL_NO_BACKEND; + } + + ((mal_jack_client_close_proc)pContext->jack.jack_client_close)((mal_jack_client_t*)pDummyClient); + return MAL_SUCCESS; +} +#endif // JACK + + + +/////////////////////////////////////////////////////////////////////////////// +// +// Core Audio Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_COREAUDIO +#include + +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 + #define MAL_APPLE_MOBILE +#else + #define MAL_APPLE_DESKTOP +#endif + +#if defined(MAL_APPLE_DESKTOP) +#include +#else +#include +#endif + +#include + +// CoreFoundation +typedef Boolean (* mal_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); + +// CoreAudio +#if defined(MAL_APPLE_DESKTOP) +typedef OSStatus (* mal_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); +typedef OSStatus (* mal_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); +typedef OSStatus (* mal_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); +typedef OSStatus (* mal_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); +#endif + +// AudioToolbox +typedef AudioComponent (* mal_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); +typedef OSStatus (* mal_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); +typedef OSStatus (* mal_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); +typedef OSStatus (* mal_AudioOutputUnitStart_proc)(AudioUnit inUnit); +typedef OSStatus (* mal_AudioOutputUnitStop_proc)(AudioUnit inUnit); +typedef OSStatus (* mal_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); +typedef OSStatus (* mal_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); +typedef OSStatus (* mal_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); +typedef OSStatus (* mal_AudioUnitInitialize_proc)(AudioUnit inUnit); +typedef OSStatus (* mal_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); + + +#define MAL_COREAUDIO_OUTPUT_BUS 0 +#define MAL_COREAUDIO_INPUT_BUS 1 + +mal_result mal_device_reinit_internal__coreaudio(mal_device* pDevice, mal_bool32 disposePreviousAudioUnit); + + +// Core Audio +// +// So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation +// apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose +// needing to figure out how this darn thing works, I'm going to outline a few things here. +// +// Since mini_al is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be +// able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen +// that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent +// and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the +// distinction between playback and capture in particular). Therefore, mini_al is using the AudioObject API. +// +// Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When +// retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific +// data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the +// devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be +// the central APIs for retrieving information about the system and specific devices. +// +// To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a +// structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" +// which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is +// typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and +// kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to +// kAudioObjectPropertyElementMaster in mini_al's case. I don't know of any cases where this would be set to anything different. +// +// Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size +// of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property +// address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the +// size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of +// AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. + +mal_result mal_result_from_OSStatus(OSStatus status) +{ + switch (status) + { + case noErr: return MAL_SUCCESS; + #if defined(MAL_APPLE_DESKTOP) + case kAudioHardwareNotRunningError: return MAL_DEVICE_NOT_STARTED; + case kAudioHardwareUnspecifiedError: return MAL_ERROR; + case kAudioHardwareUnknownPropertyError: return MAL_INVALID_ARGS; + case kAudioHardwareBadPropertySizeError: return MAL_INVALID_OPERATION; + case kAudioHardwareIllegalOperationError: return MAL_INVALID_OPERATION; + case kAudioHardwareBadObjectError: return MAL_INVALID_ARGS; + case kAudioHardwareBadDeviceError: return MAL_INVALID_ARGS; + case kAudioHardwareBadStreamError: return MAL_INVALID_ARGS; + case kAudioHardwareUnsupportedOperationError: return MAL_INVALID_OPERATION; + case kAudioDeviceUnsupportedFormatError: return MAL_FORMAT_NOT_SUPPORTED; + case kAudioDevicePermissionsError: return MAL_ACCESS_DENIED; + #endif + default: return MAL_ERROR; + } +} + +#if 0 +mal_channel mal_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) +{ + switch (bit) + { + case kAudioChannelBit_Left: return MAL_CHANNEL_LEFT; + case kAudioChannelBit_Right: return MAL_CHANNEL_RIGHT; + case kAudioChannelBit_Center: return MAL_CHANNEL_FRONT_CENTER; + case kAudioChannelBit_LFEScreen: return MAL_CHANNEL_LFE; + case kAudioChannelBit_LeftSurround: return MAL_CHANNEL_BACK_LEFT; + case kAudioChannelBit_RightSurround: return MAL_CHANNEL_BACK_RIGHT; + case kAudioChannelBit_LeftCenter: return MAL_CHANNEL_FRONT_LEFT_CENTER; + case kAudioChannelBit_RightCenter: return MAL_CHANNEL_FRONT_RIGHT_CENTER; + case kAudioChannelBit_CenterSurround: return MAL_CHANNEL_BACK_CENTER; + case kAudioChannelBit_LeftSurroundDirect: return MAL_CHANNEL_SIDE_LEFT; + case kAudioChannelBit_RightSurroundDirect: return MAL_CHANNEL_SIDE_RIGHT; + case kAudioChannelBit_TopCenterSurround: return MAL_CHANNEL_TOP_CENTER; + case kAudioChannelBit_VerticalHeightLeft: return MAL_CHANNEL_TOP_FRONT_LEFT; + case kAudioChannelBit_VerticalHeightCenter: return MAL_CHANNEL_TOP_FRONT_CENTER; + case kAudioChannelBit_VerticalHeightRight: return MAL_CHANNEL_TOP_FRONT_RIGHT; + case kAudioChannelBit_TopBackLeft: return MAL_CHANNEL_TOP_BACK_LEFT; + case kAudioChannelBit_TopBackCenter: return MAL_CHANNEL_TOP_BACK_CENTER; + case kAudioChannelBit_TopBackRight: return MAL_CHANNEL_TOP_BACK_RIGHT; + default: return MAL_CHANNEL_NONE; + } +} +#endif + +mal_channel mal_channel_from_AudioChannelLabel(AudioChannelLabel label) +{ + switch (label) + { + case kAudioChannelLabel_Unknown: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_Unused: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_UseCoordinates: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_Left: return MAL_CHANNEL_LEFT; + case kAudioChannelLabel_Right: return MAL_CHANNEL_RIGHT; + case kAudioChannelLabel_Center: return MAL_CHANNEL_FRONT_CENTER; + case kAudioChannelLabel_LFEScreen: return MAL_CHANNEL_LFE; + case kAudioChannelLabel_LeftSurround: return MAL_CHANNEL_BACK_LEFT; + case kAudioChannelLabel_RightSurround: return MAL_CHANNEL_BACK_RIGHT; + case kAudioChannelLabel_LeftCenter: return MAL_CHANNEL_FRONT_LEFT_CENTER; + case kAudioChannelLabel_RightCenter: return MAL_CHANNEL_FRONT_RIGHT_CENTER; + case kAudioChannelLabel_CenterSurround: return MAL_CHANNEL_BACK_CENTER; + case kAudioChannelLabel_LeftSurroundDirect: return MAL_CHANNEL_SIDE_LEFT; + case kAudioChannelLabel_RightSurroundDirect: return MAL_CHANNEL_SIDE_RIGHT; + case kAudioChannelLabel_TopCenterSurround: return MAL_CHANNEL_TOP_CENTER; + case kAudioChannelLabel_VerticalHeightLeft: return MAL_CHANNEL_TOP_FRONT_LEFT; + case kAudioChannelLabel_VerticalHeightCenter: return MAL_CHANNEL_TOP_FRONT_CENTER; + case kAudioChannelLabel_VerticalHeightRight: return MAL_CHANNEL_TOP_FRONT_RIGHT; + case kAudioChannelLabel_TopBackLeft: return MAL_CHANNEL_TOP_BACK_LEFT; + case kAudioChannelLabel_TopBackCenter: return MAL_CHANNEL_TOP_BACK_CENTER; + case kAudioChannelLabel_TopBackRight: return MAL_CHANNEL_TOP_BACK_RIGHT; + case kAudioChannelLabel_RearSurroundLeft: return MAL_CHANNEL_BACK_LEFT; + case kAudioChannelLabel_RearSurroundRight: return MAL_CHANNEL_BACK_RIGHT; + case kAudioChannelLabel_LeftWide: return MAL_CHANNEL_SIDE_LEFT; + case kAudioChannelLabel_RightWide: return MAL_CHANNEL_SIDE_RIGHT; + case kAudioChannelLabel_LFE2: return MAL_CHANNEL_LFE; + case kAudioChannelLabel_LeftTotal: return MAL_CHANNEL_LEFT; + case kAudioChannelLabel_RightTotal: return MAL_CHANNEL_RIGHT; + case kAudioChannelLabel_HearingImpaired: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_Narration: return MAL_CHANNEL_MONO; + case kAudioChannelLabel_Mono: return MAL_CHANNEL_MONO; + case kAudioChannelLabel_DialogCentricMix: return MAL_CHANNEL_MONO; + case kAudioChannelLabel_CenterSurroundDirect: return MAL_CHANNEL_BACK_CENTER; + case kAudioChannelLabel_Haptic: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_W: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_X: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_Y: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_Z: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_MS_Mid: return MAL_CHANNEL_LEFT; + case kAudioChannelLabel_MS_Side: return MAL_CHANNEL_RIGHT; + case kAudioChannelLabel_XY_X: return MAL_CHANNEL_LEFT; + case kAudioChannelLabel_XY_Y: return MAL_CHANNEL_RIGHT; + case kAudioChannelLabel_HeadphonesLeft: return MAL_CHANNEL_LEFT; + case kAudioChannelLabel_HeadphonesRight: return MAL_CHANNEL_RIGHT; + case kAudioChannelLabel_ClickTrack: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_ForeignLanguage: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_Discrete: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_Discrete_0: return MAL_CHANNEL_AUX_0; + case kAudioChannelLabel_Discrete_1: return MAL_CHANNEL_AUX_1; + case kAudioChannelLabel_Discrete_2: return MAL_CHANNEL_AUX_2; + case kAudioChannelLabel_Discrete_3: return MAL_CHANNEL_AUX_3; + case kAudioChannelLabel_Discrete_4: return MAL_CHANNEL_AUX_4; + case kAudioChannelLabel_Discrete_5: return MAL_CHANNEL_AUX_5; + case kAudioChannelLabel_Discrete_6: return MAL_CHANNEL_AUX_6; + case kAudioChannelLabel_Discrete_7: return MAL_CHANNEL_AUX_7; + case kAudioChannelLabel_Discrete_8: return MAL_CHANNEL_AUX_8; + case kAudioChannelLabel_Discrete_9: return MAL_CHANNEL_AUX_9; + case kAudioChannelLabel_Discrete_10: return MAL_CHANNEL_AUX_10; + case kAudioChannelLabel_Discrete_11: return MAL_CHANNEL_AUX_11; + case kAudioChannelLabel_Discrete_12: return MAL_CHANNEL_AUX_12; + case kAudioChannelLabel_Discrete_13: return MAL_CHANNEL_AUX_13; + case kAudioChannelLabel_Discrete_14: return MAL_CHANNEL_AUX_14; + case kAudioChannelLabel_Discrete_15: return MAL_CHANNEL_AUX_15; + case kAudioChannelLabel_Discrete_65535: return MAL_CHANNEL_NONE; + + #if 0 // Introduced in a later version of macOS. + case kAudioChannelLabel_HOA_ACN: return MAL_CHANNEL_NONE; + case kAudioChannelLabel_HOA_ACN_0: return MAL_CHANNEL_AUX_0; + case kAudioChannelLabel_HOA_ACN_1: return MAL_CHANNEL_AUX_1; + case kAudioChannelLabel_HOA_ACN_2: return MAL_CHANNEL_AUX_2; + case kAudioChannelLabel_HOA_ACN_3: return MAL_CHANNEL_AUX_3; + case kAudioChannelLabel_HOA_ACN_4: return MAL_CHANNEL_AUX_4; + case kAudioChannelLabel_HOA_ACN_5: return MAL_CHANNEL_AUX_5; + case kAudioChannelLabel_HOA_ACN_6: return MAL_CHANNEL_AUX_6; + case kAudioChannelLabel_HOA_ACN_7: return MAL_CHANNEL_AUX_7; + case kAudioChannelLabel_HOA_ACN_8: return MAL_CHANNEL_AUX_8; + case kAudioChannelLabel_HOA_ACN_9: return MAL_CHANNEL_AUX_9; + case kAudioChannelLabel_HOA_ACN_10: return MAL_CHANNEL_AUX_10; + case kAudioChannelLabel_HOA_ACN_11: return MAL_CHANNEL_AUX_11; + case kAudioChannelLabel_HOA_ACN_12: return MAL_CHANNEL_AUX_12; + case kAudioChannelLabel_HOA_ACN_13: return MAL_CHANNEL_AUX_13; + case kAudioChannelLabel_HOA_ACN_14: return MAL_CHANNEL_AUX_14; + case kAudioChannelLabel_HOA_ACN_15: return MAL_CHANNEL_AUX_15; + case kAudioChannelLabel_HOA_ACN_65024: return MAL_CHANNEL_NONE; + #endif + + default: return MAL_CHANNEL_NONE; + } +} + +mal_result mal_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, mal_format* pFormatOut) +{ + mal_assert(pDescription != NULL); + mal_assert(pFormatOut != NULL); + + *pFormatOut = mal_format_unknown; // Safety. + + // There's a few things mini_al doesn't support. + if (pDescription->mFormatID != kAudioFormatLinearPCM) { + return MAL_FORMAT_NOT_SUPPORTED; + } + + // We don't support any non-packed formats that are aligned high. + if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { + return MAL_FORMAT_NOT_SUPPORTED; + } + + // Only supporting native-endian. + if ((mal_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (mal_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { + return MAL_FORMAT_NOT_SUPPORTED; + } + + // We are not currently supporting non-interleaved formats (this will be added in a future version of mini_al). + //if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { + // return MAL_FORMAT_NOT_SUPPORTED; + //} + + if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { + if (pDescription->mBitsPerChannel == 32) { + *pFormatOut = mal_format_f32; + return MAL_SUCCESS; + } + } else { + if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { + if (pDescription->mBitsPerChannel == 16) { + *pFormatOut = mal_format_s16; + return MAL_SUCCESS; + } else if (pDescription->mBitsPerChannel == 24) { + if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { + *pFormatOut = mal_format_s24; + return MAL_SUCCESS; + } else { + if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(mal_int32)) { + // TODO: Implement mal_format_s24_32. + //*pFormatOut = mal_format_s24_32; + //return MAL_SUCCESS; + return MAL_FORMAT_NOT_SUPPORTED; + } + } + } else if (pDescription->mBitsPerChannel == 32) { + *pFormatOut = mal_format_s32; + return MAL_SUCCESS; + } + } else { + if (pDescription->mBitsPerChannel == 8) { + *pFormatOut = mal_format_u8; + return MAL_SUCCESS; + } + } + } + + // Getting here means the format is not supported. + return MAL_FORMAT_NOT_SUPPORTED; +} + +#if defined(MAL_APPLE_DESKTOP) +mal_result mal_get_device_object_ids__coreaudio(mal_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) // NOTE: Free the returned buffer with mal_free(). +{ + mal_assert(pContext != NULL); + mal_assert(pDeviceCount != NULL); + mal_assert(ppDeviceObjectIDs != NULL); + (void)pContext; + + // Safety. + *pDeviceCount = 0; + *ppDeviceObjectIDs = NULL; + + AudioObjectPropertyAddress propAddressDevices; + propAddressDevices.mSelector = kAudioHardwarePropertyDevices; + propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; + propAddressDevices.mElement = kAudioObjectPropertyElementMaster; + + UInt32 deviceObjectsDataSize; + OSStatus status = ((mal_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + AudioObjectID* pDeviceObjectIDs = (AudioObjectID*)mal_malloc(deviceObjectsDataSize); + if (pDeviceObjectIDs == NULL) { + return MAL_OUT_OF_MEMORY; + } + + status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); + if (status != noErr) { + mal_free(pDeviceObjectIDs); + return mal_result_from_OSStatus(status); + } + + *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); + *ppDeviceObjectIDs = pDeviceObjectIDs; + return MAL_SUCCESS; +} + +mal_result mal_get_AudioObject_uid_as_CFStringRef(mal_context* pContext, AudioObjectID objectID, CFStringRef* pUID) +{ + mal_assert(pContext != NULL); + + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = kAudioDevicePropertyDeviceUID; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = kAudioObjectPropertyElementMaster; + + UInt32 dataSize = sizeof(*pUID); + OSStatus status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + return MAL_SUCCESS; +} + +mal_result mal_get_AudioObject_uid(mal_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) +{ + mal_assert(pContext != NULL); + + CFStringRef uid; + mal_result result = mal_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); + if (result != MAL_SUCCESS) { + return result; + } + + if (!((mal_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { + return MAL_ERROR; + } + + return MAL_SUCCESS; +} + +mal_result mal_get_AudioObject_name(mal_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) +{ + mal_assert(pContext != NULL); + + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = kAudioObjectPropertyElementMaster; + + CFStringRef deviceName = NULL; + UInt32 dataSize = sizeof(deviceName); + OSStatus status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + if (!((mal_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { + return MAL_ERROR; + } + + return MAL_SUCCESS; +} + +mal_bool32 mal_does_AudioObject_support_scope(mal_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) +{ + mal_assert(pContext != NULL); + + // To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a + // playback device. + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; + propAddress.mScope = scope; + propAddress.mElement = kAudioObjectPropertyElementMaster; + + UInt32 dataSize; + OSStatus status = ((mal_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return MAL_FALSE; + } + + AudioBufferList* pBufferList = (AudioBufferList*)mal_malloc(dataSize); + if (pBufferList == NULL) { + return MAL_FALSE; // Out of memory. + } + + status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); + if (status != noErr) { + mal_free(pBufferList); + return MAL_FALSE; + } + + mal_bool32 isSupported = MAL_FALSE; + if (pBufferList->mNumberBuffers > 0) { + isSupported = MAL_TRUE; + } + + mal_free(pBufferList); + return isSupported; +} + +mal_bool32 mal_does_AudioObject_support_playback(mal_context* pContext, AudioObjectID deviceObjectID) +{ + return mal_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); +} + +mal_bool32 mal_does_AudioObject_support_capture(mal_context* pContext, AudioObjectID deviceObjectID) +{ + return mal_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); +} + + +mal_result mal_get_AudioObject_stream_descriptions(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) // NOTE: Free the returned pointer with mal_free(). +{ + mal_assert(pContext != NULL); + mal_assert(pDescriptionCount != NULL); + mal_assert(ppDescriptions != NULL); + + // TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My + // MacBook Pro uses s24/32 format, however, which mini_al does not currently support. + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; //kAudioStreamPropertyAvailablePhysicalFormats; + propAddress.mScope = (deviceType == mal_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = kAudioObjectPropertyElementMaster; + + UInt32 dataSize; + OSStatus status = ((mal_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + AudioStreamRangedDescription* pDescriptions = (AudioStreamRangedDescription*)mal_malloc(dataSize); + if (pDescriptions == NULL) { + return MAL_OUT_OF_MEMORY; + } + + status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); + if (status != noErr) { + mal_free(pDescriptions); + return mal_result_from_OSStatus(status); + } + + *pDescriptionCount = dataSize / sizeof(*pDescriptions); + *ppDescriptions = pDescriptions; + return MAL_SUCCESS; +} + + + +mal_result mal_get_AudioObject_channel_layout(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, AudioChannelLayout** ppChannelLayout) // NOTE: Free the returned pointer with mal_free(). +{ + mal_assert(pContext != NULL); + mal_assert(ppChannelLayout != NULL); + + *ppChannelLayout = NULL; // Safety. + + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; + propAddress.mScope = (deviceType == mal_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = kAudioObjectPropertyElementMaster; + + UInt32 dataSize; + OSStatus status = ((mal_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + AudioChannelLayout* pChannelLayout = (AudioChannelLayout*)mal_malloc(dataSize); + if (pChannelLayout == NULL) { + return MAL_OUT_OF_MEMORY; + } + + status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); + if (status != noErr) { + mal_free(pChannelLayout); + return mal_result_from_OSStatus(status); + } + + *ppChannelLayout = pChannelLayout; + return MAL_SUCCESS; +} + +mal_result mal_get_AudioObject_channel_count(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_uint32* pChannelCount) +{ + mal_assert(pContext != NULL); + mal_assert(pChannelCount != NULL); + + *pChannelCount = 0; // Safety. + + AudioChannelLayout* pChannelLayout; + mal_result result = mal_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); + if (result != MAL_SUCCESS) { + return result; + } + + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { + *pChannelCount = pChannelLayout->mNumberChannelDescriptions; + } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { + *pChannelCount = mal_count_set_bits(pChannelLayout->mChannelBitmap); + } else { + *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); + } + + mal_free(pChannelLayout); + return MAL_SUCCESS; +} + +mal_result mal_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + mal_assert(pChannelLayout != NULL); + + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { + for (UInt32 iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) { + channelMap[iChannel] = mal_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); + } + } else +#if 0 + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { + // This is the same kind of system that's used by Windows audio APIs. + UInt32 iChannel = 0; + AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; + for (UInt32 iBit = 0; iBit < 32; ++iBit) { + AudioChannelBitmap bit = bitmap & (1 << iBit); + if (bit != 0) { + channelMap[iChannel++] = mal_channel_from_AudioChannelBit(bit); + } + } + } else +#endif + { + // Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should + // be updated to determine the mapping based on the tag. + UInt32 channelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); + switch (pChannelLayout->mChannelLayoutTag) + { + case kAudioChannelLayoutTag_Mono: + case kAudioChannelLayoutTag_Stereo: + case kAudioChannelLayoutTag_StereoHeadphones: + case kAudioChannelLayoutTag_MatrixStereo: + case kAudioChannelLayoutTag_MidSide: + case kAudioChannelLayoutTag_XY: + case kAudioChannelLayoutTag_Binaural: + case kAudioChannelLayoutTag_Ambisonic_B_Format: + { + mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap); + } break; + + case kAudioChannelLayoutTag_Octagonal: + { + channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + channelMap[6] = MAL_CHANNEL_SIDE_LEFT; + } // Intentional fallthrough. + case kAudioChannelLayoutTag_Hexagonal: + { + channelMap[5] = MAL_CHANNEL_BACK_CENTER; + } // Intentional fallthrough. + case kAudioChannelLayoutTag_Pentagonal: + { + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + } // Intentional fallghrough. + case kAudioChannelLayoutTag_Quadraphonic: + { + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[1] = MAL_CHANNEL_RIGHT; + channelMap[0] = MAL_CHANNEL_LEFT; + } break; + + // TODO: Add support for more tags here. + + default: + { + mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap); + } break; + } + } + + return MAL_SUCCESS; +} + +mal_result mal_get_AudioObject_channel_map(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + mal_assert(pContext != NULL); + + AudioChannelLayout* pChannelLayout; + mal_result result = mal_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); + if (result != MAL_SUCCESS) { + return result; // Rather than always failing here, would it be more robust to simply assume a default? + } + + result = mal_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap); + if (result != MAL_SUCCESS) { + return result; + } + + return result; +} + +mal_result mal_get_AudioObject_sample_rates(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) // NOTE: Free the returned pointer with mal_free(). +{ + mal_assert(pContext != NULL); + mal_assert(pSampleRateRangesCount != NULL); + mal_assert(ppSampleRateRanges != NULL); + + // Safety. + *pSampleRateRangesCount = 0; + *ppSampleRateRanges = NULL; + + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + propAddress.mScope = (deviceType == mal_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = kAudioObjectPropertyElementMaster; + + UInt32 dataSize; + OSStatus status = ((mal_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + AudioValueRange* pSampleRateRanges = (AudioValueRange*)mal_malloc(dataSize); + if (pSampleRateRanges == NULL) { + return MAL_OUT_OF_MEMORY; + } + + status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); + if (status != noErr) { + mal_free(pSampleRateRanges); + return mal_result_from_OSStatus(status); + } + + *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); + *ppSampleRateRanges = pSampleRateRanges; + return MAL_SUCCESS; +} + +mal_result mal_get_AudioObject_get_closest_sample_rate(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_uint32 sampleRateIn, mal_uint32* pSampleRateOut) +{ + mal_assert(pContext != NULL); + mal_assert(pSampleRateOut != NULL); + + *pSampleRateOut = 0; // Safety. + + UInt32 sampleRateRangeCount; + AudioValueRange* pSampleRateRanges; + mal_result result = mal_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); + if (result != MAL_SUCCESS) { + return result; + } + + if (sampleRateRangeCount == 0) { + mal_free(pSampleRateRanges); + return MAL_ERROR; // Should never hit this case should we? + } + + if (sampleRateIn == 0) { + // Search in order of mini_al's preferred priority. + for (UInt32 iMALSampleRate = 0; iMALSampleRate < mal_countof(g_malStandardSampleRatePriorities); ++iMALSampleRate) { + mal_uint32 malSampleRate = g_malStandardSampleRatePriorities[iMALSampleRate]; + for (UInt32 iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { + AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; + if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { + *pSampleRateOut = malSampleRate; + mal_free(pSampleRateRanges); + return MAL_SUCCESS; + } + } + } + + // If we get here it means none of mini_al's standard sample rates matched any of the supported sample rates from the device. In this + // case we just fall back to the first one reported by Core Audio. + mal_assert(sampleRateRangeCount > 0); + + *pSampleRateOut = pSampleRateRanges[0].mMinimum; + mal_free(pSampleRateRanges); + return MAL_SUCCESS; + } else { + // Find the closest match to this sample rate. + UInt32 currentAbsoluteDifference = INT32_MAX; + UInt32 iCurrentClosestRange = (UInt32)-1; + for (UInt32 iRange = 0; iRange < sampleRateRangeCount; ++iRange) { + if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { + *pSampleRateOut = sampleRateIn; + mal_free(pSampleRateRanges); + return MAL_SUCCESS; + } else { + UInt32 absoluteDifference; + if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { + absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; + } else { + absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; + } + + if (currentAbsoluteDifference > absoluteDifference) { + currentAbsoluteDifference = absoluteDifference; + iCurrentClosestRange = iRange; + } + } + } + + mal_assert(iCurrentClosestRange != (UInt32)-1); + + *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; + mal_free(pSampleRateRanges); + return MAL_SUCCESS; + } + + // Should never get here, but it would mean we weren't able to find any suitable sample rates. + //mal_free(pSampleRateRanges); + //return MAL_ERROR; +} + + +mal_result mal_get_AudioObject_closest_buffer_size_in_frames(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_uint32 bufferSizeInFramesIn, mal_uint32* pBufferSizeInFramesOut) +{ + mal_assert(pContext != NULL); + mal_assert(pBufferSizeInFramesOut != NULL); + + *pBufferSizeInFramesOut = 0; // Safety. + + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + propAddress.mScope = (deviceType == mal_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = kAudioObjectPropertyElementMaster; + + AudioValueRange bufferSizeRange; + UInt32 dataSize = sizeof(bufferSizeRange); + OSStatus status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + // This is just a clamp. + if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { + *pBufferSizeInFramesOut = (mal_uint32)bufferSizeRange.mMinimum; + } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { + *pBufferSizeInFramesOut = (mal_uint32)bufferSizeRange.mMaximum; + } else { + *pBufferSizeInFramesOut = bufferSizeInFramesIn; + } + + return MAL_SUCCESS; +} + +mal_result mal_set_AudioObject_buffer_size_in_frames(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_uint32* pBufferSizeInOut) +{ + mal_assert(pContext != NULL); + + mal_uint32 chosenBufferSizeInFrames; + mal_result result = mal_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pBufferSizeInOut, &chosenBufferSizeInFrames); + if (result != MAL_SUCCESS) { + return result; + } + + // Try setting the size of the buffer... If this fails we just use whatever is currently set. + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; + propAddress.mScope = (deviceType == mal_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = kAudioObjectPropertyElementMaster; + + ((mal_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); + + // Get the actual size of the buffer. + UInt32 dataSize = sizeof(*pBufferSizeInOut); + OSStatus status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + *pBufferSizeInOut = chosenBufferSizeInFrames; + return MAL_SUCCESS; +} + + +mal_result mal_find_AudioObjectID(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) +{ + mal_assert(pContext != NULL); + mal_assert(pDeviceObjectID != NULL); + + // Safety. + *pDeviceObjectID = 0; + + if (pDeviceID == NULL) { + // Default device. + AudioObjectPropertyAddress propAddressDefaultDevice; + propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; + propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster; + if (type == mal_device_type_playback) { + propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + } else { + propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; + } + + UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); + AudioObjectID defaultDeviceObjectID; + OSStatus status = ((mal_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); + if (status == noErr) { + *pDeviceObjectID = defaultDeviceObjectID; + return MAL_SUCCESS; + } + } else { + // Explicit device. + UInt32 deviceCount; + AudioObjectID* pDeviceObjectIDs; + mal_result result = mal_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); + if (result != MAL_SUCCESS) { + return result; + } + + for (UInt32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; + + char uid[256]; + if (mal_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MAL_SUCCESS) { + continue; + } + + if (type == mal_device_type_playback) { + if (mal_does_AudioObject_support_playback(pContext, deviceObjectID)) { + if (strcmp(uid, pDeviceID->coreaudio) == 0) { + *pDeviceObjectID = deviceObjectID; + return MAL_SUCCESS; + } + } + } else { + if (mal_does_AudioObject_support_capture(pContext, deviceObjectID)) { + if (strcmp(uid, pDeviceID->coreaudio) == 0) { + *pDeviceObjectID = deviceObjectID; + return MAL_SUCCESS; + } + } + } + } + } + + // If we get here it means we couldn't find the device. + return MAL_NO_DEVICE; +} + + +mal_result mal_find_best_format__coreaudio(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_bool32 usingDefaultFormat, mal_bool32 usingDefaultChannels, mal_bool32 usingDefaultSampleRate, AudioStreamBasicDescription* pFormat) +{ + UInt32 deviceFormatDescriptionCount; + AudioStreamRangedDescription* pDeviceFormatDescriptions; + mal_result result = mal_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); + if (result != MAL_SUCCESS) { + return result; + } + + mal_uint32 desiredSampleRate = sampleRate; + if (usingDefaultSampleRate) { + // When using the device's default sample rate, we get the highest priority standard rate supported by the device. Otherwise + // we just use the pre-set rate. + for (mal_uint32 iStandardRate = 0; iStandardRate < mal_countof(g_malStandardSampleRatePriorities); ++iStandardRate) { + mal_uint32 standardRate = g_malStandardSampleRatePriorities[iStandardRate]; + + mal_bool32 foundRate = MAL_FALSE; + for (UInt32 iDeviceRate = 0; iDeviceRate < deviceFormatDescriptionCount; ++iDeviceRate) { + mal_uint32 deviceRate = (mal_uint32)pDeviceFormatDescriptions[iDeviceRate].mFormat.mSampleRate; + + if (deviceRate == standardRate) { + desiredSampleRate = standardRate; + foundRate = MAL_TRUE; + break; + } + } + + if (foundRate) { + break; + } + } + } + + mal_uint32 desiredChannelCount = channels; + if (usingDefaultChannels) { + mal_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &desiredChannelCount); // <-- Not critical if this fails. + } + + mal_format desiredFormat = format; + if (usingDefaultFormat) { + desiredFormat = g_malFormatPriorities[0]; + } + + // If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next + // loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. + AudioStreamBasicDescription bestDeviceFormatSoFar; + mal_zero_object(&bestDeviceFormatSoFar); + + mal_bool32 hasSupportedFormat = MAL_FALSE; + for (UInt32 iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { + mal_format format; + mal_result formatResult = mal_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format); + if (formatResult == MAL_SUCCESS && format != mal_format_unknown) { + hasSupportedFormat = MAL_TRUE; + bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; + break; + } + } + + if (!hasSupportedFormat) { + return MAL_FORMAT_NOT_SUPPORTED; + } + + + for (UInt32 iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { + AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; + + // If the format is not supported by mini_al we need to skip this one entirely. + mal_format thisSampleFormat; + mal_result formatResult = mal_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); + if (formatResult != MAL_SUCCESS || thisSampleFormat == mal_format_unknown) { + continue; // The format is not supported by mini_al. Skip. + } + + mal_format bestSampleFormatSoFar; + mal_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); + + + // Getting here means the format is supported by mini_al which makes this format a candidate. + if (thisDeviceFormat.mSampleRate != desiredSampleRate) { + // The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format + // so far has an equal sample rate we can just ignore this one. + if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { + continue; // The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. + } else { + // In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. + if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { + // This format has a different sample rate _and_ a different channel count. + if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { + continue; // No change to the best format. + } else { + // Both this format and the best so far have different sample rates and different channel counts. Whichever has the + // best format is the new best. + if (mal_get_format_priority_index(thisSampleFormat) < mal_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; // No change to the best format. + } + } + } else { + // This format has a different sample rate but the desired channel count. + if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { + // Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. + if (mal_get_format_priority_index(thisSampleFormat) < mal_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; // No change to the best format for now. + } + } else { + // This format has the desired channel count, but the best so far does not. We have a new best. + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } + } + } + } else { + // The sample rates match which makes this format a very high priority contender. If the best format so far has a different + // sample rate it needs to be replaced with this one. + if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + // In this case both this format and the best format so far have the same sample rate. Check the channel count next. + if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { + // In this case this format has the same channel count as what the client is requesting. If the best format so far has + // a different count, this one becomes the new best. + if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + // In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. + if (thisSampleFormat == desiredFormat) { + bestDeviceFormatSoFar = thisDeviceFormat; + break; // Found the exact match. + } else { + // The formats are different. The new best format is the one with the highest priority format according to mini_al. + if (mal_get_format_priority_index(thisSampleFormat) < mal_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; // No change to the best format for now. + } + } + } + } else { + // In this case the channel count is different to what the client has requested. If the best so far has the same channel + // count as the requested count then it remains the best. + if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { + continue; + } else { + // This is the case where both have the same sample rate (good) but different channel counts. Right now both have about + // the same priority, but we need to compare the format now. + if (thisSampleFormat == bestSampleFormatSoFar) { + if (mal_get_format_priority_index(thisSampleFormat) < mal_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; // No change to the best format for now. + } + } + } + } + } + } + } + + *pFormat = bestDeviceFormatSoFar; + return MAL_SUCCESS; +} +#endif + + + +mal_bool32 mal_context_is_device_id_equal__coreaudio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return strcmp(pID0->coreaudio, pID1->coreaudio) == 0; +} + +mal_result mal_context_enumerate_devices__coreaudio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + +#if defined(MAL_APPLE_DESKTOP) + UInt32 deviceCount; + AudioObjectID* pDeviceObjectIDs; + mal_result result = mal_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); + if (result != MAL_SUCCESS) { + return result; + } + + for (UInt32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; + + mal_device_info info; + mal_zero_object(&info); + if (mal_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MAL_SUCCESS) { + continue; + } + if (mal_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MAL_SUCCESS) { + continue; + } + + if (mal_does_AudioObject_support_playback(pContext, deviceObjectID)) { + if (!callback(pContext, mal_device_type_playback, &info, pUserData)) { + break; + } + } + if (mal_does_AudioObject_support_capture(pContext, deviceObjectID)) { + if (!callback(pContext, mal_device_type_capture, &info, pUserData)) { + break; + } + } + } + + mal_free(pDeviceObjectIDs); +#else + // Only supporting default devices on non-Desktop platforms. + mal_device_info info; + + mal_zero_object(&info); + mal_strncpy_s(info.name, sizeof(info.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + if (!callback(pContext, mal_device_type_playback, &info, pUserData)) { + return MAL_SUCCESS; + } + + mal_zero_object(&info); + mal_strncpy_s(info.name, sizeof(info.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + if (!callback(pContext, mal_device_type_capture, &info, pUserData)) { + return MAL_SUCCESS; + } +#endif + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__coreaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + (void)pDeviceInfo; + +#if defined(MAL_APPLE_DESKTOP) + // Desktop + // ======= + AudioObjectID deviceObjectID; + mal_result result = mal_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); + if (result != MAL_SUCCESS) { + return result; + } + + result = mal_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); + if (result != MAL_SUCCESS) { + return result; + } + + result = mal_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); + if (result != MAL_SUCCESS) { + return result; + } + + // Formats. + UInt32 streamDescriptionCount; + AudioStreamRangedDescription* pStreamDescriptions; + result = mal_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); + if (result != MAL_SUCCESS) { + return result; + } + + for (UInt32 iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { + mal_format format; + result = mal_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); + if (result != MAL_SUCCESS) { + continue; + } + + mal_assert(format != mal_format_unknown); + + // Make sure the format isn't already in the output list. + mal_bool32 exists = MAL_FALSE; + for (mal_uint32 iOutputFormat = 0; iOutputFormat < pDeviceInfo->formatCount; ++iOutputFormat) { + if (pDeviceInfo->formats[iOutputFormat] == format) { + exists = MAL_TRUE; + break; + } + } + + if (!exists) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = format; + } + } + + mal_free(pStreamDescriptions); + + + // Channels. + result = mal_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &pDeviceInfo->minChannels); + if (result != MAL_SUCCESS) { + return result; + } + pDeviceInfo->maxChannels = pDeviceInfo->minChannels; + + + // Sample rates. + UInt32 sampleRateRangeCount; + AudioValueRange* pSampleRateRanges; + result = mal_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); + if (result != MAL_SUCCESS) { + return result; + } + + if (sampleRateRangeCount > 0) { + pDeviceInfo->minSampleRate = UINT32_MAX; + pDeviceInfo->maxSampleRate = 0; + for (UInt32 iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { + if (pDeviceInfo->minSampleRate > pSampleRateRanges[iSampleRate].mMinimum) { + pDeviceInfo->minSampleRate = pSampleRateRanges[iSampleRate].mMinimum; + } + if (pDeviceInfo->maxSampleRate < pSampleRateRanges[iSampleRate].mMaximum) { + pDeviceInfo->maxSampleRate = pSampleRateRanges[iSampleRate].mMaximum; + } + } + } +#else + // Mobile + // ====== + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + // Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is + // reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to + // retrieve from the AVAudioSession shared instance. + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + AudioComponent component = ((mal_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); + if (component == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + AudioUnit audioUnit; + OSStatus status = ((mal_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + AudioUnitScope formatScope = (deviceType == mal_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; + AudioUnitElement formatElement = (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS; + + AudioStreamBasicDescription bestFormat; + UInt32 propSize = sizeof(bestFormat); + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + return mal_result_from_OSStatus(status); + } + + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + audioUnit = NULL; + + + pDeviceInfo->minChannels = bestFormat.mChannelsPerFrame; + pDeviceInfo->maxChannels = bestFormat.mChannelsPerFrame; + + pDeviceInfo->formatCount = 1; + mal_result result = mal_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->formats[0]); + if (result != MAL_SUCCESS) { + return result; + } + + // It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do + // this we just get the shared instance and inspect. + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + mal_assert(pAudioSession != NULL); + + pDeviceInfo->minSampleRate = (mal_uint32)pAudioSession.sampleRate; + pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate; + } +#endif + + return MAL_SUCCESS; +} + + +void mal_device_uninit__coreaudio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + mal_assert(mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED); + + ((mal_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + + if (pDevice->coreaudio.pAudioBufferList) { + mal_free(pDevice->coreaudio.pAudioBufferList); + } +} + + +OSStatus mal_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) +{ + (void)pActionFlags; + (void)pTimeStamp; + (void)busNumber; + + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + +#if defined(MAL_DEBUG_OUTPUT) + printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers); +#endif + + // For now we can assume everything is interleaved. + for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { + if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) { + mal_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + if (frameCountForThisBuffer > 0) { + mal_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData); + } + + #if defined(MAL_DEBUG_OUTPUT) + printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize); + #endif + } else { + // This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's + // not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. We just + // output silence here. + mal_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); + + #if defined(MAL_DEBUG_OUTPUT) + printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize); + #endif + } + } + + return noErr; +} + +OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) +{ + (void)pActionFlags; + (void)pTimeStamp; + (void)busNumber; + (void)frameCount; + (void)pUnusedBufferList; + + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + AudioBufferList* pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; + mal_assert(pRenderedBufferList); + +#if defined(MAL_DEBUG_OUTPUT) + printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers); +#endif + + OSStatus status = ((mal_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnit, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); + if (status != noErr) { + #if defined(MAL_DEBUG_OUTPUT) + printf(" ERROR: AudioUnitRender() failed with %d\n", status); + #endif + return status; + } + + // For now we can assume everything is interleaved. + for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { + if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) { + mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData); + #if defined(MAL_DEBUG_OUTPUT) + printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize); + #endif + } else { + // This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's + // not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. + } + } + + return noErr; +} + +void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) +{ + (void)propertyID; + + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + UInt32 isRunning; + UInt32 isRunningSize = sizeof(isRunning); + OSStatus status = ((mal_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); + if (status != noErr) { + return; // Don't really know what to do in this case... just ignore it, I suppose... + } + + if (!isRunning) { + // The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: + // + // 1) When the device is unplugged, this will be called _before_ the default device change notification. + // 2) When the device is changed via the default device change notification, this will be called _after_ the switch. + // + // For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. + if (pDevice->isDefaultDevice && mal_device__get_state(pDevice) != MAL_STATE_STOPPING && mal_device__get_state(pDevice) != MAL_STATE_STOPPED) { + // It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device + // via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the + // device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it + // hasn't!). + if (pDevice->coreaudio.isSwitchingDevice) { + return; + } + + // Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio + // will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most + // likely be successful in switching to the new device. + // + // TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted. + return; + } + + // Getting here means we need to stop the device. + mal_stop_proc onStop = pDevice->onStop; + if (onStop) { + onStop(pDevice); + } + } +} + +#if defined(MAL_APPLE_DESKTOP) +OSStatus mal_default_output_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) +{ + (void)objectID; + + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + if (pDevice->isDefaultDevice) { + // Not sure if I really need to check this, but it makes me feel better. + if (addressCount == 0) { + return noErr; + } + + if ((pDevice->type == mal_device_type_playback && pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) || + (pDevice->type == mal_device_type_capture && pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice)) { +#ifdef MAL_DEBUG_OUTPUT + printf("Device Changed: addressCount=%d, pAddresses[0].mElement=%d\n", addressCount, pAddresses[0].mElement); +#endif + pDevice->coreaudio.isSwitchingDevice = MAL_TRUE; + mal_result reinitResult = mal_device_reinit_internal__coreaudio(pDevice, MAL_TRUE); + pDevice->coreaudio.isSwitchingDevice = MAL_FALSE; + + if (reinitResult == MAL_SUCCESS) { + mal_device__post_init_setup(pDevice); + + // Make sure we resume the device if applicable. + if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { + mal_result startResult = pDevice->pContext->onDeviceStart(pDevice); + if (startResult != MAL_SUCCESS) { + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + } + } + } + } + } + + return noErr; +} +#endif + +typedef struct +{ + // Input. + mal_format formatIn; + mal_uint32 channelsIn; + mal_uint32 sampleRateIn; + mal_channel channelMapIn[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFramesIn; + mal_uint32 bufferSizeInMillisecondsIn; + mal_uint32 periodsIn; + mal_bool32 usingDefaultFormat; + mal_bool32 usingDefaultChannels; + mal_bool32 usingDefaultSampleRate; + mal_bool32 usingDefaultChannelMap; + mal_share_mode shareMode; + + // Output. +#if defined(MAL_APPLE_DESKTOP) + AudioObjectID deviceObjectID; +#endif + AudioComponent component; + AudioUnit audioUnit; + AudioBufferList* pAudioBufferList; // Only used for input devices. + mal_format formatOut; + mal_uint32 channelsOut; + mal_uint32 sampleRateOut; + mal_channel channelMapOut[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFramesOut; + mal_uint32 periodsOut; + mal_bool32 exclusiveMode; + char deviceName[256]; +} mal_device_init_internal_data__coreaudio; + +mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ +{ + mal_assert(pContext != NULL); + mal_assert(deviceType == mal_device_type_playback || deviceType == mal_device_type_capture); + +#if defined(MAL_APPLE_DESKTOP) + pData->deviceObjectID = 0; +#endif + pData->component = NULL; + pData->audioUnit = NULL; + pData->pAudioBufferList = NULL; + + mal_result result; + +#if defined(MAL_APPLE_DESKTOP) + AudioObjectID deviceObjectID; + result = mal_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); + if (result != MAL_SUCCESS) { + return result; + } + + pData->deviceObjectID = deviceObjectID; +#endif + + // Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. + pData->periodsOut = pData->periodsIn; + if (pData->periodsOut < 1) { + pData->periodsOut = 1; + } + if (pData->periodsOut > 16) { + pData->periodsOut = 16; + } + + + // Audio component. + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; +#if defined(MAL_APPLE_DESKTOP) + desc.componentSubType = kAudioUnitSubType_HALOutput; +#else + desc.componentSubType = kAudioUnitSubType_RemoteIO; +#endif + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + pData->component = ((mal_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); + if (pData->component == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + + // Audio unit. + OSStatus status = ((mal_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(pData->component, (AudioUnit*)&pData->audioUnit); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + + // The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. + UInt32 enableIOFlag = 1; + if (deviceType == mal_device_type_capture) { + enableIOFlag = 0; + } + + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MAL_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + + enableIOFlag = (enableIOFlag == 0) ? 1 : 0; + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MAL_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + + + // Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. +#if defined(MAL_APPLE_DESKTOP) + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS, &deviceObjectID, sizeof(AudioDeviceID)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(result); + } +#endif + + // Format. This is the hardest part of initialization because there's a few variables to take into account. + // 1) The format must be supported by the device. + // 2) The format must be supported mini_al. + // 3) There's a priority that mini_al prefers. + // + // Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The + // most important property is the sample rate. mini_al can do format conversion for any sample rate and channel count, but cannot do the same + // for the sample data format. If the sample data format is not supported by mini_al it must be ignored completely. + // + // On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. + AudioStreamBasicDescription bestFormat; + { + AudioUnitScope formatScope = (deviceType == mal_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; + AudioUnitElement formatElement = (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS; + + #if defined(MAL_APPLE_DESKTOP) + result = mal_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, pData->usingDefaultFormat, pData->usingDefaultChannels, pData->usingDefaultSampleRate, &bestFormat); + if (result != MAL_SUCCESS) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return result; + } + + // From what I can see, Apple's documentation implies that we should keep the sample rate consistent. + AudioStreamBasicDescription origFormat; + UInt32 origFormatSize = sizeof(origFormat); + if (deviceType == mal_device_type_playback) { + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MAL_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); + } else { + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MAL_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); + } + + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return result; + } + + bestFormat.mSampleRate = origFormat.mSampleRate; + + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); + if (status != noErr) { + // We failed to set the format, so fall back to the current format of the audio unit. + bestFormat = origFormat; + } + #else + UInt32 propSize = sizeof(bestFormat); + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + + // Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try + // setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since + // it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I + // can tell, it looks like the sample rate is shared between playback and capture for everything. + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + mal_assert(pAudioSession != NULL); + + [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; + bestFormat.mSampleRate = pAudioSession.sampleRate; + } + + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + #endif + + result = mal_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); + if (result != MAL_SUCCESS) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return result; + } + + if (pData->formatOut == mal_format_unknown) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return MAL_FORMAT_NOT_SUPPORTED; + } + + pData->channelsOut = bestFormat.mChannelsPerFrame; + pData->sampleRateOut = bestFormat.mSampleRate; + } + + + // Internal channel map. +#if defined(MAL_APPLE_DESKTOP) + result = mal_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut); + if (result != MAL_SUCCESS) { + return result; + } +#else + // TODO: Figure out how to get the channel map using AVAudioSession. + mal_get_standard_channel_map(mal_standard_channel_map_default, pData->channelsOut, pData->channelMapOut); +#endif + + + // Buffer size. Not allowing this to be configurable on iOS. + mal_uint32 actualBufferSizeInFrames = pData->bufferSizeInFramesIn; + +#if defined(MAL_APPLE_DESKTOP) + if (actualBufferSizeInFrames == 0) { + actualBufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, pData->sampleRateOut); + } + + actualBufferSizeInFrames = actualBufferSizeInFrames / pData->periodsOut; + result = mal_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualBufferSizeInFrames); + if (result != MAL_SUCCESS) { + return result; + } +#else + actualBufferSizeInFrames = 4096; +#endif + + pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut; + + // During testing I discovered that the buffer size can be too big. You'll get an error like this: + // + // kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 + // + // Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that + // of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. + { + /*AudioUnitScope propScope = (deviceType == mal_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; + AudioUnitElement propBus = (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS; + + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + }*/ + + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + } + + // We need a buffer list if this is an input device. We render into this in the input callback. + if (deviceType == mal_device_type_capture) { + mal_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; + + size_t allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); // Subtract sizeof(AudioBuffer) because that part is dynamically sized. + if (isInterleaved) { + // Interleaved case. This is the simple case because we just have one buffer. + allocationSize += sizeof(AudioBuffer) * 1; + allocationSize += actualBufferSizeInFrames * mal_get_bytes_per_frame(pData->formatOut, pData->channelsOut); + } else { + // Non-interleaved case. This is the more complex case because there's more than one buffer. + allocationSize += sizeof(AudioBuffer) * pData->channelsOut; + allocationSize += actualBufferSizeInFrames * mal_get_bytes_per_sample(pData->formatOut) * pData->channelsOut; + } + + AudioBufferList* pBufferList = (AudioBufferList*)mal_malloc(allocationSize); + if (pBufferList == NULL) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return MAL_OUT_OF_MEMORY; + } + + if (isInterleaved) { + pBufferList->mNumberBuffers = 1; + pBufferList->mBuffers[0].mNumberChannels = pData->channelsOut; + pBufferList->mBuffers[0].mDataByteSize = actualBufferSizeInFrames * mal_get_bytes_per_frame(pData->formatOut, pData->channelsOut); + pBufferList->mBuffers[0].mData = (mal_uint8*)pBufferList + sizeof(AudioBufferList); + } else { + pBufferList->mNumberBuffers = pData->channelsOut; + for (mal_uint32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { + pBufferList->mBuffers[iBuffer].mNumberChannels = 1; + pBufferList->mBuffers[iBuffer].mDataByteSize = actualBufferSizeInFrames * mal_get_bytes_per_sample(pData->formatOut); + pBufferList->mBuffers[iBuffer].mData = (mal_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * pData->channelsOut)) + (actualBufferSizeInFrames * mal_get_bytes_per_sample(pData->formatOut) * iBuffer); + } + } + + pData->pAudioBufferList = pBufferList; + } + + // Callbacks. + AURenderCallbackStruct callbackInfo; + callbackInfo.inputProcRefCon = pDevice_DoNotReference; + if (deviceType == mal_device_type_playback) { + callbackInfo.inputProc = mal_on_output__coreaudio; + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, MAL_COREAUDIO_OUTPUT_BUS, &callbackInfo, sizeof(callbackInfo)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + } else { + callbackInfo.inputProc = mal_on_input__coreaudio; + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, MAL_COREAUDIO_INPUT_BUS, &callbackInfo, sizeof(callbackInfo)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + } + + // We need to listen for stop events. + status = ((mal_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + + // Initialize the audio unit. + status = ((mal_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); + if (status != noErr) { + mal_free(pData->pAudioBufferList); + pData->pAudioBufferList = NULL; + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + + // Grab the name. +#if defined(MAL_APPLE_DESKTOP) + mal_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); +#endif + + return result; +} + +mal_result mal_device_reinit_internal__coreaudio(mal_device* pDevice, mal_bool32 disposePreviousAudioUnit) +{ + mal_device_init_internal_data__coreaudio data; + data.formatIn = pDevice->format; + data.channelsIn = pDevice->channels; + data.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(data.channelMapIn, pDevice->channelMap, sizeof(pDevice->channelMap)); + data.bufferSizeInFramesIn = pDevice->bufferSizeInFrames; + data.bufferSizeInMillisecondsIn = pDevice->bufferSizeInMilliseconds; + data.periodsIn = pDevice->periods; + data.usingDefaultFormat = pDevice->usingDefaultFormat; + data.usingDefaultChannels = pDevice->usingDefaultChannels; + data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; + data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; + data.shareMode = pDevice->initConfig.shareMode; + + mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, NULL, &data, (void*)pDevice); + if (result != MAL_SUCCESS) { + return result; + } + + // We have successfully initialized the new objects. We now need to uninitialize the previous objects and re-set them. + if (disposePreviousAudioUnit) { + pDevice->pContext->onDeviceStop(pDevice); + ((mal_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + } + if (pDevice->coreaudio.pAudioBufferList) { + mal_free(pDevice->coreaudio.pAudioBufferList); + } + +#if defined(MAL_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectID = (mal_uint32)data.deviceObjectID; +#endif + pDevice->coreaudio.component = (mal_ptr)data.component; + pDevice->coreaudio.audioUnit = (mal_ptr)data.audioUnit; + pDevice->coreaudio.pAudioBufferList = (mal_ptr)data.pAudioBufferList; + + pDevice->internalFormat = data.formatOut; + pDevice->internalChannels = data.channelsOut; + pDevice->internalSampleRate = data.sampleRateOut; + mal_copy_memory(pDevice->internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->bufferSizeInFrames = data.bufferSizeInFramesOut; + pDevice->periods = data.periodsOut; + pDevice->exclusiveMode = MAL_FALSE; + mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName); + + return MAL_SUCCESS; +} + + +mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pConfig; + + mal_assert(pContext != NULL); + mal_assert(pConfig != NULL); + mal_assert(pDevice != NULL); + mal_assert(deviceType == mal_device_type_playback || deviceType == mal_device_type_capture); + + mal_device_init_internal_data__coreaudio data; + data.formatIn = pDevice->format; + data.channelsIn = pDevice->channels; + data.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(data.channelMapIn, pDevice->channelMap, sizeof(pDevice->channelMap)); + data.bufferSizeInFramesIn = pDevice->bufferSizeInFrames; + data.bufferSizeInMillisecondsIn = pDevice->bufferSizeInMilliseconds; + data.periodsIn = pDevice->periods; + data.usingDefaultFormat = pDevice->usingDefaultFormat; + data.usingDefaultChannels = pDevice->usingDefaultChannels; + data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; + data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; + data.shareMode = pDevice->initConfig.shareMode; + + mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, NULL, &data, (void*)pDevice); + if (result != MAL_SUCCESS) { + return result; + } + + // We have successfully initialized the new objects. We now need to uninitialize the previous objects and re-set them. + pDevice->pContext->onDeviceStop(pDevice); + ((mal_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + if (pDevice->coreaudio.pAudioBufferList) { + mal_free(pDevice->coreaudio.pAudioBufferList); + } + +#if defined(MAL_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectID = (mal_uint32)data.deviceObjectID; +#endif + pDevice->coreaudio.component = (mal_ptr)data.component; + pDevice->coreaudio.audioUnit = (mal_ptr)data.audioUnit; + pDevice->coreaudio.pAudioBufferList = (mal_ptr)data.pAudioBufferList; + + pDevice->internalFormat = data.formatOut; + pDevice->internalChannels = data.channelsOut; + pDevice->internalSampleRate = data.sampleRateOut; + mal_copy_memory(pDevice->internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->bufferSizeInFrames = data.bufferSizeInFramesOut; + pDevice->periods = data.periodsOut; + pDevice->exclusiveMode = MAL_FALSE; + mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName); + +#if defined(MAL_APPLE_DESKTOP) + // If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly + // switch the device in the background. + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = (deviceType == mal_device_type_playback) ? kAudioHardwarePropertyDefaultOutputDevice : kAudioHardwarePropertyDefaultInputDevice; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = kAudioObjectPropertyElementMaster; + ((mal_AudioObjectAddPropertyListener_proc)pDevice->pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &mal_default_output_device_changed__coreaudio, pDevice); +#endif + + return MAL_SUCCESS; +} + + +mal_result mal_device__start_backend__coreaudio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + OSStatus status = ((mal_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnit); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + return MAL_SUCCESS; +} + +mal_result mal_device__stop_backend__coreaudio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + OSStatus status = ((mal_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnit); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + return MAL_SUCCESS; +} + + +mal_result mal_context_uninit__coreaudio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_coreaudio); + +#if !defined(MAL_NO_RUNTIME_LINKING) && !defined(MAL_APPLE_MOBILE) + mal_dlclose(pContext->coreaudio.hAudioUnit); + mal_dlclose(pContext->coreaudio.hCoreAudio); + mal_dlclose(pContext->coreaudio.hCoreFoundation); +#endif + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_init__coreaudio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + +#if !defined(MAL_NO_RUNTIME_LINKING) && !defined(MAL_APPLE_MOBILE) + pContext->coreaudio.hCoreFoundation = mal_dlopen("CoreFoundation.framework/CoreFoundation"); + if (pContext->coreaudio.hCoreFoundation == NULL) { + return MAL_API_NOT_FOUND; + } + + pContext->coreaudio.CFStringGetCString = mal_dlsym(pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); + + + pContext->coreaudio.hCoreAudio = mal_dlopen("CoreAudio.framework/CoreAudio"); + if (pContext->coreaudio.hCoreAudio == NULL) { + mal_dlclose(pContext->coreaudio.hCoreFoundation); + return MAL_API_NOT_FOUND; + } + + pContext->coreaudio.AudioObjectGetPropertyData = mal_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); + pContext->coreaudio.AudioObjectGetPropertyDataSize = mal_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); + pContext->coreaudio.AudioObjectSetPropertyData = mal_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); + pContext->coreaudio.AudioObjectAddPropertyListener = mal_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); + + + // It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still + // defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. + // The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to + // AudioToolbox. + pContext->coreaudio.hAudioUnit = mal_dlopen("AudioUnit.framework/AudioUnit"); + if (pContext->coreaudio.hAudioUnit == NULL) { + mal_dlclose(pContext->coreaudio.hCoreAudio); + mal_dlclose(pContext->coreaudio.hCoreFoundation); + return MAL_API_NOT_FOUND; + } + + if (mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { + // Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. + mal_dlclose(pContext->coreaudio.hAudioUnit); + pContext->coreaudio.hAudioUnit = mal_dlopen("AudioToolbox.framework/AudioToolbox"); + if (pContext->coreaudio.hAudioUnit == NULL) { + mal_dlclose(pContext->coreaudio.hCoreAudio); + mal_dlclose(pContext->coreaudio.hCoreFoundation); + return MAL_API_NOT_FOUND; + } + } + + pContext->coreaudio.AudioComponentFindNext = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); + pContext->coreaudio.AudioComponentInstanceDispose = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); + pContext->coreaudio.AudioComponentInstanceNew = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); + pContext->coreaudio.AudioOutputUnitStart = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); + pContext->coreaudio.AudioOutputUnitStop = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); + pContext->coreaudio.AudioUnitAddPropertyListener = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); + pContext->coreaudio.AudioUnitGetProperty = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); + pContext->coreaudio.AudioUnitSetProperty = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); + pContext->coreaudio.AudioUnitInitialize = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); + pContext->coreaudio.AudioUnitRender = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitRender"); +#else + pContext->coreaudio.CFStringGetCString = (mal_proc)CFStringGetCString; + + #if defined(MAL_APPLE_DESKTOP) + pContext->coreaudio.AudioObjectGetPropertyData = (mal_proc)AudioObjectGetPropertyData; + pContext->coreaudio.AudioObjectGetPropertyDataSize = (mal_proc)AudioObjectGetPropertyDataSize; + pContext->coreaudio.AudioObjectSetPropertyData = (mal_proc)AudioObjectSetPropertyData; + pContext->coreaudio.AudioObjectAddPropertyListener = (mal_proc)AudioObjectAddPropertyListener; + #endif + + pContext->coreaudio.AudioComponentFindNext = (mal_proc)AudioComponentFindNext; + pContext->coreaudio.AudioComponentInstanceDispose = (mal_proc)AudioComponentInstanceDispose; + pContext->coreaudio.AudioComponentInstanceNew = (mal_proc)AudioComponentInstanceNew; + pContext->coreaudio.AudioOutputUnitStart = (mal_proc)AudioOutputUnitStart; + pContext->coreaudio.AudioOutputUnitStop = (mal_proc)AudioOutputUnitStop; + pContext->coreaudio.AudioUnitAddPropertyListener = (mal_proc)AudioUnitAddPropertyListener; + pContext->coreaudio.AudioUnitGetProperty = (mal_proc)AudioUnitGetProperty; + pContext->coreaudio.AudioUnitSetProperty = (mal_proc)AudioUnitSetProperty; + pContext->coreaudio.AudioUnitInitialize = (mal_proc)AudioUnitInitialize; + pContext->coreaudio.AudioUnitRender = (mal_proc)AudioUnitRender; +#endif + + pContext->isBackendAsynchronous = MAL_TRUE; + + pContext->onUninit = mal_context_uninit__coreaudio; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__coreaudio; + pContext->onEnumDevices = mal_context_enumerate_devices__coreaudio; + pContext->onGetDeviceInfo = mal_context_get_device_info__coreaudio; + pContext->onDeviceInit = mal_device_init__coreaudio; + pContext->onDeviceUninit = mal_device_uninit__coreaudio; + pContext->onDeviceStart = mal_device__start_backend__coreaudio; + pContext->onDeviceStop = mal_device__stop_backend__coreaudio; + + return MAL_SUCCESS; +} +#endif // Core Audio + + + +/////////////////////////////////////////////////////////////////////////////// +// +// sndio Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_SNDIO +#include +#include + +// Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due +// to mini_al's implementation or if it's some kind of system configuration issue, but basically the default device +// just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's +// demand for it or if I can get it tested and debugged more thoroughly. + +//#if defined(__NetBSD__) || defined(__OpenBSD__) +//#include +//#endif +//#if defined(__FreeBSD__) || defined(__DragonFly__) +//#include +//#endif + +#define MAL_SIO_DEVANY "default" +#define MAL_SIO_PLAY 1 +#define MAL_SIO_REC 2 +#define MAL_SIO_NENC 8 +#define MAL_SIO_NCHAN 8 +#define MAL_SIO_NRATE 16 +#define MAL_SIO_NCONF 4 + +struct mal_sio_hdl; // <-- Opaque + +struct mal_sio_par +{ + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + unsigned int rchan; + unsigned int pchan; + unsigned int rate; + unsigned int bufsz; + unsigned int xrun; + unsigned int round; + unsigned int appbufsz; + int __pad[3]; + unsigned int __magic; +}; + +struct mal_sio_enc +{ + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; +}; + +struct mal_sio_conf +{ + unsigned int enc; + unsigned int rchan; + unsigned int pchan; + unsigned int rate; +}; + +struct mal_sio_cap +{ + struct mal_sio_enc enc[MAL_SIO_NENC]; + unsigned int rchan[MAL_SIO_NCHAN]; + unsigned int pchan[MAL_SIO_NCHAN]; + unsigned int rate[MAL_SIO_NRATE]; + int __pad[7]; + unsigned int nconf; + struct mal_sio_conf confs[MAL_SIO_NCONF]; +}; + +typedef struct mal_sio_hdl* (* mal_sio_open_proc) (const char*, unsigned int, int); +typedef void (* mal_sio_close_proc) (struct mal_sio_hdl*); +typedef int (* mal_sio_setpar_proc) (struct mal_sio_hdl*, struct mal_sio_par*); +typedef int (* mal_sio_getpar_proc) (struct mal_sio_hdl*, struct mal_sio_par*); +typedef int (* mal_sio_getcap_proc) (struct mal_sio_hdl*, struct mal_sio_cap*); +typedef size_t (* mal_sio_write_proc) (struct mal_sio_hdl*, const void*, size_t); +typedef size_t (* mal_sio_read_proc) (struct mal_sio_hdl*, void*, size_t); +typedef int (* mal_sio_start_proc) (struct mal_sio_hdl*); +typedef int (* mal_sio_stop_proc) (struct mal_sio_hdl*); +typedef int (* mal_sio_initpar_proc)(struct mal_sio_par*); + +mal_format mal_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb) +{ + // We only support native-endian right now. + if ((mal_is_little_endian() && le == 0) || (mal_is_big_endian() && le == 1)) { + return mal_format_unknown; + } + + if (bits == 8 && bps == 1 && sig == 0) { + return mal_format_u8; + } + if (bits == 16 && bps == 2 && sig == 1) { + return mal_format_s16; + } + if (bits == 24 && bps == 3 && sig == 1) { + return mal_format_s24; + } + if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { + //return mal_format_s24_32; + } + if (bits == 32 && bps == 4 && sig == 1) { + return mal_format_s32; + } + + return mal_format_unknown; +} + +mal_format mal_find_best_format_from_sio_cap__sndio(struct mal_sio_cap* caps) +{ + mal_assert(caps != NULL); + + mal_format bestFormat = mal_format_unknown; + for (unsigned int iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) { + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + unsigned int bits = caps->enc[iEncoding].bits; + unsigned int bps = caps->enc[iEncoding].bps; + unsigned int sig = caps->enc[iEncoding].sig; + unsigned int le = caps->enc[iEncoding].le; + unsigned int msb = caps->enc[iEncoding].msb; + mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format == mal_format_unknown) { + continue; // Format not supported. + } + + if (bestFormat == mal_format_unknown) { + bestFormat = format; + } else { + if (mal_get_format_priority_index(bestFormat) > mal_get_format_priority_index(format)) { // <-- Lower = better. + bestFormat = format; + } + } + } + } + + return mal_format_unknown; +} + +mal_uint32 mal_find_best_channels_from_sio_cap__sndio(struct mal_sio_cap* caps, mal_device_type deviceType, mal_format requiredFormat) +{ + mal_assert(caps != NULL); + mal_assert(requiredFormat != mal_format_unknown); + + // Just pick whatever configuration has the most channels. + mal_uint32 maxChannels = 0; + for (unsigned int iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + // The encoding should be of requiredFormat. + for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) { + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + unsigned int bits = caps->enc[iEncoding].bits; + unsigned int bps = caps->enc[iEncoding].bps; + unsigned int sig = caps->enc[iEncoding].sig; + unsigned int le = caps->enc[iEncoding].le; + unsigned int msb = caps->enc[iEncoding].msb; + mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format != requiredFormat) { + continue; + } + + // Getting here means the format is supported. Iterate over each channel count and grab the biggest one. + for (unsigned int iChannel = 0; iChannel < MAL_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + if (deviceType == mal_device_type_playback) { + chan = caps->confs[iConfig].pchan; + } else { + chan = caps->confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + unsigned int channels; + if (deviceType == mal_device_type_playback) { + channels = caps->pchan[iChannel]; + } else { + channels = caps->rchan[iChannel]; + } + + if (maxChannels < channels) { + maxChannels = channels; + } + } + } + } + + return maxChannels; +} + +mal_uint32 mal_find_best_sample_rate_from_sio_cap__sndio(struct mal_sio_cap* caps, mal_device_type deviceType, mal_format requiredFormat, mal_uint32 requiredChannels) +{ + mal_assert(caps != NULL); + mal_assert(requiredFormat != mal_format_unknown); + mal_assert(requiredChannels > 0); + mal_assert(requiredChannels <= MAL_MAX_CHANNELS); + + mal_uint32 firstSampleRate = 0; // <-- If the device does not support a standard rate we'll fall back to the first one that's found. + + mal_uint32 bestSampleRate = 0; + for (unsigned int iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + // The encoding should be of requiredFormat. + for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) { + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + unsigned int bits = caps->enc[iEncoding].bits; + unsigned int bps = caps->enc[iEncoding].bps; + unsigned int sig = caps->enc[iEncoding].sig; + unsigned int le = caps->enc[iEncoding].le; + unsigned int msb = caps->enc[iEncoding].msb; + mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format != requiredFormat) { + continue; + } + + // Getting here means the format is supported. Iterate over each channel count and grab the biggest one. + for (unsigned int iChannel = 0; iChannel < MAL_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + if (deviceType == mal_device_type_playback) { + chan = caps->confs[iConfig].pchan; + } else { + chan = caps->confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + unsigned int channels; + if (deviceType == mal_device_type_playback) { + channels = caps->pchan[iChannel]; + } else { + channels = caps->rchan[iChannel]; + } + + if (channels != requiredChannels) { + continue; + } + + // Getting here means we have found a compatible encoding/channel pair. + for (unsigned int iRate = 0; iRate < MAL_SIO_NRATE; iRate += 1) { + mal_uint32 rate = (mal_uint32)caps->rate[iRate]; + + if (firstSampleRate == 0) { + firstSampleRate = rate; + } + + // Disregard this rate if it's not a standard one. + mal_uint32 ratePriority = mal_get_standard_sample_rate_priority_index(rate); + if (ratePriority == (mal_uint32)-1) { + continue; + } + + if (mal_get_standard_sample_rate_priority_index(bestSampleRate) > ratePriority) { // Lower = better. + bestSampleRate = rate; + } + } + } + } + } + + // If a standard sample rate was not found just fall back to the first one that was iterated. + if (bestSampleRate == 0) { + bestSampleRate = firstSampleRate; + } + + return bestSampleRate; +} + + +mal_bool32 mal_context_is_device_id_equal__sndio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return mal_strcmp(pID0->sndio, pID1->sndio) == 0; +} + +mal_result mal_context_enumerate_devices__sndio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + // sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating + // over default devices for now. + mal_bool32 isTerminating = MAL_FALSE; + struct mal_sio_hdl* handle; + + // Playback. + if (!isTerminating) { + handle = ((mal_sio_open_proc)pContext->sndio.sio_open)(MAL_SIO_DEVANY, MAL_SIO_PLAY, 0); + if (handle != NULL) { + // Supports playback. + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MAL_SIO_DEVANY); + mal_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME); + + isTerminating = !callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + + ((mal_sio_close_proc)pContext->sndio.sio_close)(handle); + } + } + + // Capture. + if (!isTerminating) { + handle = ((mal_sio_open_proc)pContext->sndio.sio_open)(MAL_SIO_DEVANY, MAL_SIO_REC, 0); + if (handle != NULL) { + // Supports capture. + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); + mal_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME); + + isTerminating = !callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + + ((mal_sio_close_proc)pContext->sndio.sio_close)(handle); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__sndio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + + // We need to open the device before we can get information about it. + char devid[256]; + if (pDeviceID == NULL) { + mal_strcpy_s(devid, sizeof(devid), MAL_SIO_DEVANY); + mal_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == mal_device_type_playback) ? MAL_DEFAULT_PLAYBACK_DEVICE_NAME : MAL_DEFAULT_CAPTURE_DEVICE_NAME); + } else { + mal_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); + mal_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); + } + + struct mal_sio_hdl* handle = ((mal_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == mal_device_type_playback) ? MAL_SIO_PLAY : MAL_SIO_REC, 0); + if (handle == NULL) { + return MAL_NO_DEVICE; + } + + struct mal_sio_cap caps; + if (((mal_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { + return MAL_ERROR; + } + + for (unsigned int iConfig = 0; iConfig < caps.nconf; iConfig += 1) { + // The main thing we care about is that the encoding is supported by mini_al. If it is, we want to give + // preference to some formats over others. + for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) { + if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + unsigned int bits = caps.enc[iEncoding].bits; + unsigned int bps = caps.enc[iEncoding].bps; + unsigned int sig = caps.enc[iEncoding].sig; + unsigned int le = caps.enc[iEncoding].le; + unsigned int msb = caps.enc[iEncoding].msb; + mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format == mal_format_unknown) { + continue; // Format not supported. + } + + // Add this format if it doesn't already exist. + mal_bool32 formatExists = MAL_FALSE; + for (mal_uint32 iExistingFormat = 0; iExistingFormat < pDeviceInfo->formatCount; iExistingFormat += 1) { + if (pDeviceInfo->formats[iExistingFormat] == format) { + formatExists = MAL_TRUE; + break; + } + } + + if (!formatExists) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = format; + } + } + + // Channels. + for (unsigned int iChannel = 0; iChannel < MAL_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + if (deviceType == mal_device_type_playback) { + chan = caps.confs[iConfig].pchan; + } else { + chan = caps.confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + unsigned int channels; + if (deviceType == mal_device_type_playback) { + channels = caps.pchan[iChannel]; + } else { + channels = caps.rchan[iChannel]; + } + + if (pDeviceInfo->minChannels > channels) { + pDeviceInfo->minChannels = channels; + } + if (pDeviceInfo->maxChannels < channels) { + pDeviceInfo->maxChannels = channels; + } + } + + // Sample rates. + for (unsigned int iRate = 0; iRate < MAL_SIO_NRATE; iRate += 1) { + if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { + unsigned int rate = caps.rate[iRate]; + if (pDeviceInfo->minSampleRate > rate) { + pDeviceInfo->minSampleRate = rate; + } + if (pDeviceInfo->maxSampleRate < rate) { + pDeviceInfo->maxSampleRate = rate; + } + } + } + } + + ((mal_sio_close_proc)pContext->sndio.sio_close)(handle); + return MAL_SUCCESS; +} + +void mal_device_uninit__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + ((mal_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + mal_free(pDevice->sndio.pIntermediaryBuffer); +} + +mal_result mal_device_init__sndio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->sndio); + + const char* deviceName = MAL_SIO_DEVANY; +//#if defined(__FreeBSD__) || defined(__DragonFly__) +// deviceName = "rsnd/0"; +//#else + if (pDeviceID != NULL) { + deviceName = pDeviceID->sndio; + } + + pDevice->sndio.handle = (mal_ptr)((mal_sio_open_proc)pContext->sndio.sio_open)(deviceName, (deviceType == mal_device_type_playback) ? MAL_SIO_PLAY : MAL_SIO_REC, 0); + if (pDevice->sndio.handle == NULL) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + // We need to retrieve the device caps to determine the most appropriate format to use. + struct mal_sio_cap caps; + if (((mal_sio_getcap_proc)pContext->sndio.sio_getcap)((struct mal_sio_hdl*)pDevice->sndio.handle, &caps) == 0) { + ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MAL_ERROR); + } + + mal_format desiredFormat = pDevice->format; + if (pDevice->usingDefaultFormat) { + desiredFormat = mal_find_best_format_from_sio_cap__sndio(&caps); + } + + if (desiredFormat == mal_format_unknown) { + desiredFormat = pDevice->format; + } + + + // Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real + // way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this + // to the requested channels, regardless of whether or not the default channel count is requested. + // + // For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the + // value returned by mal_find_best_channels_from_sio_cap__sndio(). + mal_uint32 desiredChannels = pDevice->channels; + if (pDevice->usingDefaultChannels) { + if (strlen(deviceName) > strlen("rsnd/") && strncmp(deviceName, "rsnd/", strlen("rsnd/")) == 0) { + desiredChannels = mal_find_best_channels_from_sio_cap__sndio(&caps, deviceType, desiredFormat); + } + } + + if (desiredChannels == 0) { + desiredChannels = pDevice->channels; + } + + + mal_uint32 desiredSampleRate = pDevice->sampleRate; + if (pDevice->usingDefaultSampleRate) { + desiredSampleRate = mal_find_best_sample_rate_from_sio_cap__sndio(&caps, deviceType, desiredFormat, desiredChannels); + } + + if (desiredSampleRate == 0) { + desiredSampleRate = pDevice->sampleRate; + } + + + struct mal_sio_par par; + ((mal_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); + par.msb = 0; + par.le = mal_is_little_endian(); + + switch (desiredFormat) { + case mal_format_u8: + { + par.bits = 8; + par.bps = 1; + par.sig = 0; + } break; + + case mal_format_s24: + { + par.bits = 24; + par.bps = 3; + par.sig = 1; + } break; + + case mal_format_s32: + { + par.bits = 32; + par.bps = 4; + par.sig = 1; + } break; + + case mal_format_s16: + case mal_format_f32: + default: + { + par.bits = 16; + par.bps = 2; + par.sig = 1; + } break; + } + + if (deviceType == mal_device_type_playback) { + par.pchan = desiredChannels; + } else { + par.rchan = desiredChannels; + } + + par.rate = desiredSampleRate; + + // Try calculating an appropriate default buffer size after we have the sample rate. + mal_uint32 desiredBufferSizeInFrames = pDevice->bufferSizeInFrames; + if (desiredBufferSizeInFrames == 0) { + desiredBufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, par.rate); + } + + par.round = desiredBufferSizeInFrames / pDevice->periods; + par.appbufsz = par.round * pDevice->periods; + + if (((mal_sio_setpar_proc)pContext->sndio.sio_setpar)((struct mal_sio_hdl*)pDevice->sndio.handle, &par) == 0) { + ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MAL_FORMAT_NOT_SUPPORTED); + } + if (((mal_sio_getpar_proc)pContext->sndio.sio_getpar)((struct mal_sio_hdl*)pDevice->sndio.handle, &par) == 0) { + ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalFormat = mal_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); + + if (deviceType == mal_device_type_playback) { + pDevice->internalChannels = par.pchan; + } else { + pDevice->internalChannels = par.rchan; + } + + pDevice->internalSampleRate = par.rate; + + pDevice->periods = par.appbufsz / par.round; + if (pDevice->periods < 2) { + pDevice->periods = 2; + } + pDevice->bufferSizeInFrames = par.round * pDevice->periods; + pDevice->sndio.fragmentSizeInFrames = par.round; + + mal_get_standard_channel_map(mal_standard_channel_map_sndio, pDevice->internalChannels, pDevice->internalChannelMap); + + // The device is always shared with sndio. + pDevice->exclusiveMode = MAL_FALSE; + + pDevice->sndio.pIntermediaryBuffer = mal_malloc(pDevice->sndio.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels)); + if (pDevice->sndio.pIntermediaryBuffer == NULL) { + ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + } + +#ifdef MAL_DEBUG_OUTPUT + printf("DEVICE INFO\n"); + printf(" Format: %s\n", mal_get_format_name(pDevice->internalFormat)); + printf(" Channels: %d\n", pDevice->internalChannels); + printf(" Sample Rate: %d\n", pDevice->internalSampleRate); + printf(" Buffer Size: %d\n", pDevice->bufferSizeInFrames); + printf(" Periods: %d\n", pDevice->periods); + printf(" appbufsz: %d\n", par.appbufsz); + printf(" round: %d\n", par.round); +#endif + + return MAL_SUCCESS; +} + +mal_result mal_device__start_backend__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (((mal_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct mal_sio_hdl*)pDevice->sndio.handle) == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to start backend device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + + // The device is started by the next calls to read() and write(). For playback it's simple - just read + // data from the client, then write it to the device with write() which will in turn start the device. + // For capture it's a bit less intuitive - we do nothing (it'll be started automatically by the first + // call to read(). + if (pDevice->type == mal_device_type_playback) { + // Playback. Need to load the entire buffer, which means we need to write a fragment for each period. + for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; iPeriod += 1) { + mal_device__read_frames_from_client(pDevice, pDevice->sndio.fragmentSizeInFrames, pDevice->sndio.pIntermediaryBuffer); + + int bytesWritten = ((mal_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct mal_sio_hdl*)pDevice->sndio.handle, pDevice->sndio.pIntermediaryBuffer, pDevice->sndio.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels)); + if (bytesWritten == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to send initial chunk of data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + } + } else { + // Capture. Do nothing. + } + + return MAL_SUCCESS; +} + +mal_result mal_device__stop_backend__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + ((mal_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct mal_sio_hdl*)pDevice->sndio.handle); + return MAL_SUCCESS; +} + +mal_result mal_device__break_main_loop__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->sndio.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +mal_result mal_device__main_loop__sndio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->sndio.breakFromMainLoop = MAL_FALSE; + while (!pDevice->sndio.breakFromMainLoop) { + // Break from the main loop if the device isn't started anymore. Likely what's happened is the application + // has requested that the device be stopped. + if (!mal_device_is_started(pDevice)) { + break; + } + + if (pDevice->type == mal_device_type_playback) { + // Playback. + mal_device__read_frames_from_client(pDevice, pDevice->sndio.fragmentSizeInFrames, pDevice->sndio.pIntermediaryBuffer); + + int bytesWritten = ((mal_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct mal_sio_hdl*)pDevice->sndio.handle, pDevice->sndio.pIntermediaryBuffer, pDevice->sndio.fragmentSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat)); + if (bytesWritten == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + } else { + // Capture. + int bytesRead = ((mal_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct mal_sio_hdl*)pDevice->sndio.handle, pDevice->sndio.pIntermediaryBuffer, pDevice->sndio.fragmentSizeInFrames * mal_get_bytes_per_sample(pDevice->internalFormat)); + if (bytesRead == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + } + + mal_uint32 framesRead = (mal_uint32)bytesRead / pDevice->internalChannels / mal_get_bytes_per_sample(pDevice->internalFormat); + mal_device__send_frames_to_client(pDevice, framesRead, pDevice->sndio.pIntermediaryBuffer); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__sndio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_sndio); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_init__sndio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + +#ifndef MAL_NO_RUNTIME_LINKING + // libpulse.so + const char* libsndioNames[] = { + "libsndio.so" + }; + + for (size_t i = 0; i < mal_countof(libsndioNames); ++i) { + pContext->sndio.sndioSO = mal_dlopen(libsndioNames[i]); + if (pContext->sndio.sndioSO != NULL) { + break; + } + } + + if (pContext->sndio.sndioSO == NULL) { + return MAL_NO_BACKEND; + } + + pContext->sndio.sio_open = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_open"); + pContext->sndio.sio_close = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_close"); + pContext->sndio.sio_setpar = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_setpar"); + pContext->sndio.sio_getpar = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_getpar"); + pContext->sndio.sio_getcap = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_getcap"); + pContext->sndio.sio_write = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_write"); + pContext->sndio.sio_read = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_read"); + pContext->sndio.sio_start = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_start"); + pContext->sndio.sio_stop = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_stop"); + pContext->sndio.sio_initpar = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_initpar"); +#else + pContext->sndio.sio_open = sio_open; + pContext->sndio.sio_close = sio_close; + pContext->sndio.sio_setpar = sio_setpar; + pContext->sndio.sio_getpar = sio_getpar; + pContext->sndio.sio_getcap = sio_getcap; + pContext->sndio.sio_write = sio_write; + pContext->sndio.sio_read = sio_read; + pContext->sndio.sio_start = sio_start; + pContext->sndio.sio_stop = sio_stop; + pContext->sndio.sio_initpar = sio_initpar; +#endif + + pContext->onUninit = mal_context_uninit__sndio; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__sndio; + pContext->onEnumDevices = mal_context_enumerate_devices__sndio; + pContext->onGetDeviceInfo = mal_context_get_device_info__sndio; + pContext->onDeviceInit = mal_device_init__sndio; + pContext->onDeviceUninit = mal_device_uninit__sndio; + pContext->onDeviceStart = mal_device__start_backend__sndio; + pContext->onDeviceStop = mal_device__stop_backend__sndio; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__sndio; + pContext->onDeviceMainLoop = mal_device__main_loop__sndio; + + return MAL_SUCCESS; +} +#endif // sndio + + + +/////////////////////////////////////////////////////////////////////////////// +// +// audio(4) Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_AUDIO4 +#include +#include +#include +#include +#include +#include +#include + +#if defined(__OpenBSD__) + #include + #if defined(OpenBSD) && OpenBSD >= 201709 + #define MAL_AUDIO4_USE_NEW_API + #endif +#endif + +void mal_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) +{ + mal_assert(id != NULL); + mal_assert(idSize > 0); + mal_assert(deviceIndex >= 0); + + size_t baseLen = strlen(base); + mal_assert(idSize > baseLen); + + mal_strcpy_s(id, idSize, base); + mal_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); +} + +mal_result mal_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) +{ + mal_assert(id != NULL); + mal_assert(base != NULL); + mal_assert(pIndexOut != NULL); + + size_t idLen = strlen(id); + size_t baseLen = strlen(base); + if (idLen <= baseLen) { + return MAL_ERROR; // Doesn't look like the id starts with the base. + } + + if (strncmp(id, base, baseLen) != 0) { + return MAL_ERROR; // ID does not begin with base. + } + + const char* deviceIndexStr = id + baseLen; + if (deviceIndexStr[0] == '\0') { + return MAL_ERROR; // No index specified in the ID. + } + + if (pIndexOut) { + *pIndexOut = atoi(deviceIndexStr); + } + + return MAL_SUCCESS; +} + +mal_bool32 mal_context_is_device_id_equal__audio4(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return mal_strcmp(pID0->audio4, pID1->audio4) == 0; +} + +#if !defined(MAL_AUDIO4_USE_NEW_API) +mal_format mal_format_from_encoding__audio4(unsigned int encoding, unsigned int precision) +{ + if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) { + return mal_format_u8; + } else { + if (mal_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { + if (precision == 16) { + return mal_format_s16; + } else if (precision == 24) { + return mal_format_s24; + } else if (precision == 32) { + return mal_format_s32; + } + } else if (mal_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { + if (precision == 16) { + return mal_format_s16; + } else if (precision == 24) { + return mal_format_s24; + } else if (precision == 32) { + return mal_format_s32; + } + } + } + + return mal_format_unknown; // Encoding not supported. +} + +mal_format mal_format_from_prinfo__audio4(struct audio_prinfo* prinfo) +{ + return mal_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); +} +#else +mal_format mal_format_from_swpar__audio4(struct audio_swpar* par) +{ + if (par->bits == 8 && par->bps == 1 && par->sig == 0) { + return mal_format_u8; + } + if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == mal_is_little_endian()) { + return mal_format_s16; + } + if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == mal_is_little_endian()) { + return mal_format_s24; + } + if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == mal_is_little_endian()) { + return mal_format_f32; + } + + // Format not supported. + return mal_format_unknown; +} +#endif + +mal_result mal_context_get_device_info_from_fd__audio4(mal_context* pContext, mal_device_type deviceType, int fd, mal_device_info* pInfoOut) +{ + mal_assert(pContext != NULL); + mal_assert(fd >= 0); + mal_assert(pInfoOut != NULL); + + (void)pContext; + (void)deviceType; + + audio_device_t fdDevice; + if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { + return MAL_ERROR; // Failed to retrieve device info. + } + + // Name. + mal_strcpy_s(pInfoOut->name, sizeof(pInfoOut->name), fdDevice.name); + +#if !defined(MAL_AUDIO4_USE_NEW_API) + // Supported formats. We get this by looking at the encodings. + int counter = 0; + for (;;) { + audio_encoding_t encoding; + mal_zero_object(&encoding); + encoding.index = counter; + if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { + break; + } + + mal_format format = mal_format_from_encoding__audio4(encoding.encoding, encoding.precision); + if (format != mal_format_unknown) { + pInfoOut->formats[pInfoOut->formatCount++] = format; + } + + counter += 1; + } + + audio_info_t fdInfo; + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + return MAL_ERROR; + } + + if (deviceType == mal_device_type_playback) { + pInfoOut->minChannels = fdInfo.play.channels; + pInfoOut->maxChannels = fdInfo.play.channels; + pInfoOut->minSampleRate = fdInfo.play.sample_rate; + pInfoOut->maxSampleRate = fdInfo.play.sample_rate; + } else { + pInfoOut->minChannels = fdInfo.record.channels; + pInfoOut->maxChannels = fdInfo.record.channels; + pInfoOut->minSampleRate = fdInfo.record.sample_rate; + pInfoOut->maxSampleRate = fdInfo.record.sample_rate; + } +#else + struct audio_swpar fdPar; + if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { + return MAL_ERROR; + } + + mal_format format = mal_format_from_swpar__audio4(&fdPar); + if (format == mal_format_unknown) { + return MAL_FORMAT_NOT_SUPPORTED; + } + pInfoOut->formats[pInfoOut->formatCount++] = format; + + if (deviceType == mal_device_type_playback) { + pInfoOut->minChannels = fdPar.pchan; + pInfoOut->maxChannels = fdPar.pchan; + } else { + pInfoOut->minChannels = fdPar.rchan; + pInfoOut->maxChannels = fdPar.rchan; + } + + pInfoOut->minSampleRate = fdPar.rate; + pInfoOut->maxSampleRate = fdPar.rate; +#endif + + return MAL_SUCCESS; +} + +mal_result mal_context_enumerate_devices__audio4(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + const int maxDevices = 64; + + // Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" + // version here since we can open it even when another process has control of the "/dev/audioN" device. + char devpath[256]; + for (int iDevice = 0; iDevice < maxDevices; ++iDevice) { + mal_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); + mal_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); + + struct stat st; + if (stat(devpath, &st) < 0) { + break; + } + + // The device exists, but we need to check if it's usable as playback and/or capture. + int fd; + mal_bool32 isTerminating = MAL_FALSE; + + // Playback. + if (!isTerminating) { + fd = open(devpath, O_RDONLY, 0); + if (fd >= 0) { + // Supports playback. + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); + if (mal_context_get_device_info_from_fd__audio4(pContext, mal_device_type_playback, fd, &deviceInfo) == MAL_SUCCESS) { + isTerminating = !callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + + close(fd); + } + } + + // Capture. + if (!isTerminating) { + fd = open(devpath, O_WRONLY, 0); + if (fd >= 0) { + // Supports capture. + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); + if (mal_context_get_device_info_from_fd__audio4(pContext, mal_device_type_capture, fd, &deviceInfo) == MAL_SUCCESS) { + isTerminating = !callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } + + close(fd); + } + } + + if (isTerminating) { + break; + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__audio4(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + + // We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number + // from the device ID which will be in "/dev/audioN" format. + int fd = -1; + int deviceIndex = -1; + char ctlid[256]; + if (pDeviceID == NULL) { + // Default device. + mal_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); + } else { + // Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". + mal_result result = mal_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); + if (result != MAL_SUCCESS) { + return result; + } + + mal_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); + } + + fd = open(ctlid, (deviceType == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0); + if (fd == -1) { + return MAL_NO_DEVICE; + } + + if (deviceIndex == -1) { + mal_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); + } else { + mal_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); + } + + mal_result result = mal_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); + + close(fd); + return result; +} + +void mal_device_uninit__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + close(pDevice->audio4.fd); + mal_free(pDevice->audio4.pIntermediaryBuffer); +} + +mal_result mal_device_init__audio4(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->audio4); + pDevice->audio4.fd = -1; + + // The first thing to do is open the file. + const char* deviceName = "/dev/audio"; + if (pDeviceID != NULL) { + deviceName = pDeviceID->audio4; + } + + pDevice->audio4.fd = open(deviceName, ((deviceType == mal_device_type_playback) ? O_WRONLY : O_RDONLY) | O_NONBLOCK, 0); + if (pDevice->audio4.fd == -1) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + +#if !defined(MAL_AUDIO4_USE_NEW_API) + audio_info_t fdInfo; + AUDIO_INITINFO(&fdInfo); + + struct audio_prinfo* prinfo; + if (deviceType == mal_device_type_playback) { + prinfo = &fdInfo.play; + fdInfo.mode = AUMODE_PLAY; + } else { + prinfo = &fdInfo.record; + fdInfo.mode = AUMODE_RECORD; + } + + // Format. Note that it looks like audio4 does not support floating point formats. In this case + // we just fall back to s16. + switch (pDevice->format) + { + case mal_format_u8: + { + prinfo->encoding = AUDIO_ENCODING_ULINEAR; + prinfo->precision = 8; + } break; + + case mal_format_s24: + { + prinfo->encoding = (mal_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + prinfo->precision = 24; + } break; + + case mal_format_s32: + { + prinfo->encoding = (mal_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + prinfo->precision = 32; + } break; + + case mal_format_s16: + case mal_format_f32: + default: + { + prinfo->encoding = (mal_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + prinfo->precision = 16; + } break; + } + + // We always want to the use the devices native channel count and sample rate. + mal_device_info nativeInfo; + mal_result result = mal_context_get_device_info(pContext, deviceType, pDeviceID, pConfig->shareMode, &nativeInfo); + if (result != MAL_SUCCESS) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve device format.", result); + } + + prinfo->channels = nativeInfo.maxChannels; + prinfo->sample_rate = nativeInfo.maxSampleRate; + + // We need to apply the settings so far so we can get back the actual sample rate which we need for calculating + // the default buffer size below. + if (ioctl(pDevice->audio4.fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + if (ioctl(pDevice->audio4.fd, AUDIO_GETINFO, &fdInfo) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalFormat = mal_format_from_prinfo__audio4(prinfo); + if (pDevice->internalFormat == mal_format_unknown) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by mini_al. The device is unusable.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalChannels = prinfo->channels; + pDevice->internalSampleRate = prinfo->sample_rate; + + + + // Try calculating an appropriate default buffer size. + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + } + + // What mini_al calls a fragment, audio4 calls a block. + mal_uint32 fragmentSizeInBytes = pDevice->bufferSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + if (fragmentSizeInBytes < 16) { + fragmentSizeInBytes = 16; + } + + + AUDIO_INITINFO(&fdInfo); + fdInfo.hiwat = mal_max(pDevice->periods, 5); + fdInfo.lowat = (unsigned int)(fdInfo.hiwat * 0.75); + fdInfo.blocksize = fragmentSizeInBytes / fdInfo.hiwat; + if (ioctl(pDevice->audio4.fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->periods = fdInfo.hiwat; + pDevice->bufferSizeInFrames = (fdInfo.blocksize * fdInfo.hiwat) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + pDevice->audio4.fragmentSizeInFrames = fdInfo.blocksize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); +#else + // We need to retrieve the format of the device so we can know the channel count and sample rate. Then we + // can calculate the buffer size. + struct audio_swpar fdPar; + if (ioctl(pDevice->audio4.fd, AUDIO_GETPAR, &fdPar) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.", MAL_FORMAT_NOT_SUPPORTED); + } + + // Set the initial internal formats so we can do calculations below. + pDevice->internalFormat = mal_format_from_swpar__audio4(&fdPar); + if (deviceType == mal_device_type_playback) { + pDevice->internalChannels = fdPar.pchan; + } else { + pDevice->internalChannels = fdPar.rchan; + } + pDevice->internalSampleRate = fdPar.rate; + + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + } + + // What mini_al calls a fragment, audio4 calls a block. + mal_uint32 bufferSizeInBytes = pDevice->bufferSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + if (bufferSizeInBytes < 16) { + bufferSizeInBytes = 16; + } + + fdPar.nblks = pDevice->periods; + fdPar.round = bufferSizeInBytes / fdPar.nblks; + + if (ioctl(pDevice->audio4.fd, AUDIO_SETPAR, &fdPar) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MAL_FORMAT_NOT_SUPPORTED); + } + + if (ioctl(pDevice->audio4.fd, AUDIO_GETPAR, &fdPar) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalFormat = mal_format_from_swpar__audio4(&fdPar); + if (deviceType == mal_device_type_playback) { + pDevice->internalChannels = fdPar.pchan; + } else { + pDevice->internalChannels = fdPar.rchan; + } + pDevice->internalSampleRate = fdPar.rate; + + pDevice->periods = fdPar.nblks; + pDevice->bufferSizeInFrames = (fdPar.nblks * fdPar.round) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + pDevice->audio4.fragmentSizeInFrames = fdPar.round / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); +#endif + + + // For the channel map, I'm not sure how to query the channel map (or if it's even possible). I'm just + // using the channels defined in FreeBSD's sound(4) man page. + mal_get_standard_channel_map(mal_standard_channel_map_sound4, pDevice->internalChannels, pDevice->internalChannelMap); + + + // The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD + // introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as + // I'm aware. +#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 + pDevice->exclusiveMode = MAL_FALSE; +#else + pDevice->exclusiveMode = MAL_TRUE; +#endif + + + // When not using MMAP mode we need to use an intermediary buffer to the data transfer between the client + // and device. Everything is done by the size of a fragment. + pDevice->audio4.pIntermediaryBuffer = mal_malloc(pDevice->audio4.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels)); + if (pDevice->audio4.pIntermediaryBuffer == NULL) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + } + + + return MAL_SUCCESS; +} + +mal_result mal_device__start_backend__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->audio4.fd == -1) { + return MAL_INVALID_ARGS; + } + + // The device is started by the next calls to read() and write(). For playback it's simple - just read + // data from the client, then write it to the device with write() which will in turn start the device. + // For capture it's a bit less intuitive - we do nothing (it'll be started automatically by the first + // call to read(). + if (pDevice->type == mal_device_type_playback) { + // Playback. Need to load the entire buffer, which means we need to write a fragment for each period. + for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; iPeriod += 1) { + mal_device__read_frames_from_client(pDevice, pDevice->audio4.fragmentSizeInFrames, pDevice->audio4.pIntermediaryBuffer); + + int bytesWritten = write(pDevice->audio4.fd, pDevice->audio4.pIntermediaryBuffer, pDevice->audio4.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels)); + if (bytesWritten == -1) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to send initial chunk of data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + } + } else { + // Capture. Do nothing. + } + + return MAL_SUCCESS; +} + +mal_result mal_device__stop_backend__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->audio4.fd == -1) { + return MAL_INVALID_ARGS; + } + +#if !defined(MAL_AUDIO4_USE_NEW_API) + if (ioctl(pDevice->audio4.fd, AUDIO_FLUSH, 0) < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } +#else + if (ioctl(pDevice->audio4.fd, AUDIO_STOP, 0) < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } +#endif + + return MAL_SUCCESS; +} + +mal_result mal_device__break_main_loop__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->audio4.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +mal_result mal_device__wait__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + struct pollfd fds[1]; + fds[0].fd = pDevice->audio4.fd; + fds[0].events = (pDevice->type == mal_device_type_playback) ? (POLLOUT | POLLWRBAND) : (POLLIN | POLLPRI); + int timeout = 2 * 1000; + int ioresult = poll(fds, mal_countof(fds), timeout); + if (ioresult < 0) { + #ifdef MAL_DEBUG_OUTPUT + printf("poll() failed: timeout=%d, ioresult=%d\n", pDevice->bufferSizeInMilliseconds, ioresult); + #endif + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to wait for device.", MAL_ERROR); + } + + // Check for a timeout. This has been annoying in my testing. In my testing, when the device is unplugged it will just + // hang on the next calls to write(), ioctl(), etc. The only way I have figured out how to handle this is to wait for + // a timeout from poll(). In the unplugging case poll() will timeout, however there's no indication that the device is + // unusable - no flags are set, no errors are reported, nothing. To work around this I have decided to outright fail + // in the event of a timeout. + if (ioresult == 0) { + // Check for errors. + if ((fds[0].revents & (POLLERR | POLLNVAL)) != 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to wait for device.", MAL_NO_DEVICE); + } + if ((fds[0].revents & (POLLHUP)) != 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to wait for device. Disconnected.", MAL_NO_DEVICE); + } + + // A return value of 0 from poll indicates a timeout. + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Timeout while waiting for device.", MAL_TIMEOUT); + } + + mal_assert(ioresult > 0); + return MAL_SUCCESS; +} + +mal_result mal_device__main_loop__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->audio4.breakFromMainLoop = MAL_FALSE; + while (!pDevice->audio4.breakFromMainLoop) { + // Break from the main loop if the device isn't started anymore. Likely what's happened is the application + // has requested that the device be stopped. + if (!mal_device_is_started(pDevice)) { + break; + } + + if (pDevice->type == mal_device_type_playback) { + // Playback. + mal_device__read_frames_from_client(pDevice, pDevice->audio4.fragmentSizeInFrames, pDevice->audio4.pIntermediaryBuffer); + + // Wait for data to become available. + mal_result result = mal_device__wait__audio4(pDevice); + if (result != MAL_SUCCESS) { + return result; + } + + size_t bytesToWrite = pDevice->audio4.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + while (bytesToWrite > 0) { + ssize_t bytesWritten = write(pDevice->audio4.fd, pDevice->audio4.pIntermediaryBuffer, bytesToWrite); + if (bytesWritten < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + + if (bytesWritten == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to write any data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + + bytesToWrite -= bytesWritten; + } + } else { + // Capture. + mal_result result = mal_device__wait__audio4(pDevice); + if (result != MAL_SUCCESS) { + return result; + } + + size_t totalBytesRead = 0; + size_t bytesToRead = pDevice->audio4.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + while (bytesToRead > 0) { + ssize_t bytesRead = read(pDevice->audio4.fd, pDevice->audio4.pIntermediaryBuffer, bytesToRead); + if (bytesRead < 0) { + if (errno == EAGAIN) { + break; + } + + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + } + + if (bytesRead == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to read any data from the device.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + } + + bytesToRead -= bytesRead; + totalBytesRead += bytesRead; + } + + mal_uint32 framesRead = (mal_uint32)totalBytesRead / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + mal_device__send_frames_to_client(pDevice, framesRead, pDevice->audio4.pIntermediaryBuffer); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__audio4(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_audio4); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_init__audio4(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + pContext->onUninit = mal_context_uninit__audio4; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__audio4; + pContext->onEnumDevices = mal_context_enumerate_devices__audio4; + pContext->onGetDeviceInfo = mal_context_get_device_info__audio4; + pContext->onDeviceInit = mal_device_init__audio4; + pContext->onDeviceUninit = mal_device_uninit__audio4; + pContext->onDeviceStart = mal_device__start_backend__audio4; + pContext->onDeviceStop = mal_device__stop_backend__audio4; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__audio4; + pContext->onDeviceMainLoop = mal_device__main_loop__audio4; + + return MAL_SUCCESS; +} +#endif // audio4 + + /////////////////////////////////////////////////////////////////////////////// // // OSS Backend @@ -6750,51 +17158,47 @@ int mal_open_temp_device__oss() return -1; } -mal_result mal_context_init__oss(mal_context* pContext) +mal_result mal_context_open_device__oss(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, int* pfd) { mal_assert(pContext != NULL); + mal_assert(pfd != NULL); + (void)pContext; - // Try opening a temporary device first so we can get version information. This is closed at the end. - int fd = mal_open_temp_device__oss(); - if (fd == -1) { - return mal_context_post_error(pContext, NULL, "[OSS] Failed to open temporary device for retrieving system properties.", MAL_NO_BACKEND); // Looks liks OSS isn't installed, or there are no available devices. + *pfd = -1; + + char deviceName[64]; + if (pDeviceID != NULL) { + mal_strncpy_s(deviceName, sizeof(deviceName), pDeviceID->oss, (size_t)-1); + } else { + mal_strncpy_s(deviceName, sizeof(deviceName), "/dev/dsp", (size_t)-1); } - // Grab the OSS version. - int ossVersion = 0; - int result = ioctl(fd, OSS_GETVERSION, &ossVersion); - if (result == -1) { - close(fd); - return mal_context_post_error(pContext, NULL, "[OSS] Failed to retrieve OSS version.", MAL_NO_BACKEND); + *pfd = open(deviceName, (type == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0); + if (*pfd == -1) { + return MAL_FAILED_TO_OPEN_BACKEND_DEVICE; } - pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); - pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); - - close(fd); return MAL_SUCCESS; } -mal_result mal_context_uninit__oss(mal_context* pContext) +mal_bool32 mal_context_is_device_id_equal__oss(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) { mal_assert(pContext != NULL); - mal_assert(pContext->backend == mal_backend_oss); - + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); (void)pContext; - return MAL_SUCCESS; + + return mal_strcmp(pID0->oss, pID1->oss) == 0; } -static mal_result mal_enumerate_devices__oss(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +mal_result mal_context_enumerate_devices__oss(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) { - (void)pContext; + mal_assert(pContext != NULL); + mal_assert(callback != NULL); - mal_uint32 infoSize = *pCount; - *pCount = 0; - - // The object returned by SNDCTL_SYSINFO will have the information we're after. int fd = mal_open_temp_device__oss(); if (fd == -1) { - return mal_context_post_error(pContext, NULL, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MAL_NO_BACKEND); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MAL_NO_BACKEND); } oss_sysinfo si; @@ -6805,62 +17209,141 @@ static mal_result mal_enumerate_devices__oss(mal_context* pContext, mal_device_t ai.dev = iAudioDevice; result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); if (result != -1) { - mal_bool32 includeThisDevice = MAL_FALSE; - if (type == mal_device_type_playback && (ai.caps & PCM_CAP_OUTPUT) != 0) { - includeThisDevice = MAL_TRUE; - } else if (type == mal_device_type_capture && (ai.caps & PCM_CAP_INPUT) != 0) { - includeThisDevice = MAL_TRUE; - } + if (ai.devnode[0] != '\0') { // <-- Can be blank, according to documentation. + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); - if (includeThisDevice) { - if (ai.devnode[0] != '\0') { // <-- Can be blank, according to documentation. - if (pInfo != NULL) { - if (infoSize > 0) { - mal_strncpy_s(pInfo->id.oss, sizeof(pInfo->id.oss), ai.devnode, (size_t)-1); + // ID + mal_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); - // The human readable device name should be in the "ai.handle" variable, but it can - // sometimes be empty in which case we just fall back to "ai.name" which is less user - // friendly, but usually has a value. - if (ai.handle[0] != '\0') { - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), ai.handle, (size_t)-1); - } else { - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), ai.name, (size_t)-1); - } + // The human readable device name should be in the "ai.handle" variable, but it can + // sometimes be empty in which case we just fall back to "ai.name" which is less user + // friendly, but usually has a value. + if (ai.handle[0] != '\0') { + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); + } else { + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); + } - pInfo += 1; - infoSize -= 1; - *pCount += 1; - } - } else { - *pCount += 1; - } + // The device can be both playback and capture. + mal_bool32 isTerminating = MAL_FALSE; + if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { + isTerminating = !callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { + isTerminating = !callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } + + if (isTerminating) { + break; } } } } } else { - // Failed to retrieve the system information. Just return a default device for both playback and capture. - if (pInfo != NULL) { - if (infoSize > 0) { - mal_strncpy_s(pInfo[0].id.oss, sizeof(pInfo[0].id.oss), "/dev/dsp", (size_t)-1); - if (type == mal_device_type_playback) { - mal_strncpy_s(pInfo[0].name, sizeof(pInfo[0].name), "Default Playback Device", (size_t)-1); - } else { - mal_strncpy_s(pInfo[0].name, sizeof(pInfo[0].name), "Default Capture Device", (size_t)-1); - } - - *pCount = 1; - } - } else { - *pCount = 1; - } + close(fd); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MAL_NO_BACKEND); } close(fd); return MAL_SUCCESS; } -static void mal_device_uninit__oss(mal_device* pDevice) +mal_result mal_context_get_device_info__oss(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + + // Handle the default device a little differently. + if (pDeviceID == NULL) { + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + return MAL_SUCCESS; + } + + + // If we get here it means we are _not_ using the default device. + mal_bool32 foundDevice = MAL_FALSE; + + int fdTemp = mal_open_temp_device__oss(); + if (fdTemp == -1) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MAL_NO_BACKEND); + } + + oss_sysinfo si; + int result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); + if (result != -1) { + for (int iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { + oss_audioinfo ai; + ai.dev = iAudioDevice; + result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); + if (result != -1) { + if (mal_strcmp(ai.devnode, pDeviceID->oss) == 0) { + // It has the same name, so now just confirm the type. + if ((deviceType == mal_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || + (deviceType == mal_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { + // ID + mal_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); + + // The human readable device name should be in the "ai.handle" variable, but it can + // sometimes be empty in which case we just fall back to "ai.name" which is less user + // friendly, but usually has a value. + if (ai.handle[0] != '\0') { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); + } + + pDeviceInfo->minChannels = ai.min_channels; + pDeviceInfo->maxChannels = ai.max_channels; + pDeviceInfo->minSampleRate = ai.min_rate; + pDeviceInfo->maxSampleRate = ai.max_rate; + pDeviceInfo->formatCount = 0; + + unsigned int formatMask; + if (deviceType == mal_device_type_playback) { + formatMask = ai.oformats; + } else { + formatMask = ai.iformats; + } + + if ((formatMask & AFMT_U8) != 0) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_u8; + } + if (((formatMask & AFMT_S16_LE) != 0 && mal_is_little_endian()) || (AFMT_S16_BE && mal_is_big_endian())) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s16; + } + if (((formatMask & AFMT_S32_LE) != 0 && mal_is_little_endian()) || (AFMT_S32_BE && mal_is_big_endian())) { + pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s32; + } + + foundDevice = MAL_TRUE; + break; + } + } + } + } + } else { + close(fdTemp); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MAL_NO_BACKEND); + } + + + close(fdTemp); + + if (!foundDevice) { + return MAL_NO_DEVICE; + } + + + return MAL_SUCCESS; +} + +void mal_device_uninit__oss(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -6868,23 +17351,16 @@ static void mal_device_uninit__oss(mal_device* pDevice) mal_free(pDevice->oss.pIntermediaryBuffer); } -static mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) { (void)pContext; mal_assert(pDevice != NULL); mal_zero_object(&pDevice->oss); - char deviceName[64]; - if (pDeviceID != NULL) { - mal_strncpy_s(deviceName, sizeof(deviceName), pDeviceID->oss, (size_t)-1); - } else { - mal_strncpy_s(deviceName, sizeof(deviceName), "/dev/dsp", (size_t)-1); - } - - pDevice->oss.fd = open(deviceName, (type == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0); - if (pDevice->oss.fd == -1) { - return mal_post_error(pDevice, "[OSS] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + mal_result result = mal_context_open_device__oss(pContext, type, pDeviceID, &pDevice->oss.fd); + if (result != MAL_SUCCESS) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } // The OSS documantation is very clear about the order we should be initializing the device's properties: @@ -6895,33 +17371,43 @@ static mal_result mal_device_init__oss(mal_context* pContext, mal_device_type ty // Format. int ossFormat = AFMT_U8; switch (pDevice->format) { - case mal_format_s16: ossFormat = AFMT_S16_LE; break; - case mal_format_s24: ossFormat = AFMT_S32_LE; break; - case mal_format_s32: ossFormat = AFMT_S32_LE; break; - case mal_format_f32: ossFormat = AFMT_S32_LE; break; + case mal_format_s16: ossFormat = (mal_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; + case mal_format_s24: ossFormat = (mal_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; + case mal_format_s32: ossFormat = (mal_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; + case mal_format_f32: ossFormat = (mal_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; case mal_format_u8: default: ossFormat = AFMT_U8; break; } - int result = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFMT, &ossFormat); - if (result == -1) { + int ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFMT, &ossFormat); + if (ossResult == -1) { close(pDevice->oss.fd); - return mal_post_error(pDevice, "[OSS] Failed to set format.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MAL_FORMAT_NOT_SUPPORTED); } - switch (ossFormat) { - case AFMT_U8: pDevice->internalFormat = mal_format_u8; break; - case AFMT_S16_LE: pDevice->internalFormat = mal_format_s16; break; - case AFMT_S32_LE: pDevice->internalFormat = mal_format_s32; break; - default: mal_post_error(pDevice, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + if (ossFormat == AFMT_U8) { + pDevice->internalFormat = mal_format_u8; + } else { + if (mal_is_little_endian()) { + switch (ossFormat) { + case AFMT_S16_LE: pDevice->internalFormat = mal_format_s16; break; + case AFMT_S32_LE: pDevice->internalFormat = mal_format_s32; break; + default: mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + } + } else { + switch (ossFormat) { + case AFMT_S16_BE: pDevice->internalFormat = mal_format_s16; break; + case AFMT_S32_BE: pDevice->internalFormat = mal_format_s32; break; + default: mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + } + } } - // Channels. int ossChannels = (int)pConfig->channels; - result = ioctl(pDevice->oss.fd, SNDCTL_DSP_CHANNELS, &ossChannels); - if (result == -1) { + ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_CHANNELS, &ossChannels); + if (ossResult == -1) { close(pDevice->oss.fd); - return mal_post_error(pDevice, "[OSS] Failed to set channel count.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.", MAL_FORMAT_NOT_SUPPORTED); } pDevice->internalChannels = ossChannels; @@ -6929,22 +17415,26 @@ static mal_result mal_device_init__oss(mal_context* pContext, mal_device_type ty // Sample rate. int ossSampleRate = (int)pConfig->sampleRate; - result = ioctl(pDevice->oss.fd, SNDCTL_DSP_SPEED, &ossSampleRate); - if (result == -1) { + ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_SPEED, &ossSampleRate); + if (ossResult == -1) { close(pDevice->oss.fd); - return mal_post_error(pDevice, "[OSS] Failed to set sample rate.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.", MAL_FORMAT_NOT_SUPPORTED); } - pDevice->sampleRate = ossSampleRate; + pDevice->internalSampleRate = ossSampleRate; + // Try calculating an appropriate default buffer size. + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + } // The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if // it should be done before or after format/channels/rate. // // OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual // value. - mal_uint32 fragmentSizeInBytes = mal_round_to_power_of_2(pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat)); + mal_uint32 fragmentSizeInBytes = mal_round_to_power_of_2(pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat)); if (fragmentSizeInBytes < 16) { fragmentSizeInBytes = 16; } @@ -6956,36 +17446,37 @@ static mal_result mal_device_init__oss(mal_context* pContext, mal_device_type ty } int ossFragment = (int)((pDevice->periods << 16) | ossFragmentSizePower); - result = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); - if (result == -1) { + ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); + if (ossResult == -1) { close(pDevice->oss.fd); - return mal_post_error(pDevice, "[OSS] Failed to set fragment size and period count.", MAL_FORMAT_NOT_SUPPORTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.", MAL_FORMAT_NOT_SUPPORTED); } int actualFragmentSizeInBytes = 1 << (ossFragment & 0xFFFF); - pDevice->oss.fragmentSizeInFrames = actualFragmentSizeInBytes / mal_get_sample_size_in_bytes(pDevice->internalFormat) / pDevice->internalChannels; + pDevice->oss.fragmentSizeInFrames = actualFragmentSizeInBytes / mal_get_bytes_per_sample(pDevice->internalFormat) / pDevice->internalChannels; pDevice->periods = (mal_uint32)(ossFragment >> 16); pDevice->bufferSizeInFrames = (mal_uint32)(pDevice->oss.fragmentSizeInFrames * pDevice->periods); + // Set the internal channel map. Not sure if this can be queried. For now just using the channel layouts defined in FreeBSD's sound(4) man page. + mal_get_standard_channel_map(mal_standard_channel_map_sound4, pDevice->internalChannels, pDevice->internalChannelMap); - // Set the internal channel map. Not sure if this can be queried. For now just using our default assumptions. - mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); - + // OSS seems to be shared. + pDevice->exclusiveMode = MAL_FALSE; // When not using MMAP mode, we need to use an intermediary buffer for the client <-> device transfer. We do // everything by the size of a fragment. pDevice->oss.pIntermediaryBuffer = mal_malloc(actualFragmentSizeInBytes); if (pDevice->oss.pIntermediaryBuffer == NULL) { close(pDevice->oss.fd); - return mal_post_error(pDevice, "[OSS] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); } return MAL_SUCCESS; } -static mal_result mal_device__start_backend__oss(mal_device* pDevice) +mal_result mal_device__start_backend__oss(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -6997,9 +17488,9 @@ static mal_result mal_device__start_backend__oss(mal_device* pDevice) // Playback. mal_device__read_frames_from_client(pDevice, pDevice->oss.fragmentSizeInFrames, pDevice->oss.pIntermediaryBuffer); - int bytesWritten = write(pDevice->oss.fd, pDevice->oss.pIntermediaryBuffer, pDevice->oss.fragmentSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat)); + int bytesWritten = write(pDevice->oss.fd, pDevice->oss.pIntermediaryBuffer, pDevice->oss.fragmentSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat)); if (bytesWritten == -1) { - return mal_post_error(pDevice, "[OSS] Failed to send initial chunk of data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to send initial chunk of data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); } } else { // Capture. Do nothing. @@ -7008,7 +17499,7 @@ static mal_result mal_device__start_backend__oss(mal_device* pDevice) return MAL_SUCCESS; } -static mal_result mal_device__stop_backend__oss(mal_device* pDevice) +mal_result mal_device__stop_backend__oss(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -7025,13 +17516,13 @@ static mal_result mal_device__stop_backend__oss(mal_device* pDevice) int result = ioctl(pDevice->oss.fd, SNDCTL_DSP_HALT, 0); if (result == -1) { - return mal_post_error(pDevice, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); } return MAL_SUCCESS; } -static mal_result mal_device__break_main_loop__oss(mal_device* pDevice) +mal_result mal_device__break_main_loop__oss(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -7039,7 +17530,7 @@ static mal_result mal_device__break_main_loop__oss(mal_device* pDevice) return MAL_SUCCESS; } -static mal_result mal_device__main_loop__oss(mal_device* pDevice) +mal_result mal_device__main_loop__oss(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -7055,24 +17546,69 @@ static mal_result mal_device__main_loop__oss(mal_device* pDevice) // Playback. mal_device__read_frames_from_client(pDevice, pDevice->oss.fragmentSizeInFrames, pDevice->oss.pIntermediaryBuffer); - int bytesWritten = write(pDevice->oss.fd, pDevice->oss.pIntermediaryBuffer, pDevice->oss.fragmentSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat)); + int bytesWritten = write(pDevice->oss.fd, pDevice->oss.pIntermediaryBuffer, pDevice->oss.fragmentSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat)); if (bytesWritten < 0) { - return mal_post_error(pDevice, "[OSS] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); } } else { // Capture. - int bytesRead = read(pDevice->oss.fd, pDevice->oss.pIntermediaryBuffer, pDevice->oss.fragmentSizeInFrames * mal_get_sample_size_in_bytes(pDevice->internalFormat)); + int bytesRead = read(pDevice->oss.fd, pDevice->oss.pIntermediaryBuffer, pDevice->oss.fragmentSizeInFrames * mal_get_bytes_per_sample(pDevice->internalFormat)); if (bytesRead < 0) { - return mal_post_error(pDevice, "[OSS] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); } - mal_uint32 framesRead = (mal_uint32)bytesRead / pDevice->internalChannels / mal_get_sample_size_in_bytes(pDevice->internalFormat); + mal_uint32 framesRead = (mal_uint32)bytesRead / pDevice->internalChannels / mal_get_bytes_per_sample(pDevice->internalFormat); mal_device__send_frames_to_client(pDevice, framesRead, pDevice->oss.pIntermediaryBuffer); } } return MAL_SUCCESS; } + +mal_result mal_context_uninit__oss(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_oss); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_init__oss(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + // Try opening a temporary device first so we can get version information. This is closed at the end. + int fd = mal_open_temp_device__oss(); + if (fd == -1) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties.", MAL_NO_BACKEND); // Looks liks OSS isn't installed, or there are no available devices. + } + + // Grab the OSS version. + int ossVersion = 0; + int result = ioctl(fd, OSS_GETVERSION, &ossVersion); + if (result == -1) { + close(fd); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.", MAL_NO_BACKEND); + } + + pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); + pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); + + pContext->onUninit = mal_context_uninit__oss; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__oss; + pContext->onEnumDevices = mal_context_enumerate_devices__oss; + pContext->onGetDeviceInfo = mal_context_get_device_info__oss; + pContext->onDeviceInit = mal_device_init__oss; + pContext->onDeviceUninit = mal_device_uninit__oss; + pContext->onDeviceStart = mal_device__start_backend__oss; + pContext->onDeviceStop = mal_device__stop_backend__oss; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__oss; + pContext->onDeviceMainLoop = mal_device__main_loop__oss; + + close(fd); + return MAL_SUCCESS; +} #endif // OSS @@ -7087,8 +17623,24 @@ static mal_result mal_device__main_loop__oss(mal_device* pDevice) #include #endif +// OpenSL|ES has one-per-application objects :( +SLObjectItf g_malEngineObjectSL = NULL; +SLEngineItf g_malEngineSL = NULL; +mal_uint32 g_malOpenSLInitCounter = 0; + +#define MAL_OPENSL_OBJ(p) (*((SLObjectItf)(p))) +#define MAL_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) +#define MAL_OPENSL_PLAY(p) (*((SLPlayItf)(p))) +#define MAL_OPENSL_RECORD(p) (*((SLRecordItf)(p))) + +#ifdef MAL_ANDROID +#define MAL_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) +#else +#define MAL_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) +#endif + // Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to mini_al. -static mal_uint8 mal_channel_id_to_mal__opensl(SLuint32 id) +mal_uint8 mal_channel_id_to_mal__opensl(SLuint32 id) { switch (id) { @@ -7115,10 +17667,11 @@ static mal_uint8 mal_channel_id_to_mal__opensl(SLuint32 id) } // Converts an individual mini_al channel identifier (MAL_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. -static SLuint32 mal_channel_id_to_opensl(mal_uint8 id) +SLuint32 mal_channel_id_to_opensl(mal_uint8 id) { switch (id) { + case MAL_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; case MAL_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; case MAL_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; case MAL_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; @@ -7142,7 +17695,7 @@ static SLuint32 mal_channel_id_to_opensl(mal_uint8 id) } // Converts a channel mapping to an OpenSL-style channel mask. -static SLuint32 mal_channel_map_to_channel_mask__opensl(const mal_uint8 channelMap[MAL_MAX_CHANNELS], mal_uint32 channels) +SLuint32 mal_channel_map_to_channel_mask__opensl(const mal_channel channelMap[MAL_MAX_CHANNELS], mal_uint32 channels) { SLuint32 channelMask = 0; for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { @@ -7153,20 +17706,26 @@ static SLuint32 mal_channel_map_to_channel_mask__opensl(const mal_uint8 channelM } // Converts an OpenSL-style channel mask to a mini_al channel map. -static void mal_channel_mask_to_channel_map__opensl(SLuint32 channelMask, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +void mal_channel_mask_to_channel_map__opensl(SLuint32 channelMask, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) { - if (channels == 2 && channelMask == 0) { + if (channels == 1 && channelMask == 0) { + channelMap[0] = MAL_CHANNEL_MONO; + } else if (channels == 2 && channelMask == 0) { channelMap[0] = MAL_CHANNEL_FRONT_LEFT; channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; } else { - // Just iterate over each bit. - mal_uint32 iChannel = 0; - for (mal_uint32 iBit = 0; iBit < 32; ++iBit) { - SLuint32 bitValue = (channelMask & (1 << iBit)); - if (bitValue != 0) { - // The bit is set. - channelMap[iChannel] = mal_channel_id_to_mal__opensl(bitValue); - iChannel += 1; + if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { + channelMap[0] = MAL_CHANNEL_MONO; + } else { + // Just iterate over each bit. + mal_uint32 iChannel = 0; + for (mal_uint32 iBit = 0; iBit < 32; ++iBit) { + SLuint32 bitValue = (channelMask & (1UL << iBit)); + if (bitValue != 0) { + // The bit is set. + channelMap[iChannel] = mal_channel_id_to_mal__opensl(bitValue); + iChannel += 1; + } } } } @@ -7221,147 +17780,198 @@ SLuint32 mal_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) return SL_SAMPLINGRATE_16; } -mal_result mal_context_init__opensl(mal_context* pContext) + +mal_bool32 mal_context_is_device_id_equal__opensl(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) { mal_assert(pContext != NULL); - + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); (void)pContext; - return MAL_SUCCESS; + + return pID0->opensl == pID1->opensl; } -mal_result mal_context_uninit__opensl(mal_context* pContext) +mal_result mal_context_enumerate_devices__opensl(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) { mal_assert(pContext != NULL); - mal_assert(pContext->backend == mal_backend_opensl); - - (void)pContext; - return MAL_SUCCESS; -} - -mal_result mal_enumerate_devices__opensl(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) -{ - (void)pContext; - - mal_uint32 infoSize = *pCount; - *pCount = 0; - - SLObjectItf engineObj; - SLresult resultSL = slCreateEngine(&engineObj, 0, NULL, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - return MAL_NO_BACKEND; - } - - (*engineObj)->Realize(engineObj, SL_BOOLEAN_FALSE); + mal_assert(callback != NULL); // TODO: Test Me. // // This is currently untested, so for now we are just returning default devices. -#if 0 +#if 0 && !defined(MAL_ANDROID) + mal_bool32 isTerminated = MAL_FALSE; + SLuint32 pDeviceIDs[128]; SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); SLAudioIODeviceCapabilitiesItf deviceCaps; - resultSL = (*engineObj)->GetInterface(engineObj, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + SLresult resultSL = (*g_malEngineObjectSL)->GetInterface(g_malEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); if (resultSL != SL_RESULT_SUCCESS) { // The interface may not be supported so just report a default device. - (*engineObj)->Destroy(engineObj); goto return_default_device; } - if (type == mal_device_type_playback) { + // Playback + if (!isTerminated) { resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); if (resultSL != SL_RESULT_SUCCESS) { - (*engineObj)->Destroy(engineObj); return MAL_NO_DEVICE; } - } else { - resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - (*engineObj)->Destroy(engineObj); - return MAL_NO_DEVICE; - } - } - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - if (pInfo != NULL) { - if (infoSize > 0) { - mal_zero_object(pInfo); - pInfo->id.opensl = pDeviceIDs[iDevice]; + for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + deviceInfo.id.opensl = pDeviceIDs[iDevice]; - mal_bool32 isValidDevice = MAL_TRUE; - if (type == mal_device_type_playback) { - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pInfo->id.opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - isValidDevice = MAL_FALSE; - } + SLAudioOutputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); + if (resultSL == SL_RESULT_SUCCESS) { + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), (const char*)desc.pDeviceName, (size_t)-1); - } else { - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pInfo->id.opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - isValidDevice = MAL_FALSE; - } - - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), (const char*)desc.deviceName, (size_t)-1); - } - - if (isValidDevice) { - pInfo += 1; - infoSize -= 1; - *pCount += 1; + mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + isTerminated = MAL_TRUE; + break; + } + } + } + } + + // Capture + if (!isTerminated) { + resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); + if (resultSL != SL_RESULT_SUCCESS) { + return MAL_NO_DEVICE; + } + + for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + deviceInfo.id.opensl = pDeviceIDs[iDevice]; + + SLAudioInputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); + if (resultSL == SL_RESULT_SUCCESS) { + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); + + mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + isTerminated = MAL_TRUE; + break; } } - } else { - *pCount += 1; } } - (*engineObj)->Destroy(engineObj); return MAL_SUCCESS; #else - (*engineObj)->Destroy(engineObj); goto return_default_device; #endif -return_default_device: - *pCount = 1; - if (pInfo != NULL) { - if (infoSize > 0) { - if (type == mal_device_type_playback) { - pInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1); - } else { - pInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1); - } - } +return_default_device:; + mal_bool32 cbResult = MAL_TRUE; + + // Playback. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + + // Capture. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); } return MAL_SUCCESS; } +mal_result mal_context_get_device_info__opensl(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; -// OpenSL|ES has one-per-application objects :( -static SLObjectItf g_malEngineObjectSL = NULL; -static SLEngineItf g_malEngineSL = NULL; -static mal_uint32 g_malOpenSLInitCounter = 0; + // TODO: Test Me. + // + // This is currently untested, so for now we are just returning default devices. +#if 0 && !defined(MAL_ANDROID) + SLAudioIODeviceCapabilitiesItf deviceCaps; + SLresult resultSL = (*g_malEngineObjectSL)->GetInterface(g_malEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + if (resultSL != SL_RESULT_SUCCESS) { + // The interface may not be supported so just report a default device. + goto return_default_device; + } -#define MAL_OPENSL_OBJ(p) (*((SLObjectItf)(p))) -#define MAL_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) -#define MAL_OPENSL_PLAY(p) (*((SLPlayItf)(p))) -#define MAL_OPENSL_RECORD(p) (*((SLRecordItf)(p))) + if (deviceType == mal_device_type_playback) { + SLAudioOutputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); + if (resultSL != SL_RESULT_SUCCESS) { + return MAL_NO_DEVICE; + } -#ifdef MAL_ANDROID -#define MAL_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); + } else { + SLAudioInputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); + if (resultSL != SL_RESULT_SUCCESS) { + return MAL_NO_DEVICE; + } + + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); + } + + goto return_detailed_info; #else -#define MAL_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) + goto return_default_device; #endif +return_default_device: + if (pDeviceID != NULL) { + if ((deviceType == mal_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || + (deviceType == mal_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { + return MAL_NO_DEVICE; // Don't know the device. + } + } + + // Name / Description + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + goto return_detailed_info; + + +return_detailed_info: + + // For now we're just outputting a set of values that are supported by the API but not necessarily supported + // by the device natively. Later on we should work on this so that it more closely reflects the device's + // actual native format. + pDeviceInfo->minChannels = 1; + pDeviceInfo->maxChannels = 2; + pDeviceInfo->minSampleRate = 8000; + pDeviceInfo->maxSampleRate = 48000; + pDeviceInfo->formatCount = 2; + pDeviceInfo->formats[0] = mal_format_u8; + pDeviceInfo->formats[1] = mal_format_s16; +#if defined(MAL_ANDROID) && __ANDROID_API__ >= 21 + pDeviceInfo->formats[pDeviceInfo->formatCount] = mal_format_f32; + pDeviceInfo->formatCount += 1; +#endif + + return MAL_SUCCESS; +} + + #ifdef MAL_ANDROID -//static void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext) -static void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) +//void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext) +void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) { (void)pBufferQueue; @@ -7382,7 +17992,7 @@ static void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueue return; } - size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); mal_uint8* pBuffer = pDevice->opensl.pBuffer + (pDevice->opensl.currentBufferIndex * periodSizeInBytes); if (pDevice->type == mal_device_type_playback) { @@ -7409,7 +18019,7 @@ static void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueue } #endif -static void mal_device_uninit__opensl(mal_device* pDevice) +void mal_device_uninit__opensl(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -7422,17 +18032,9 @@ static void mal_device_uninit__opensl(mal_device* pDevice) } mal_free(pDevice->opensl.pBuffer); - - - // Uninit global data. - if (g_malOpenSLInitCounter > 0) { - if (mal_atomic_decrement_32(&g_malOpenSLInitCounter) == 0) { - (*g_malEngineObjectSL)->Destroy(g_malEngineObjectSL); - } - } } -static mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) { (void)pContext; @@ -7448,33 +18050,10 @@ static mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type pDevice->internalFormat = mal_format_s32; } - // Initialize global data first if applicable. - if (mal_atomic_increment_32(&g_malOpenSLInitCounter) == 1) { - SLresult resultSL = slCreateEngine(&g_malEngineObjectSL, 0, NULL, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - mal_atomic_decrement_32(&g_malOpenSLInitCounter); - return mal_post_error(pDevice, "[OpenSL] slCreateEngine() failed.", MAL_NO_BACKEND); - } - - (*g_malEngineObjectSL)->Realize(g_malEngineObjectSL, SL_BOOLEAN_FALSE); - - resultSL = (*g_malEngineObjectSL)->GetInterface(g_malEngineObjectSL, SL_IID_ENGINE, &g_malEngineSL); - if (resultSL != SL_RESULT_SUCCESS) { - (*g_malEngineObjectSL)->Destroy(g_malEngineObjectSL); - mal_atomic_decrement_32(&g_malOpenSLInitCounter); - return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_ENGINE interface.", MAL_NO_BACKEND); - } - } - - // Now we can start initializing the device properly. mal_assert(pDevice != NULL); mal_zero_object(&pDevice->opensl); - pDevice->opensl.currentBufferIndex = 0; - pDevice->opensl.periodSizeInFrames = pDevice->bufferSizeInFrames / pConfig->periods; - pDevice->bufferSizeInFrames = pDevice->opensl.periodSizeInFrames * pConfig->periods; - SLDataLocator_AndroidSimpleBufferQueue queue; queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; queue.numBuffers = pConfig->periods; @@ -7498,15 +18077,15 @@ static mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type pFormat->numChannels = pDevice->channels; pFormat->samplesPerSec = mal_round_to_standard_sample_rate__opensl(pDevice->sampleRate * 1000); // In millihertz. - pFormat->bitsPerSample = mal_get_sample_size_in_bytes(pDevice->format)*8; + pFormat->bitsPerSample = mal_get_bytes_per_sample(pDevice->format)*8; pFormat->containerSize = pFormat->bitsPerSample; // Always tightly packed for now. pFormat->channelMask = mal_channel_map_to_channel_mask__opensl(pConfig->channelMap, pFormat->numChannels); - pFormat->endianness = SL_BYTEORDER_LITTLEENDIAN; + pFormat->endianness = (mal_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; // Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html // - Only mono and stereo is supported. // - Only u8 and s16 formats are supported. - // - Limited to a sample rate of 48000. + // - Maximum sample rate of 48000. #ifdef MAL_ANDROID if (pFormat->numChannels > 2) { pFormat->numChannels = 2; @@ -7539,22 +18118,23 @@ static mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type SLresult resultSL = (*g_malEngineSL)->CreateOutputMix(g_malEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); if (resultSL != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to create output mix.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE)) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to realize output mix object.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix) != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } // Set the output device. if (pDeviceID != NULL) { - MAL_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &pDeviceID->opensl); + SLuint32 deviceID_OpenSL = pDeviceID->opensl; + MAL_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); } SLDataSource source; @@ -7585,28 +18165,28 @@ static mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type if (resultSL != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to create audio player.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to realize audio player.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_PLAY, &pDevice->opensl.pAudioPlayer) != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueue) != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, mal_buffer_queue_callback__opensl_android, pDevice) != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to register buffer queue callback.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } } else { SLDataLocator_IODevice locatorDevice; @@ -7639,27 +18219,27 @@ static mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type if (resultSL != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to create audio recorder.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to realize audio recorder.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_RECORD, &pDevice->opensl.pAudioRecorder) != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueue) != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } if (MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, mal_buffer_queue_callback__opensl_android, pDevice) != SL_RESULT_SUCCESS) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to register buffer queue callback.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } } @@ -7697,12 +18277,20 @@ static mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type pDevice->internalSampleRate = pFormat->samplesPerSec / 1000; mal_channel_mask_to_channel_map__opensl(pFormat->channelMask, pDevice->internalChannels, pDevice->internalChannelMap); + // Try calculating an appropriate default buffer size. + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + } - size_t bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + pDevice->opensl.currentBufferIndex = 0; + pDevice->opensl.periodSizeInFrames = pDevice->bufferSizeInFrames / pConfig->periods; + pDevice->bufferSizeInFrames = pDevice->opensl.periodSizeInFrames * pConfig->periods; + + size_t bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); pDevice->opensl.pBuffer = (mal_uint8*)mal_malloc(bufferSizeInBytes); if (pDevice->opensl.pBuffer == NULL) { mal_device_uninit__opensl(pDevice); - return mal_post_error(pDevice, "[OpenSL] Failed to allocate memory for data buffer.", MAL_OUT_OF_MEMORY); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MAL_OUT_OF_MEMORY); } mal_zero_memory(pDevice->opensl.pBuffer, bufferSizeInBytes); @@ -7710,39 +18298,39 @@ static mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type return MAL_SUCCESS; } -static mal_result mal_device__start_backend__opensl(mal_device* pDevice) +mal_result mal_device__start_backend__opensl(mal_device* pDevice) { mal_assert(pDevice != NULL); if (pDevice->type == mal_device_type_playback) { SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); if (resultSL != SL_RESULT_SUCCESS) { - return mal_post_error(pDevice, "[OpenSL] Failed to start internal playback device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } // We need to enqueue a buffer for each period. mal_device__read_frames_from_client(pDevice, pDevice->bufferSizeInFrames, pDevice->opensl.pBuffer); - size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; ++iPeriod) { resultSL = MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, pDevice->opensl.pBuffer + (periodSizeInBytes * iPeriod), periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - return mal_post_error(pDevice, "[OpenSL] Failed to enqueue buffer for playback device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } } } else { SLresult resultSL = MAL_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); if (resultSL != SL_RESULT_SUCCESS) { - return mal_post_error(pDevice, "[OpenSL] Failed to start internal capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } - size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; ++iPeriod) { resultSL = MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, pDevice->opensl.pBuffer + (periodSizeInBytes * iPeriod), periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { MAL_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - return mal_post_error(pDevice, "[OpenSL] Failed to enqueue buffer for capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); } } } @@ -7750,19 +18338,19 @@ static mal_result mal_device__start_backend__opensl(mal_device* pDevice) return MAL_SUCCESS; } -static mal_result mal_device__stop_backend__opensl(mal_device* pDevice) +mal_result mal_device__stop_backend__opensl(mal_device* pDevice) { mal_assert(pDevice != NULL); if (pDevice->type == mal_device_type_playback) { SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); if (resultSL != SL_RESULT_SUCCESS) { - return mal_post_error(pDevice, "[OpenSL] Failed to stop internal playback device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); } } else { SLresult resultSL = MAL_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); if (resultSL != SL_RESULT_SUCCESS) { - return mal_post_error(pDevice, "[OpenSL] Failed to stop internal capture device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); } } @@ -7771,12 +18359,67 @@ static mal_result mal_device__stop_backend__opensl(mal_device* pDevice) // Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. mal_device__set_state(pDevice, MAL_STATE_STOPPED); - if (pDevice->onStop) { - pDevice->onStop(pDevice); + mal_stop_proc onStop = pDevice->onStop; + if (onStop) { + onStop(pDevice); } return MAL_SUCCESS; } + + +mal_result mal_context_uninit__opensl(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_opensl); + (void)pContext; + + // Uninit global data. + if (g_malOpenSLInitCounter > 0) { + if (mal_atomic_decrement_32(&g_malOpenSLInitCounter) == 0) { + (*g_malEngineObjectSL)->Destroy(g_malEngineObjectSL); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_init__opensl(mal_context* pContext) +{ + mal_assert(pContext != NULL); + (void)pContext; + + // Initialize global data first if applicable. + if (mal_atomic_increment_32(&g_malOpenSLInitCounter) == 1) { + SLresult resultSL = slCreateEngine(&g_malEngineObjectSL, 0, NULL, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + mal_atomic_decrement_32(&g_malOpenSLInitCounter); + return MAL_NO_BACKEND; + } + + (*g_malEngineObjectSL)->Realize(g_malEngineObjectSL, SL_BOOLEAN_FALSE); + + resultSL = (*g_malEngineObjectSL)->GetInterface(g_malEngineObjectSL, SL_IID_ENGINE, &g_malEngineSL); + if (resultSL != SL_RESULT_SUCCESS) { + (*g_malEngineObjectSL)->Destroy(g_malEngineObjectSL); + mal_atomic_decrement_32(&g_malOpenSLInitCounter); + return MAL_NO_BACKEND; + } + } + + pContext->isBackendAsynchronous = MAL_TRUE; + + pContext->onUninit = mal_context_uninit__opensl; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__opensl; + pContext->onEnumDevices = mal_context_enumerate_devices__opensl; + pContext->onGetDeviceInfo = mal_context_get_device_info__opensl; + pContext->onDeviceInit = mal_device_init__opensl; + pContext->onDeviceUninit = mal_device_uninit__opensl; + pContext->onDeviceStart = mal_device__start_backend__opensl; + pContext->onDeviceStop = mal_device__stop_backend__opensl; + + return MAL_SUCCESS; +} #endif // OpenSL|ES /////////////////////////////////////////////////////////////////////////////// @@ -7943,35 +18586,633 @@ typedef void (MAL_AL_APIENTRY * MAL_LPALGETBUFFERI) (mal_ typedef void (MAL_AL_APIENTRY * MAL_LPALGETBUFFER3I) (mal_ALuint buffer, mal_ALenum param, mal_ALint *value1, mal_ALint *value2, mal_ALint *value3); typedef void (MAL_AL_APIENTRY * MAL_LPALGETBUFFERIV) (mal_ALuint buffer, mal_ALenum param, mal_ALint *values); +mal_bool32 mal_context_is_device_id_equal__openal(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return mal_strcmp(pID0->openal, pID1->openal) == 0; +} + +mal_result mal_context_enumerate_devices__openal(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + if (pContext->openal.isEnumerationSupported) { + mal_bool32 isTerminated = MAL_FALSE; + + // Playback + if (!isTerminated) { + const mal_ALCchar* pPlaybackDeviceNames = ((MAL_LPALCGETSTRING)pContext->openal.alcGetString)(NULL, MAL_ALC_DEVICE_SPECIFIER); + if (pPlaybackDeviceNames == NULL) { + return MAL_NO_DEVICE; + } + + // Each device is stored in pDeviceNames, separated by a null-terminator. The string itself is double-null-terminated. + const mal_ALCchar* pNextPlaybackDeviceName = pPlaybackDeviceNames; + while (pNextPlaybackDeviceName[0] != '\0') { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.id.openal, sizeof(deviceInfo.id.openal), (const char*)pNextPlaybackDeviceName, (size_t)-1); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)pNextPlaybackDeviceName, (size_t)-1); + + mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + isTerminated = MAL_TRUE; + break; + } + + // Move to the next device name. + while (*pNextPlaybackDeviceName != '\0') { + pNextPlaybackDeviceName += 1; + } + + // Skip past the null terminator. + pNextPlaybackDeviceName += 1; + }; + } + + // Capture + if (!isTerminated) { + const mal_ALCchar* pCaptureDeviceNames = ((MAL_LPALCGETSTRING)pContext->openal.alcGetString)(NULL, MAL_ALC_CAPTURE_DEVICE_SPECIFIER); + if (pCaptureDeviceNames == NULL) { + return MAL_NO_DEVICE; + } + + const mal_ALCchar* pNextCaptureDeviceName = pCaptureDeviceNames; + while (pNextCaptureDeviceName[0] != '\0') { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.id.openal, sizeof(deviceInfo.id.openal), (const char*)pNextCaptureDeviceName, (size_t)-1); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)pNextCaptureDeviceName, (size_t)-1); + + mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + isTerminated = MAL_TRUE; + break; + } + + // Move to the next device name. + while (*pNextCaptureDeviceName != '\0') { + pNextCaptureDeviceName += 1; + } + + // Skip past the null terminator. + pNextCaptureDeviceName += 1; + }; + } + } else { + // Enumeration is not supported. Use default devices. + mal_bool32 cbResult = MAL_TRUE; + + // Playback. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + + // Capture. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } + } + + return MAL_SUCCESS; +} + + +typedef struct +{ + mal_device_type deviceType; + const mal_device_id* pDeviceID; + mal_share_mode shareMode; + mal_device_info* pDeviceInfo; + mal_bool32 foundDevice; +} mal_context_get_device_info_enum_callback_data__openal; + +mal_bool32 mal_context_get_device_info_enum_callback__openal(mal_context* pContext, mal_device_type deviceType, const mal_device_info* pDeviceInfo, void* pUserData) +{ + mal_context_get_device_info_enum_callback_data__openal* pData = (mal_context_get_device_info_enum_callback_data__openal*)pUserData; + mal_assert(pData != NULL); + + if (pData->deviceType == deviceType && mal_context_is_device_id_equal__openal(pContext, pData->pDeviceID, &pDeviceInfo->id)) { + mal_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); + pData->foundDevice = MAL_TRUE; + } + + // Keep enumerating until we have found the device. + return !pData->foundDevice; +} + +mal_result mal_context_get_device_info__openal(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + + // Name / Description + if (pDeviceID == NULL) { + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + return MAL_SUCCESS; + } else { + mal_context_get_device_info_enum_callback_data__openal data; + data.deviceType = deviceType; + data.pDeviceID = pDeviceID; + data.shareMode = shareMode; + data.pDeviceInfo = pDeviceInfo; + data.foundDevice = MAL_FALSE; + mal_result result = mal_context_enumerate_devices__openal(pContext, mal_context_get_device_info_enum_callback__openal, &data); + if (result != MAL_SUCCESS) { + return result; + } + + if (!data.foundDevice) { + return MAL_NO_DEVICE; + } + } + + // mini_al's OpenAL backend only supports: + // - mono and stereo + // - u8, s16 and f32 + // - All standard sample rates + pDeviceInfo->minChannels = 1; + pDeviceInfo->maxChannels = 2; + pDeviceInfo->minSampleRate = MAL_MIN_SAMPLE_RATE; + pDeviceInfo->maxSampleRate = MAL_MAX_SAMPLE_RATE; + pDeviceInfo->formatCount = 2; + pDeviceInfo->formats[0] = mal_format_u8; + pDeviceInfo->formats[1] = mal_format_s16; + if (pContext->openal.isFloat32Supported) { + pDeviceInfo->formats[pDeviceInfo->formatCount] = mal_format_f32; + pDeviceInfo->formatCount += 1; + } + + return MAL_SUCCESS; +} + + +void mal_device_uninit__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // Delete buffers and source first. + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); + if (pDevice->openal.sourceAL != 0) { + ((MAL_LPALDELETESOURCES)pDevice->pContext->openal.alDeleteSources)(1, (const mal_ALuint*)&pDevice->openal.sourceAL); + } + if (pDevice->periods > 0 && pDevice->openal.buffersAL[0] != 0) { + ((MAL_LPALDELETEBUFFERS)pDevice->pContext->openal.alDeleteBuffers)(pDevice->periods, (const mal_ALuint*)pDevice->openal.buffersAL); + } + + + // Now that resources have been deleted we can destroy the OpenAL context and close the device. + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)(NULL); + ((MAL_LPALCDESTROYCONTEXT)pDevice->pContext->openal.alcDestroyContext)((mal_ALCcontext*)pDevice->openal.pContextALC); + + if (pDevice->type == mal_device_type_playback) { + ((MAL_LPALCCLOSEDEVICE)pDevice->pContext->openal.alcCloseDevice)((mal_ALCdevice*)pDevice->openal.pDeviceALC); + } else { + ((MAL_LPALCCAPTURECLOSEDEVICE)pDevice->pContext->openal.alcCaptureCloseDevice)((mal_ALCdevice*)pDevice->openal.pDeviceALC); + } + + mal_free(pDevice->openal.pIntermediaryBuffer); +} + +mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + if (pDevice->periods > MAL_MAX_PERIODS_OPENAL) { + pDevice->periods = MAL_MAX_PERIODS_OPENAL; + } + + // Try calculating an appropriate default buffer size. + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate); + if (pDevice->usingDefaultBufferSize) { + float bufferSizeScaleFactor = 3; + pDevice->bufferSizeInFrames = mal_scale_buffer_size(pDevice->bufferSizeInFrames, bufferSizeScaleFactor); + } + } + + mal_ALCsizei bufferSizeInSamplesAL = pDevice->bufferSizeInFrames; + mal_ALCuint frequencyAL = pConfig->sampleRate; + + mal_uint32 channelsAL = 0; + + // OpenAL currently only supports only mono and stereo. TODO: Check for the AL_EXT_MCFORMATS extension and use one of those formats for quad, 5.1, etc. + mal_ALCenum formatAL = 0; + if (pConfig->channels == 1) { + // Mono. + channelsAL = 1; + if (pConfig->format == mal_format_f32) { + if (pContext->openal.isFloat32Supported) { + formatAL = MAL_AL_FORMAT_MONO_FLOAT32; + } else { + formatAL = MAL_AL_FORMAT_MONO16; + } + } else if (pConfig->format == mal_format_s32) { + formatAL = MAL_AL_FORMAT_MONO16; + } else if (pConfig->format == mal_format_s24) { + formatAL = MAL_AL_FORMAT_MONO16; + } else if (pConfig->format == mal_format_s16) { + formatAL = MAL_AL_FORMAT_MONO16; + } else if (pConfig->format == mal_format_u8) { + formatAL = MAL_AL_FORMAT_MONO8; + } + } else { + // Stereo. + channelsAL = 2; + if (pConfig->format == mal_format_f32) { + if (pContext->openal.isFloat32Supported) { + formatAL = MAL_AL_FORMAT_STEREO_FLOAT32; + } else { + formatAL = MAL_AL_FORMAT_STEREO16; + } + } else if (pConfig->format == mal_format_s32) { + formatAL = MAL_AL_FORMAT_STEREO16; + } else if (pConfig->format == mal_format_s24) { + formatAL = MAL_AL_FORMAT_STEREO16; + } else if (pConfig->format == mal_format_s16) { + formatAL = MAL_AL_FORMAT_STEREO16; + } else if (pConfig->format == mal_format_u8) { + formatAL = MAL_AL_FORMAT_STEREO8; + } + } + + if (formatAL == 0) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OpenAL] Format not supported.", MAL_FORMAT_NOT_SUPPORTED); + } + + bufferSizeInSamplesAL *= channelsAL; + + + // OpenAL feels a bit unintuitive to me... The global object is a device, and it would appear that each device can have + // many context's... + mal_ALCdevice* pDeviceALC = NULL; + if (type == mal_device_type_playback) { + pDeviceALC = ((MAL_LPALCOPENDEVICE)pContext->openal.alcOpenDevice)((pDeviceID == NULL) ? NULL : pDeviceID->openal); + } else { + pDeviceALC = ((MAL_LPALCCAPTUREOPENDEVICE)pContext->openal.alcCaptureOpenDevice)((pDeviceID == NULL) ? NULL : pDeviceID->openal, frequencyAL, formatAL, bufferSizeInSamplesAL); + } + + if (pDeviceALC == NULL) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OpenAL] Failed to open device.", MAL_FAILED_TO_INIT_BACKEND); + } + + // A context is only required for playback. + mal_ALCcontext* pContextALC = NULL; + if (pDevice->type == mal_device_type_playback) { + pContextALC = ((MAL_LPALCCREATECONTEXT)pContext->openal.alcCreateContext)(pDeviceALC, NULL); + if (pContextALC == NULL) { + ((MAL_LPALCCLOSEDEVICE)pDevice->pContext->openal.alcCloseDevice)(pDeviceALC); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OpenAL] Failed to open OpenAL context.", MAL_FAILED_TO_INIT_BACKEND); + } + + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)(pContextALC); + + mal_ALuint sourceAL; + ((MAL_LPALGENSOURCES)pDevice->pContext->openal.alGenSources)(1, &sourceAL); + pDevice->openal.sourceAL = sourceAL; + + // We create the buffers, but only fill and queue them when the device is started. + mal_ALuint buffersAL[MAL_MAX_PERIODS_OPENAL]; + ((MAL_LPALGENBUFFERS)pDevice->pContext->openal.alGenBuffers)(pDevice->periods, buffersAL); + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + pDevice->openal.buffersAL[i] = buffersAL[i]; + } + } + + pDevice->internalChannels = channelsAL; + pDevice->internalSampleRate = frequencyAL; + + switch (formatAL) + { + case MAL_AL_FORMAT_MONO8: + case MAL_AL_FORMAT_STEREO8: + case MAL_AL_FORMAT_REAR8: + case MAL_AL_FORMAT_QUAD8: + case MAL_AL_FORMAT_51CHN8: + case MAL_AL_FORMAT_61CHN8: + case MAL_AL_FORMAT_71CHN8: + { + pDevice->internalFormat = mal_format_u8; + } break; + + case MAL_AL_FORMAT_MONO16: + case MAL_AL_FORMAT_STEREO16: + case MAL_AL_FORMAT_REAR16: + case MAL_AL_FORMAT_QUAD16: + case MAL_AL_FORMAT_51CHN16: + case MAL_AL_FORMAT_61CHN16: + case MAL_AL_FORMAT_71CHN16: + { + pDevice->internalFormat = mal_format_s16; + } break; + + case MAL_AL_FORMAT_REAR32: + case MAL_AL_FORMAT_QUAD32: + case MAL_AL_FORMAT_51CHN32: + case MAL_AL_FORMAT_61CHN32: + case MAL_AL_FORMAT_71CHN32: + { + pDevice->internalFormat = mal_format_s32; + } break; + + case MAL_AL_FORMAT_MONO_FLOAT32: + case MAL_AL_FORMAT_STEREO_FLOAT32: + { + pDevice->internalFormat = mal_format_f32; + } break; + } + + // From what I can tell, the ordering of channels is fixed for OpenAL. + switch (formatAL) + { + case MAL_AL_FORMAT_MONO8: + case MAL_AL_FORMAT_MONO16: + case MAL_AL_FORMAT_MONO_FLOAT32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case MAL_AL_FORMAT_STEREO8: + case MAL_AL_FORMAT_STEREO16: + case MAL_AL_FORMAT_STEREO_FLOAT32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + } break; + + case MAL_AL_FORMAT_REAR8: + case MAL_AL_FORMAT_REAR16: + case MAL_AL_FORMAT_REAR32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_BACK_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case MAL_AL_FORMAT_QUAD8: + case MAL_AL_FORMAT_QUAD16: + case MAL_AL_FORMAT_QUAD32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + pDevice->internalChannelMap[2] = MAL_CHANNEL_BACK_LEFT; + pDevice->internalChannelMap[3] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case MAL_AL_FORMAT_51CHN8: + case MAL_AL_FORMAT_51CHN16: + case MAL_AL_FORMAT_51CHN32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + pDevice->internalChannelMap[2] = MAL_CHANNEL_FRONT_CENTER; + pDevice->internalChannelMap[3] = MAL_CHANNEL_LFE; + pDevice->internalChannelMap[4] = MAL_CHANNEL_BACK_LEFT; + pDevice->internalChannelMap[5] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case MAL_AL_FORMAT_61CHN8: + case MAL_AL_FORMAT_61CHN16: + case MAL_AL_FORMAT_61CHN32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + pDevice->internalChannelMap[2] = MAL_CHANNEL_FRONT_CENTER; + pDevice->internalChannelMap[3] = MAL_CHANNEL_LFE; + pDevice->internalChannelMap[4] = MAL_CHANNEL_BACK_CENTER; + pDevice->internalChannelMap[5] = MAL_CHANNEL_SIDE_LEFT; + pDevice->internalChannelMap[6] = MAL_CHANNEL_SIDE_RIGHT; + } break; + + case MAL_AL_FORMAT_71CHN8: + case MAL_AL_FORMAT_71CHN16: + case MAL_AL_FORMAT_71CHN32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + pDevice->internalChannelMap[2] = MAL_CHANNEL_FRONT_CENTER; + pDevice->internalChannelMap[3] = MAL_CHANNEL_LFE; + pDevice->internalChannelMap[4] = MAL_CHANNEL_BACK_LEFT; + pDevice->internalChannelMap[5] = MAL_CHANNEL_BACK_RIGHT; + pDevice->internalChannelMap[6] = MAL_CHANNEL_SIDE_LEFT; + pDevice->internalChannelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + } break; + + default: break; + } + + pDevice->openal.pDeviceALC = pDeviceALC; + pDevice->openal.pContextALC = pContextALC; + pDevice->openal.formatAL = formatAL; + pDevice->openal.subBufferSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; + pDevice->openal.pIntermediaryBuffer = (mal_uint8*)mal_malloc(pDevice->openal.subBufferSizeInFrames * channelsAL * mal_get_bytes_per_sample(pDevice->internalFormat)); + if (pDevice->openal.pIntermediaryBuffer == NULL) { + mal_device_uninit__openal(pDevice); + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[OpenAL] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + } + + return MAL_SUCCESS; +} + +mal_result mal_device__start_backend__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + // Playback. + // + // When starting playback we want to ensure each buffer is filled and queued before playing the source. + pDevice->openal.iNextBuffer = 0; + + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); + + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + mal_device__read_frames_from_client(pDevice, pDevice->openal.subBufferSizeInFrames, pDevice->openal.pIntermediaryBuffer); + + mal_ALuint bufferAL = pDevice->openal.buffersAL[i]; + ((MAL_LPALBUFFERDATA)pDevice->pContext->openal.alBufferData)(bufferAL, pDevice->openal.formatAL, pDevice->openal.pIntermediaryBuffer, pDevice->openal.subBufferSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat), pDevice->internalSampleRate); + ((MAL_LPALSOURCEQUEUEBUFFERS)pDevice->pContext->openal.alSourceQueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL); + } + + // Start the source only after filling and queueing each buffer. + ((MAL_LPALSOURCEPLAY)pDevice->pContext->openal.alSourcePlay)(pDevice->openal.sourceAL); + } else { + // Capture. + ((MAL_LPALCCAPTURESTART)pDevice->pContext->openal.alcCaptureStart)((mal_ALCdevice*)pDevice->openal.pDeviceALC); + } + + return MAL_SUCCESS; +} + +mal_result mal_device__stop_backend__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); + ((MAL_LPALSOURCESTOP)pDevice->pContext->openal.alSourceStop)(pDevice->openal.sourceAL); + } else { + ((MAL_LPALCCAPTURESTOP)pDevice->pContext->openal.alcCaptureStop)((mal_ALCdevice*)pDevice->openal.pDeviceALC); + } + + return MAL_SUCCESS; +} + +mal_result mal_device__break_main_loop__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->openal.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +mal_uint32 mal_device__get_available_frames__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); + + mal_ALint processedBufferCount = 0; + ((MAL_LPALGETSOURCEI)pDevice->pContext->openal.alGetSourcei)(pDevice->openal.sourceAL, MAL_AL_BUFFERS_PROCESSED, &processedBufferCount); + + return processedBufferCount * pDevice->openal.subBufferSizeInFrames; + } else { + mal_ALint samplesAvailable = 0; + ((MAL_LPALCGETINTEGERV)pDevice->pContext->openal.alcGetIntegerv)((mal_ALCdevice*)pDevice->openal.pDeviceALC, MAL_ALC_CAPTURE_SAMPLES, 1, &samplesAvailable); + + return samplesAvailable / pDevice->channels; + } +} + +mal_uint32 mal_device__wait_for_frames__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + while (!pDevice->openal.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__get_available_frames__openal(pDevice); + if (framesAvailable > 0) { + return framesAvailable; + } + + mal_sleep(1); + } + + // We'll get here if the loop was terminated. When capturing we want to return whatever is available. For playback we just drop it. + if (pDevice->type == mal_device_type_playback) { + return 0; + } else { + return mal_device__get_available_frames__openal(pDevice); + } +} + +mal_result mal_device__main_loop__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->openal.breakFromMainLoop = MAL_FALSE; + while (!pDevice->openal.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__wait_for_frames__openal(pDevice); + if (framesAvailable == 0) { + continue; + } + + // If it's a playback device, don't bother grabbing more data if the device is being stopped. + if (pDevice->openal.breakFromMainLoop && pDevice->type == mal_device_type_playback) { + return MAL_FALSE; + } + + if (pDevice->type == mal_device_type_playback) { + while (framesAvailable > 0) { + mal_uint32 framesToRead = (framesAvailable > pDevice->openal.subBufferSizeInFrames) ? pDevice->openal.subBufferSizeInFrames : framesAvailable; + + mal_ALuint bufferAL = pDevice->openal.buffersAL[pDevice->openal.iNextBuffer]; + pDevice->openal.iNextBuffer = (pDevice->openal.iNextBuffer + 1) % pDevice->periods; + + mal_device__read_frames_from_client(pDevice, framesToRead, pDevice->openal.pIntermediaryBuffer); + + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); + ((MAL_LPALSOURCEUNQUEUEBUFFERS)pDevice->pContext->openal.alSourceUnqueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL); + ((MAL_LPALBUFFERDATA)pDevice->pContext->openal.alBufferData)(bufferAL, pDevice->openal.formatAL, pDevice->openal.pIntermediaryBuffer, pDevice->openal.subBufferSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat), pDevice->internalSampleRate); + ((MAL_LPALSOURCEQUEUEBUFFERS)pDevice->pContext->openal.alSourceQueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL); + + framesAvailable -= framesToRead; + } + + + // There's a chance the source has stopped playing due to there not being any buffer's queue. Make sure it's restarted. + mal_ALenum state; + ((MAL_LPALGETSOURCEI)pDevice->pContext->openal.alGetSourcei)(pDevice->openal.sourceAL, MAL_AL_SOURCE_STATE, &state); + + if (state != MAL_AL_PLAYING) { + ((MAL_LPALSOURCEPLAY)pDevice->pContext->openal.alSourcePlay)(pDevice->openal.sourceAL); + } + } else { + while (framesAvailable > 0) { + mal_uint32 framesToSend = (framesAvailable > pDevice->openal.subBufferSizeInFrames) ? pDevice->openal.subBufferSizeInFrames : framesAvailable; + ((MAL_LPALCCAPTURESAMPLES)pDevice->pContext->openal.alcCaptureSamples)((mal_ALCdevice*)pDevice->openal.pDeviceALC, pDevice->openal.pIntermediaryBuffer, framesToSend); + + mal_device__send_frames_to_client(pDevice, framesToSend, pDevice->openal.pIntermediaryBuffer); + framesAvailable -= framesToSend; + } + } + } + + return MAL_SUCCESS; +} + + +mal_result mal_context_uninit__openal(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_openal); + +#ifndef MAL_NO_RUNTIME_LINKING + mal_dlclose(pContext->openal.hOpenAL); +#endif + + return MAL_SUCCESS; +} + mal_result mal_context_init__openal(mal_context* pContext) { mal_assert(pContext != NULL); #ifndef MAL_NO_RUNTIME_LINKING - const char* libName = NULL; -#ifdef MAL_WIN32 - libName = "OpenAL32.dll"; + const char* libNames[] = { +#if defined(MAL_WIN32) + "OpenAL32.dll", + "soft_oal.dll" #endif #if defined(MAL_UNIX) && !defined(MAL_APPLE) - libName = "libopenal.so"; + "libopenal.so", + "libopenal.so.1" #endif -#ifdef MAL_APPLE - libName = "OpenAL.framework/OpenAL"; +#if defined(MAL_APPLE) + "OpenAL.framework/OpenAL" #endif - if (libName == NULL) { - return MAL_NO_BACKEND; // Don't know what the library name is called. + }; + + for (size_t i = 0; i < mal_countof(libNames); ++i) { + pContext->openal.hOpenAL = mal_dlopen(libNames[i]); + if (pContext->openal.hOpenAL != NULL) { + break; + } } - - pContext->openal.hOpenAL = mal_dlopen(libName); - -#ifdef MAL_WIN32 - // Special case for Win32 - try "soft_oal.dll" for OpenAL-Soft drop-ins. - if (pContext->openal.hOpenAL == NULL) { - pContext->openal.hOpenAL = mal_dlopen("soft_oal.dll"); - } -#endif - if (pContext->openal.hOpenAL == NULL) { return MAL_FAILED_TO_INIT_BACKEND; } @@ -8137,475 +19378,19 @@ mal_result mal_context_init__openal(mal_context* pContext) // We depend on the ALC_ENUMERATION_EXT extension for enumeration. If this is not supported we fall back to default devices. pContext->openal.isEnumerationSupported = ((MAL_LPALCISEXTENSIONPRESENT)pContext->openal.alcIsExtensionPresent)(NULL, "ALC_ENUMERATION_EXT"); - pContext->openal.isFloat32Supported = ((MAL_LPALISEXTENSIONPRESENT)pContext->openal.alIsExtensionPresent)("AL_EXT_float32"); - pContext->openal.isMCFormatsSupported = ((MAL_LPALISEXTENSIONPRESENT)pContext->openal.alIsExtensionPresent)("AL_EXT_MCFORMATS"); - - return MAL_SUCCESS; -} - -mal_result mal_context_uninit__openal(mal_context* pContext) -{ - mal_assert(pContext != NULL); - mal_assert(pContext->backend == mal_backend_openal); - -#ifndef MAL_NO_RUNTIME_LINKING - mal_dlclose(pContext->openal.hOpenAL); -#endif - - return MAL_SUCCESS; -} - -mal_result mal_enumerate_devices__openal(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) -{ - mal_uint32 infoSize = *pCount; - *pCount = 0; - - if (pContext->openal.isEnumerationSupported) { - const mal_ALCchar* pDeviceNames = ((MAL_LPALCGETSTRING)pContext->openal.alcGetString)(NULL, (type == mal_device_type_playback) ? MAL_ALC_DEVICE_SPECIFIER : MAL_ALC_CAPTURE_DEVICE_SPECIFIER); - if (pDeviceNames == NULL) { - return MAL_NO_DEVICE; - } - - // Each device is stored in pDeviceNames, separated by a null-terminator. The string itself is double-null-terminated. - const mal_ALCchar* pNextDeviceName = pDeviceNames; - while (pNextDeviceName[0] != '\0') { - if (pInfo != NULL) { - if (infoSize > 0) { - mal_strncpy_s(pInfo->id.openal, sizeof(pInfo->id.openal), (const char*)pNextDeviceName, (size_t)-1); - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), (const char*)pNextDeviceName, (size_t)-1); - - pInfo += 1; - infoSize -= 1; - *pCount += 1; - } - } else { - *pCount += 1; - } - - // Move to the next device name. - while (*pNextDeviceName != '\0') { - pNextDeviceName += 1; - } - - // Skip past the null terminator. - pNextDeviceName += 1; - }; - } else { - // Enumeration is not supported. Use default devices. - if (pInfo != NULL) { - if (infoSize > 0) { - if (type == mal_device_type_playback) { - pInfo->id.sdl = 0; - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1); - } else { - pInfo->id.sdl = 0; - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1); - } - - pInfo += 1; - *pCount += 1; - } - } else { - *pCount += 1; - } - } - - return MAL_SUCCESS; -} - -void mal_device_uninit__openal(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)(NULL); - ((MAL_LPALCDESTROYCONTEXT)pDevice->pContext->openal.alcDestroyContext)((mal_ALCcontext*)pDevice->openal.pContextALC); - - if (pDevice->type == mal_device_type_playback) { - ((MAL_LPALCCLOSEDEVICE)pDevice->pContext->openal.alcCloseDevice)((mal_ALCdevice*)pDevice->openal.pDeviceALC); - } else { - ((MAL_LPALCCAPTURECLOSEDEVICE)pDevice->pContext->openal.alcCaptureCloseDevice)((mal_ALCdevice*)pDevice->openal.pDeviceALC); - } - - mal_free(pDevice->openal.pIntermediaryBuffer); -} - -mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) -{ - if (pDevice->periods > MAL_MAX_PERIODS_OPENAL) { - pDevice->periods = MAL_MAX_PERIODS_OPENAL; - } - - // OpenAL has bad latency in my testing :( - if (pDevice->usingDefaultBufferSize) { - pDevice->bufferSizeInFrames *= 4; - } - - mal_ALCsizei bufferSizeInSamplesAL = pDevice->bufferSizeInFrames; - mal_ALCuint frequencyAL = pConfig->sampleRate; - - mal_uint32 channelsAL = 0; - - // OpenAL currently only supports only mono and stereo. TODO: Check for the AL_EXT_MCFORMATS extension and use one of those formats for quad, 5.1, etc. - mal_ALCenum formatAL = 0; - if (pConfig->channels == 1) { - // Mono. - channelsAL = 1; - if (pConfig->format == mal_format_f32) { - if (pContext->openal.isFloat32Supported) { - formatAL = MAL_AL_FORMAT_MONO_FLOAT32; - } else { - formatAL = MAL_AL_FORMAT_MONO16; - } - } else if (pConfig->format == mal_format_s32) { - formatAL = MAL_AL_FORMAT_MONO16; - } else if (pConfig->format == mal_format_s24) { - formatAL = MAL_AL_FORMAT_MONO16; - } else if (pConfig->format == mal_format_s16) { - formatAL = MAL_AL_FORMAT_MONO16; - } else if (pConfig->format == mal_format_u8) { - formatAL = MAL_AL_FORMAT_MONO8; - } - } else { - // Stereo. - channelsAL = 2; - if (pConfig->format == mal_format_f32) { - if (pContext->openal.isFloat32Supported) { - formatAL = MAL_AL_FORMAT_STEREO_FLOAT32; - } else { - formatAL = MAL_AL_FORMAT_STEREO16; - } - } else if (pConfig->format == mal_format_s32) { - formatAL = MAL_AL_FORMAT_STEREO16; - } else if (pConfig->format == mal_format_s24) { - formatAL = MAL_AL_FORMAT_STEREO16; - } else if (pConfig->format == mal_format_s16) { - formatAL = MAL_AL_FORMAT_STEREO16; - } else if (pConfig->format == mal_format_u8) { - formatAL = MAL_AL_FORMAT_STEREO8; - } - } - - if (formatAL == 0) { - return mal_context_post_error(pContext, NULL, "[OpenAL] Format not supported.", MAL_FORMAT_NOT_SUPPORTED); - } - - bufferSizeInSamplesAL *= channelsAL; - - - // OpenAL feels a bit unintuitive to me... The global object is a device, and it would appear that each device can have - // many context's... - mal_ALCdevice* pDeviceALC = NULL; - if (type == mal_device_type_playback) { - pDeviceALC = ((MAL_LPALCOPENDEVICE)pContext->openal.alcOpenDevice)((pDeviceID == NULL) ? NULL : pDeviceID->openal); - } else { - pDeviceALC = ((MAL_LPALCCAPTUREOPENDEVICE)pContext->openal.alcCaptureOpenDevice)((pDeviceID == NULL) ? NULL : pDeviceID->openal, frequencyAL, formatAL, bufferSizeInSamplesAL); - } - - if (pDeviceALC == NULL) { - return mal_context_post_error(pContext, NULL, "[OpenAL] Failed to open device.", MAL_FAILED_TO_INIT_BACKEND); - } - - // A context is only required for playback. - mal_ALCcontext* pContextALC = NULL; - if (pDevice->type == mal_device_type_playback) { - pContextALC = ((MAL_LPALCCREATECONTEXT)pContext->openal.alcCreateContext)(pDeviceALC, NULL); - if (pContextALC == NULL) { - ((MAL_LPALCCLOSEDEVICE)pDevice->pContext->openal.alcCloseDevice)(pDeviceALC); - return mal_context_post_error(pContext, NULL, "[OpenAL] Failed to open OpenAL context.", MAL_FAILED_TO_INIT_BACKEND); - } - - ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)(pContextALC); - - mal_ALuint sourceAL; - ((MAL_LPALGENSOURCES)pDevice->pContext->openal.alGenSources)(1, &sourceAL); - pDevice->openal.sourceAL = sourceAL; - - // We create the buffers, but only fill and queue them when the device is started. - mal_ALuint buffersAL[MAL_MAX_PERIODS_OPENAL]; - ((MAL_LPALGENBUFFERS)pDevice->pContext->openal.alGenBuffers)(pDevice->periods, buffersAL); - for (mal_uint32 i = 0; i < pDevice->periods; ++i) { - pDevice->openal.buffersAL[i] = buffersAL[i]; - } - } - - pDevice->internalChannels = channelsAL; - pDevice->internalSampleRate = frequencyAL; - - // The internal format is a little bit straight with OpenAL. - switch (formatAL) - { - case MAL_AL_FORMAT_MONO8: - case MAL_AL_FORMAT_STEREO8: - case MAL_AL_FORMAT_REAR8: - case MAL_AL_FORMAT_QUAD8: - case MAL_AL_FORMAT_51CHN8: - case MAL_AL_FORMAT_61CHN8: - case MAL_AL_FORMAT_71CHN8: - { - pDevice->internalFormat = mal_format_u8; - } break; - - case MAL_AL_FORMAT_MONO16: - case MAL_AL_FORMAT_STEREO16: - case MAL_AL_FORMAT_REAR16: - case MAL_AL_FORMAT_QUAD16: - case MAL_AL_FORMAT_51CHN16: - case MAL_AL_FORMAT_61CHN16: - case MAL_AL_FORMAT_71CHN16: - { - pDevice->internalFormat = mal_format_s16; - } break; - - case MAL_AL_FORMAT_REAR32: - case MAL_AL_FORMAT_QUAD32: - case MAL_AL_FORMAT_51CHN32: - case MAL_AL_FORMAT_61CHN32: - case MAL_AL_FORMAT_71CHN32: - { - pDevice->internalFormat = mal_format_s32; - } break; - - case MAL_AL_FORMAT_MONO_FLOAT32: - case MAL_AL_FORMAT_STEREO_FLOAT32: - { - pDevice->internalFormat = mal_format_f32; - } break; - } - - // From what I can tell, the ordering of channels is fixed for OpenAL. - switch (formatAL) - { - case MAL_AL_FORMAT_MONO8: - case MAL_AL_FORMAT_MONO16: - case MAL_AL_FORMAT_MONO_FLOAT32: - { - pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_CENTER; - } break; - - case MAL_AL_FORMAT_STEREO8: - case MAL_AL_FORMAT_STEREO16: - case MAL_AL_FORMAT_STEREO_FLOAT32: - { - pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; - pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - } break; - - case MAL_AL_FORMAT_REAR8: - case MAL_AL_FORMAT_REAR16: - case MAL_AL_FORMAT_REAR32: - { - pDevice->internalChannelMap[0] = MAL_CHANNEL_BACK_LEFT; - pDevice->internalChannelMap[1] = MAL_CHANNEL_BACK_RIGHT; - } break; - - case MAL_AL_FORMAT_QUAD8: - case MAL_AL_FORMAT_QUAD16: - case MAL_AL_FORMAT_QUAD32: - { - pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; - pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - pDevice->internalChannelMap[2] = MAL_CHANNEL_BACK_LEFT; - pDevice->internalChannelMap[3] = MAL_CHANNEL_BACK_RIGHT; - } break; - - case MAL_AL_FORMAT_51CHN8: - case MAL_AL_FORMAT_51CHN16: - case MAL_AL_FORMAT_51CHN32: - { - pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; - pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - pDevice->internalChannelMap[2] = MAL_CHANNEL_FRONT_CENTER; - pDevice->internalChannelMap[3] = MAL_CHANNEL_LFE; - pDevice->internalChannelMap[4] = MAL_CHANNEL_BACK_LEFT; - pDevice->internalChannelMap[5] = MAL_CHANNEL_BACK_RIGHT; - } break; - - case MAL_AL_FORMAT_61CHN8: - case MAL_AL_FORMAT_61CHN16: - case MAL_AL_FORMAT_61CHN32: - { - pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; - pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - pDevice->internalChannelMap[2] = MAL_CHANNEL_FRONT_CENTER; - pDevice->internalChannelMap[3] = MAL_CHANNEL_LFE; - pDevice->internalChannelMap[4] = MAL_CHANNEL_BACK_CENTER; - pDevice->internalChannelMap[5] = MAL_CHANNEL_SIDE_LEFT; - pDevice->internalChannelMap[6] = MAL_CHANNEL_SIDE_RIGHT; - } break; - - case MAL_AL_FORMAT_71CHN8: - case MAL_AL_FORMAT_71CHN16: - case MAL_AL_FORMAT_71CHN32: - { - pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; - pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - pDevice->internalChannelMap[2] = MAL_CHANNEL_FRONT_CENTER; - pDevice->internalChannelMap[3] = MAL_CHANNEL_LFE; - pDevice->internalChannelMap[4] = MAL_CHANNEL_BACK_LEFT; - pDevice->internalChannelMap[5] = MAL_CHANNEL_BACK_RIGHT; - pDevice->internalChannelMap[6] = MAL_CHANNEL_SIDE_LEFT; - pDevice->internalChannelMap[7] = MAL_CHANNEL_SIDE_RIGHT; - } break; - - default: break; - } - - pDevice->openal.pDeviceALC = pDeviceALC; - pDevice->openal.pContextALC = pContextALC; - pDevice->openal.formatAL = formatAL; - pDevice->openal.subBufferSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; - pDevice->openal.pIntermediaryBuffer = (mal_uint8*)mal_malloc(pDevice->openal.subBufferSizeInFrames * channelsAL * mal_get_sample_size_in_bytes(pDevice->internalFormat)); - if (pDevice->openal.pIntermediaryBuffer == NULL) { - mal_device_uninit__openal(pDevice); - return mal_context_post_error(pContext, NULL, "[OpenAL] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); - } - - return MAL_SUCCESS; -} - -static mal_result mal_device__start_backend__openal(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - if (pDevice->type == mal_device_type_playback) { - // Playback. - // - // When starting playback we want to ensure each buffer is filled and queued before playing the source. - pDevice->openal.iNextBuffer = 0; - - ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); - - for (mal_uint32 i = 0; i < pDevice->periods; ++i) { - mal_device__read_frames_from_client(pDevice, pDevice->openal.subBufferSizeInFrames, pDevice->openal.pIntermediaryBuffer); - - mal_ALuint bufferAL = pDevice->openal.buffersAL[i]; - ((MAL_LPALBUFFERDATA)pDevice->pContext->openal.alBufferData)(bufferAL, pDevice->openal.formatAL, pDevice->openal.pIntermediaryBuffer, pDevice->openal.subBufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat), pDevice->internalSampleRate); - ((MAL_LPALSOURCEQUEUEBUFFERS)pDevice->pContext->openal.alSourceQueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL); - } - - // Start the source only after filling and queueing each buffer. - ((MAL_LPALSOURCEPLAY)pDevice->pContext->openal.alSourcePlay)(pDevice->openal.sourceAL); - } else { - // Capture. - ((MAL_LPALCCAPTURESTART)pDevice->pContext->openal.alcCaptureStart)((mal_ALCdevice*)pDevice->openal.pDeviceALC); - } - - return MAL_SUCCESS; -} - -static mal_result mal_device__stop_backend__openal(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - if (pDevice->type == mal_device_type_playback) { - ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); - ((MAL_LPALSOURCESTOP)pDevice->pContext->openal.alSourceStop)(pDevice->openal.sourceAL); - } else { - ((MAL_LPALCCAPTURESTOP)pDevice->pContext->openal.alcCaptureStop)((mal_ALCdevice*)pDevice->openal.pDeviceALC); - } - - return MAL_SUCCESS; -} - -static mal_result mal_device__break_main_loop__openal(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - pDevice->openal.breakFromMainLoop = MAL_TRUE; - return MAL_SUCCESS; -} - -static mal_uint32 mal_device__get_available_frames__openal(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - if (pDevice->type == mal_device_type_playback) { - ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); - - mal_ALint processedBufferCount = 0; - ((MAL_LPALGETSOURCEI)pDevice->pContext->openal.alGetSourcei)(pDevice->openal.sourceAL, MAL_AL_BUFFERS_PROCESSED, &processedBufferCount); - - return processedBufferCount * pDevice->openal.subBufferSizeInFrames; - } else { - mal_ALint samplesAvailable = 0; - ((MAL_LPALCGETINTEGERV)pDevice->pContext->openal.alcGetIntegerv)((mal_ALCdevice*)pDevice->openal.pDeviceALC, MAL_ALC_CAPTURE_SAMPLES, 1, &samplesAvailable); - - return samplesAvailable / pDevice->channels; - } -} - -static mal_uint32 mal_device__wait_for_frames__openal(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - while (!pDevice->openal.breakFromMainLoop) { - mal_uint32 framesAvailable = mal_device__get_available_frames__openal(pDevice); - if (framesAvailable > 0) { - return framesAvailable; - } - - mal_sleep(1); - } - - // We'll get here if the loop was terminated. When capturing we want to return whatever is available. For playback we just drop it. - if (pDevice->type == mal_device_type_playback) { - return 0; - } else { - return mal_device__get_available_frames__openal(pDevice); - } -} - -static mal_result mal_device__main_loop__openal(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - pDevice->openal.breakFromMainLoop = MAL_FALSE; - while (!pDevice->openal.breakFromMainLoop) { - mal_uint32 framesAvailable = mal_device__wait_for_frames__openal(pDevice); - if (framesAvailable == 0) { - continue; - } - - // If it's a playback device, don't bother grabbing more data if the device is being stopped. - if (pDevice->openal.breakFromMainLoop && pDevice->type == mal_device_type_playback) { - return MAL_FALSE; - } - - if (pDevice->type == mal_device_type_playback) { - while (framesAvailable > 0) { - mal_uint32 framesToRead = (framesAvailable > pDevice->openal.subBufferSizeInFrames) ? pDevice->openal.subBufferSizeInFrames : framesAvailable; - - mal_ALuint bufferAL = pDevice->openal.buffersAL[pDevice->openal.iNextBuffer]; - pDevice->openal.iNextBuffer = (pDevice->openal.iNextBuffer + 1) % pDevice->periods; - - mal_device__read_frames_from_client(pDevice, framesToRead, pDevice->openal.pIntermediaryBuffer); - - ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); - ((MAL_LPALSOURCEUNQUEUEBUFFERS)pDevice->pContext->openal.alSourceUnqueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL); - ((MAL_LPALBUFFERDATA)pDevice->pContext->openal.alBufferData)(bufferAL, pDevice->openal.formatAL, pDevice->openal.pIntermediaryBuffer, pDevice->openal.subBufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat), pDevice->internalSampleRate); - ((MAL_LPALSOURCEQUEUEBUFFERS)pDevice->pContext->openal.alSourceQueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL); - - framesAvailable -= framesToRead; - } - - - // There's a chance the source has stopped playing due to there not being any buffer's queue. Make sure it's restarted. - mal_ALenum state; - ((MAL_LPALGETSOURCEI)pDevice->pContext->openal.alGetSourcei)(pDevice->openal.sourceAL, MAL_AL_SOURCE_STATE, &state); - - if (state != MAL_AL_PLAYING) { - ((MAL_LPALSOURCEPLAY)pDevice->pContext->openal.alSourcePlay)(pDevice->openal.sourceAL); - } - } else { - while (framesAvailable > 0) { - mal_uint32 framesToSend = (framesAvailable > pDevice->openal.subBufferSizeInFrames) ? pDevice->openal.subBufferSizeInFrames : framesAvailable; - ((MAL_LPALCCAPTURESAMPLES)pDevice->pContext->openal.alcCaptureSamples)((mal_ALCdevice*)pDevice->openal.pDeviceALC, pDevice->openal.pIntermediaryBuffer, framesToSend); - - mal_device__send_frames_to_client(pDevice, framesToSend, pDevice->openal.pIntermediaryBuffer); - framesAvailable -= framesToSend; - } - } - } + pContext->openal.isFloat32Supported = ((MAL_LPALISEXTENSIONPRESENT)pContext->openal.alIsExtensionPresent)("AL_EXT_float32"); + pContext->openal.isMCFormatsSupported = ((MAL_LPALISEXTENSIONPRESENT)pContext->openal.alIsExtensionPresent)("AL_EXT_MCFORMATS"); + + pContext->onUninit = mal_context_uninit__openal; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__openal; + pContext->onEnumDevices = mal_context_enumerate_devices__openal; + pContext->onGetDeviceInfo = mal_context_get_device_info__openal; + pContext->onDeviceInit = mal_device_init__openal; + pContext->onDeviceUninit = mal_device_uninit__openal; + pContext->onDeviceStart = mal_device__start_backend__openal; + pContext->onDeviceStop = mal_device__stop_backend__openal; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__openal; + pContext->onDeviceMainLoop = mal_device__main_loop__openal; return MAL_SUCCESS; } @@ -8706,6 +19491,365 @@ mal_format mal_format_from_sdl(MAL_SDL_AudioFormat format) } } +mal_bool32 mal_context_is_device_id_equal__sdl(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return pID0->sdl == pID1->sdl; +} + +mal_result mal_context_enumerate_devices__sdl(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + +#ifndef MAL_USE_SDL_1 + if (!pContext->sdl.usingSDL1) { + mal_bool32 isTerminated = MAL_FALSE; + + // Playback + if (!isTerminated) { + int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(0); + for (int i = 0; i < deviceCount; ++i) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + + deviceInfo.id.sdl = i; + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 0), (size_t)-1); + + mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + isTerminated = MAL_TRUE; + break; + } + } + } + + // Capture + if (!isTerminated) { + int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(1); + for (int i = 0; i < deviceCount; ++i) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + + deviceInfo.id.sdl = i; + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 1), (size_t)-1); + + mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + isTerminated = MAL_TRUE; + break; + } + } + } + } else +#endif + { + // SDL1 only uses default devices, and does not support capture. + mal_bool32 cbResult = MAL_TRUE; + + // Playback. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + +#if 0 // No capture with SDL1. + // Capture. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } +#endif + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + +#ifndef MAL_USE_SDL_1 + if (!pContext->sdl.usingSDL1) { + if (pDeviceID == NULL) { + if (deviceType == mal_device_type_playback) { + pDeviceInfo->id.sdl = 0; + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + pDeviceInfo->id.sdl = 0; + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + } else { + pDeviceInfo->id.sdl = pDeviceID->sdl; + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, (deviceType == mal_device_type_playback) ? 0 : 1), (size_t)-1); + } + } else +#endif + { + // SDL1 uses default devices. + if (deviceType == mal_device_type_playback) { + pDeviceInfo->id.sdl = 0; + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + pDeviceInfo->id.sdl = 0; + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + } + + // To get an accurate idea on the backend's native format we need to open the device. Not ideal, but it's the only way. An + // alternative to this is to report all channel counts, sample rates and formats, but that doesn't offer a good representation + // of the device's _actual_ ideal format. + // + // Note: With Emscripten, it looks like non-zero values need to be specified for desiredSpec. Whatever is specified in + // desiredSpec will be used by SDL since it uses it just does it's own format conversion internally. Therefore, from what + // I can tell, there's no real way to know the device's actual format which means I'm just going to fall back to the full + // range of channels and sample rates on Emscripten builds. +#if defined(__EMSCRIPTEN__) + pDeviceInfo->minChannels = MAL_MIN_CHANNELS; + pDeviceInfo->maxChannels = MAL_MAX_CHANNELS; + pDeviceInfo->minSampleRate = MAL_MIN_SAMPLE_RATE; + pDeviceInfo->maxSampleRate = MAL_MAX_SAMPLE_RATE; + pDeviceInfo->formatCount = 3; + pDeviceInfo->formats[0] = mal_format_u8; + pDeviceInfo->formats[1] = mal_format_s16; + pDeviceInfo->formats[2] = mal_format_s32; +#else + MAL_SDL_AudioSpec desiredSpec, obtainedSpec; + mal_zero_memory(&desiredSpec, sizeof(desiredSpec)); + +#ifndef MAL_USE_SDL_1 + if (!pContext->sdl.usingSDL1) { + int isCapture = (deviceType == mal_device_type_playback) ? 0 : 1; + + const char* pDeviceName = NULL; + if (pDeviceID != NULL) { + pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture); + } + + MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE); + if (tempDeviceID == 0) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + ((MAL_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID); + } else +#endif + { + // SDL1 uses default devices. + (void)pDeviceID; + + // SDL1 only supports playback as far as I can tell. + if (deviceType != mal_device_type_playback) { + return MAL_NO_DEVICE; + } + + MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudio)pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec); + if (tempDeviceID != 0) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + ((MAL_PFN_SDL_CloseAudio)pContext->sdl.SDL_CloseAudio)(); + } + + pDeviceInfo->minChannels = obtainedSpec.channels; + pDeviceInfo->maxChannels = obtainedSpec.channels; + pDeviceInfo->minSampleRate = obtainedSpec.freq; + pDeviceInfo->maxSampleRate = obtainedSpec.freq; + pDeviceInfo->formatCount = 1; + if (obtainedSpec.format == MAL_AUDIO_U8) { + pDeviceInfo->formats[0] = mal_format_u8; + } else if (obtainedSpec.format == MAL_AUDIO_S16) { + pDeviceInfo->formats[0] = mal_format_s16; + } else if (obtainedSpec.format == MAL_AUDIO_S32) { + pDeviceInfo->formats[0] = mal_format_s32; + } else if (obtainedSpec.format == MAL_AUDIO_F32) { + pDeviceInfo->formats[0] = mal_format_f32; + } else { + return MAL_FORMAT_NOT_SUPPORTED; + } +#endif + + return MAL_SUCCESS; +} + + +void mal_device_uninit__sdl(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + +#ifndef MAL_USE_SDL_1 + if (!pDevice->pContext->sdl.usingSDL1) { + ((MAL_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID); + } else +#endif + { + ((MAL_PFN_SDL_CloseAudio)pDevice->pContext->sdl.SDL_CloseAudio)(); + } +} + + +void mal_audio_callback__sdl(void* pUserData, mal_uint8* pBuffer, int bufferSizeInBytes) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_uint32 bufferSizeInFrames = (mal_uint32)bufferSizeInBytes / mal_get_bytes_per_sample(pDevice->internalFormat) / pDevice->internalChannels; + + if (pDevice->type == mal_device_type_playback) { + mal_device__read_frames_from_client(pDevice, bufferSizeInFrames, pBuffer); + } else { + mal_device__send_frames_to_client(pDevice, bufferSizeInFrames, pBuffer); + } +} + +mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + mal_assert(pContext != NULL); + mal_assert(pConfig != NULL); + mal_assert(pDevice != NULL); + + (void)pContext; + + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate); + } + + // SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need + // to explicitly clamp this because it will be easy to overflow. + mal_uint32 bufferSize = pConfig->bufferSizeInFrames; + if (bufferSize > 32768) { + bufferSize = 32768; + } else { + bufferSize = mal_next_power_of_2(bufferSize); + } + + mal_assert(bufferSize <= 32768); + + + MAL_SDL_AudioSpec desiredSpec, obtainedSpec; + mal_zero_memory(&desiredSpec, sizeof(desiredSpec)); + desiredSpec.freq = (int)pConfig->sampleRate; + desiredSpec.format = mal_format_to_sdl(pConfig->format); + desiredSpec.channels = (mal_uint8)pConfig->channels; + desiredSpec.samples = (mal_uint16)bufferSize; + desiredSpec.callback = mal_audio_callback__sdl; + desiredSpec.userdata = pDevice; + + // Fall back to f32 if we don't have an appropriate mapping between mini_al and SDL. + if (desiredSpec.format == 0) { + desiredSpec.format = MAL_AUDIO_F32; + } + +#ifndef MAL_USE_SDL_1 + if (!pDevice->pContext->sdl.usingSDL1) { + int isCapture = (type == mal_device_type_playback) ? 0 : 1; + + const char* pDeviceName = NULL; + if (pDeviceID != NULL) { + pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pDevice->pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture); + } + + pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE); + if (pDevice->sdl.deviceID == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } else +#endif + { + // SDL1 uses default devices. + (void)pDeviceID; + + // SDL1 only supports playback as far as I can tell. + if (type != mal_device_type_playback) { + return MAL_NO_DEVICE; + } + + // SDL1 does not support floating point formats. + if (desiredSpec.format == MAL_AUDIO_F32) { + desiredSpec.format = MAL_AUDIO_S16; + } + + pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec); + if (pDevice->sdl.deviceID != 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } + + pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format); + pDevice->internalChannels = obtainedSpec.channels; + pDevice->internalSampleRate = (mal_uint32)obtainedSpec.freq; + mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); + pDevice->bufferSizeInFrames = obtainedSpec.samples; + pDevice->periods = 1; // SDL doesn't seem to tell us what the period count is. Just set this 1. + +#ifdef MAL_DEBUG_OUTPUT + printf("=== SDL CONFIG ===\n"); + printf("REQUESTED -> RECEIVED\n"); + printf(" FORMAT: %s -> %s\n", mal_get_format_name(pConfig->format), mal_get_format_name(pDevice->internalFormat)); + printf(" CHANNELS: %d -> %d\n", desiredSpec.channels, obtainedSpec.channels); + printf(" SAMPLE RATE: %d -> %d\n", desiredSpec.freq, obtainedSpec.freq); + printf(" BUFFER SIZE IN SAMPLES: %d -> %d\n", desiredSpec.samples, obtainedSpec.samples); +#endif + + return MAL_SUCCESS; +} + +mal_result mal_device__start_backend__sdl(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + +#ifndef MAL_USE_SDL_1 + if (!pDevice->pContext->sdl.usingSDL1) { + ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0); + } else +#endif + { + ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(0); + } + + return MAL_SUCCESS; +} + +mal_result mal_device__stop_backend__sdl(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + +#ifndef MAL_USE_SDL_1 + if (!pDevice->pContext->sdl.usingSDL1) { + ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1); + } else +#endif + { + ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(1); + } + + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + mal_stop_proc onStop = pDevice->onStop; + if (onStop) { + onStop(pDevice); + } + + return MAL_SUCCESS; +} + + +mal_result mal_context_uninit__sdl(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_sdl); + + ((MAL_PFN_SDL_QuitSubSystem)pContext->sdl.SDL_QuitSubSystem)(MAL_SDL_INIT_AUDIO); + return MAL_SUCCESS; +} mal_result mal_context_init__sdl(mal_context* pContext) { @@ -8718,8 +19862,8 @@ mal_result mal_context_init__sdl(mal_context* pContext) "SDL2.dll", "SDL.dll" #elif defined(MAL_APPLE) - "libSDL2-2.0.0.dylib", // Can any Mac users out there comfirm these library names? - "libSDL-1.2.0.dylib" + "SDL2.framework/SDL2", + "SDL.framework/SDL" #else "libSDL2-2.0.so.0", "libSDL-1.2.so.0" @@ -8780,210 +19924,16 @@ mal_result mal_context_init__sdl(mal_context* pContext) return MAL_ERROR; } - return MAL_SUCCESS; -} + pContext->isBackendAsynchronous = MAL_TRUE; -mal_result mal_context_uninit__sdl(mal_context* pContext) -{ - mal_assert(pContext != NULL); - mal_assert(pContext->backend == mal_backend_sdl); - - ((MAL_PFN_SDL_QuitSubSystem)pContext->sdl.SDL_QuitSubSystem)(MAL_SDL_INIT_AUDIO); - return MAL_SUCCESS; -} - -mal_result mal_enumerate_devices__sdl(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) -{ - (void)pContext; - - mal_uint32 infoSize = *pCount; - *pCount = 0; - -#ifndef MAL_USE_SDL_1 - if (!pContext->sdl.usingSDL1) { - int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)((type == mal_device_type_playback) ? 0 : 1); - for (int i = 0; i < deviceCount; ++i) { - if (pInfo != NULL) { - if (infoSize > 0) { - pInfo->id.sdl = i; - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, (type == mal_device_type_playback) ? 0 : 1), (size_t)-1); - - pInfo += 1; - *pCount += 1; - } - } else { - *pCount += 1; - } - } - } else -#endif - { - if (pInfo != NULL) { - if (infoSize > 0) { - // SDL1 uses default devices. - if (type == mal_device_type_playback) { - pInfo->id.sdl = 0; - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1); - } else { - pInfo->id.sdl = 0; - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1); - } - - pInfo += 1; - *pCount += 1; - } - } else { - *pCount += 1; - } - } - - return MAL_SUCCESS; -} - -void mal_device_uninit__sdl(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - -#ifndef MAL_USE_SDL_1 - if (!pDevice->pContext->sdl.usingSDL1) { - ((MAL_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID); - } else -#endif - { - ((MAL_PFN_SDL_CloseAudio)pDevice->pContext->sdl.SDL_CloseAudio)(); - } -} - - -static void mal_audio_callback__sdl(void* pUserData, mal_uint8* pBuffer, int bufferSizeInBytes) -{ - mal_device* pDevice = (mal_device*)pUserData; - mal_assert(pDevice != NULL); - - mal_uint32 bufferSizeInFrames = (mal_uint32)bufferSizeInBytes / mal_get_sample_size_in_bytes(pDevice->internalFormat) / pDevice->internalChannels; - - if (pDevice->type == mal_device_type_playback) { - mal_device__read_frames_from_client(pDevice, bufferSizeInFrames, pBuffer); - } else { - mal_device__send_frames_to_client(pDevice, bufferSizeInFrames, pBuffer); - } -} - -mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) -{ - mal_assert(pContext != NULL); - mal_assert(pConfig != NULL); - mal_assert(pDevice != NULL); - - (void)pContext; - - // SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need - // to explicitly clamp this because it will be easy to overflow. - mal_uint32 bufferSize = pConfig->bufferSizeInFrames; - if (bufferSize > 32768) { - bufferSize = 32768; - } else { - bufferSize = mal_next_power_of_2(bufferSize); - } - - mal_assert(bufferSize <= 32768); - - - MAL_SDL_AudioSpec desiredSpec, obtainedSpec; - mal_zero_memory(&desiredSpec, sizeof(desiredSpec)); - desiredSpec.freq = (int)pConfig->sampleRate; - desiredSpec.format = mal_format_to_sdl(pConfig->format); - desiredSpec.channels = (mal_uint8)pConfig->channels; - desiredSpec.samples = (mal_uint16)bufferSize; - desiredSpec.callback = mal_audio_callback__sdl; - desiredSpec.userdata = pDevice; - - // Fall back to f32 if we don't have an appropriate mapping between mini_al and SDL. - if (desiredSpec.format == 0) { - desiredSpec.format = MAL_AUDIO_F32; - } - -#ifndef MAL_USE_SDL_1 - if (!pDevice->pContext->sdl.usingSDL1) { - int isCapture = (type == mal_device_type_playback) ? 0 : 1; - - const char* pDeviceName = NULL; - if (pDeviceID != NULL) { - pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pDevice->pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture); - } - - pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE); - if (pDevice->sdl.deviceID == 0) { - return mal_post_error(pDevice, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - } else -#endif - { - // SDL1 uses default devices. - (void)pDeviceID; - - // SDL1 only supports playback as far as I can tell. - if (type != mal_device_type_playback) { - return MAL_NO_DEVICE; - } - - // SDL1 does not support floating point formats. - if (desiredSpec.format == MAL_AUDIO_F32) { - desiredSpec.format = MAL_AUDIO_S16; - } - - pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec); - if (pDevice->sdl.deviceID != 0) { - return mal_post_error(pDevice, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - } - - pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format); - pDevice->internalChannels = obtainedSpec.channels; - pDevice->internalSampleRate = (mal_uint32)obtainedSpec.freq; - pDevice->bufferSizeInFrames = obtainedSpec.samples; - pDevice->periods = 1; // SDL doesn't seem to tell us what the period count is. Just set this 1. - -#if 0 - printf("=== SDL CONFIG ===\n"); - printf("REQUESTED -> RECEIVED\n"); - printf(" FORMAT: %s -> %s\n", mal_get_format_name(pConfig->format), mal_get_format_name(pDevice->internalFormat)); - printf(" CHANNELS: %d -> %d\n", desiredSpec.channels, obtainedSpec.channels); - printf(" SAMPLE RATE: %d -> %d\n", desiredSpec.freq, obtainedSpec.freq); - printf(" BUFFER SIZE IN SAMPLES: %d -> %d\n", desiredSpec.samples, obtainedSpec.samples); -#endif - - return MAL_SUCCESS; -} - -static mal_result mal_device__start_backend__sdl(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - -#ifndef MAL_USE_SDL_1 - if (!pDevice->pContext->sdl.usingSDL1) { - ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0); - } else -#endif - { - ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(0); - } - - return MAL_SUCCESS; -} - -static mal_result mal_device__stop_backend__sdl(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - -#ifndef MAL_USE_SDL_1 - if (!pDevice->pContext->sdl.usingSDL1) { - ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1); - } else -#endif - { - ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(1); - } + pContext->onUninit = mal_context_uninit__sdl; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__sdl; + pContext->onEnumDevices = mal_context_enumerate_devices__sdl; + pContext->onGetDeviceInfo = mal_context_get_device_info__sdl; + pContext->onDeviceInit = mal_device_init__sdl; + pContext->onDeviceUninit = mal_device_uninit__sdl; + pContext->onDeviceStart = mal_device__start_backend__sdl; + pContext->onDeviceStop = mal_device__stop_backend__sdl; return MAL_SUCCESS; } @@ -8991,16 +19941,20 @@ static mal_result mal_device__stop_backend__sdl(mal_device* pDevice) - mal_bool32 mal__is_channel_map_valid(const mal_channel* channelMap, mal_uint32 channels) { - mal_assert(channels > 0); + // A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. + if (channelMap[0] != MAL_CHANNEL_NONE) { + if (channels == 0) { + return MAL_FALSE; // No channels. + } - // A channel cannot be present in the channel map more than once. - for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { - for (mal_uint32 jChannel = iChannel + 1; jChannel < channels; ++jChannel) { - if (channelMap[iChannel] == channelMap[jChannel]) { - return MAL_FALSE; + // A channel cannot be present in the channel map more than once. + for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { + for (mal_uint32 jChannel = iChannel + 1; jChannel < channels; ++jChannel) { + if (channelMap[iChannel] == channelMap[jChannel]) { + return MAL_FALSE; + } } } } @@ -9009,181 +19963,64 @@ mal_bool32 mal__is_channel_map_valid(const mal_channel* channelMap, mal_uint32 c } -static mal_result mal_device__start_backend(mal_device* pDevice) +void mal_device__post_init_setup(mal_device* pDevice) { mal_assert(pDevice != NULL); - mal_result result = MAL_NO_BACKEND; -#ifdef MAL_HAS_WASAPI - if (pDevice->pContext->backend == mal_backend_wasapi) { - result = mal_device__start_backend__wasapi(pDevice); + // Make sure the internal channel map was set correctly by the backend. If it's not valid, just fall back to defaults. + if (!mal_channel_map_valid(pDevice->internalChannels, pDevice->internalChannelMap)) { + mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); } -#endif -#ifdef MAL_HAS_DSOUND - if (pDevice->pContext->backend == mal_backend_dsound) { - result = mal_device__start_backend__dsound(pDevice); - } -#endif -#ifdef MAL_HAS_WINMM - if (pDevice->pContext->backend == mal_backend_winmm) { - result = mal_device__start_backend__winmm(pDevice); - } -#endif -#ifdef MAL_HAS_ALSA - if (pDevice->pContext->backend == mal_backend_alsa) { - result = mal_device__start_backend__alsa(pDevice); - } -#endif -#ifdef MAL_HAS_OSS - if (pDevice->pContext->backend == mal_backend_oss) { - result = mal_device__start_backend__oss(pDevice); - } -#endif -#ifdef MAL_HAS_OPENAL - if (pDevice->pContext->backend == mal_backend_openal) { - result = mal_device__start_backend__openal(pDevice); - } -#endif -#ifdef MAL_HAS_NULL - if (pDevice->pContext->backend == mal_backend_null) { - result = mal_device__start_backend__null(pDevice); - } -#endif - return result; + + // If the format/channels/rate is using defaults we need to set these to be the same as the internal config. + if (pDevice->usingDefaultFormat) { + pDevice->format = pDevice->internalFormat; + } + if (pDevice->usingDefaultChannels) { + pDevice->channels = pDevice->internalChannels; + } + if (pDevice->usingDefaultSampleRate) { + pDevice->sampleRate = pDevice->internalSampleRate; + } + if (pDevice->usingDefaultChannelMap) { + mal_copy_memory(pDevice->channelMap, pDevice->internalChannelMap, sizeof(pDevice->channelMap)); + } + + // Buffer size. The backend will have set bufferSizeInFrames. We need to calculate bufferSizeInMilliseconds here. + pDevice->bufferSizeInMilliseconds = pDevice->bufferSizeInFrames / (pDevice->internalSampleRate/1000); + + + // We need a DSP object which is where samples are moved through in order to convert them to the + // format required by the backend. + mal_dsp_config dspConfig = mal_dsp_config_init_new(); + dspConfig.neverConsumeEndOfInput = MAL_TRUE; + dspConfig.pUserData = pDevice; + if (pDevice->type == mal_device_type_playback) { + dspConfig.formatIn = pDevice->format; + dspConfig.channelsIn = pDevice->channels; + dspConfig.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(dspConfig.channelMapIn, pDevice->channelMap, sizeof(dspConfig.channelMapIn)); + dspConfig.formatOut = pDevice->internalFormat; + dspConfig.channelsOut = pDevice->internalChannels; + dspConfig.sampleRateOut = pDevice->internalSampleRate; + mal_copy_memory(dspConfig.channelMapOut, pDevice->internalChannelMap, sizeof(dspConfig.channelMapOut)); + dspConfig.onRead = mal_device__on_read_from_client; + mal_dsp_init(&dspConfig, &pDevice->dsp); + } else { + dspConfig.formatIn = pDevice->internalFormat; + dspConfig.channelsIn = pDevice->internalChannels; + dspConfig.sampleRateIn = pDevice->internalSampleRate; + mal_copy_memory(dspConfig.channelMapIn, pDevice->internalChannelMap, sizeof(dspConfig.channelMapIn)); + dspConfig.formatOut = pDevice->format; + dspConfig.channelsOut = pDevice->channels; + dspConfig.sampleRateOut = pDevice->sampleRate; + mal_copy_memory(dspConfig.channelMapOut, pDevice->channelMap, sizeof(dspConfig.channelMapOut)); + dspConfig.onRead = mal_device__on_read_from_device; + mal_dsp_init(&dspConfig, &pDevice->dsp); + } } -static mal_result mal_device__stop_backend(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - mal_result result = MAL_NO_BACKEND; -#ifdef MAL_HAS_WASAPI - if (pDevice->pContext->backend == mal_backend_wasapi) { - result = mal_device__stop_backend__wasapi(pDevice); - } -#endif -#ifdef MAL_HAS_DSOUND - if (pDevice->pContext->backend == mal_backend_dsound) { - result = mal_device__stop_backend__dsound(pDevice); - } -#endif -#ifdef MAL_HAS_WINMM - if (pDevice->pContext->backend == mal_backend_winmm) { - result = mal_device__stop_backend__winmm(pDevice); - } -#endif -#ifdef MAL_HAS_ALSA - if (pDevice->pContext->backend == mal_backend_alsa) { - result = mal_device__stop_backend__alsa(pDevice); - } -#endif -#ifdef MAL_HAS_OSS - if (pDevice->pContext->backend == mal_backend_oss) { - result = mal_device__stop_backend__oss(pDevice); - } -#endif -#ifdef MAL_HAS_OPENAL - if (pDevice->pContext->backend == mal_backend_openal) { - result = mal_device__stop_backend__openal(pDevice); - } -#endif -#ifdef MAL_HAS_NULL - if (pDevice->pContext->backend == mal_backend_null) { - result = mal_device__stop_backend__null(pDevice); - } -#endif - - return result; -} - -static mal_result mal_device__break_main_loop(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - mal_result result = MAL_NO_BACKEND; -#ifdef MAL_HAS_WASAPI - if (pDevice->pContext->backend == mal_backend_wasapi) { - result = mal_device__break_main_loop__wasapi(pDevice); - } -#endif -#ifdef MAL_HAS_DSOUND - if (pDevice->pContext->backend == mal_backend_dsound) { - result = mal_device__break_main_loop__dsound(pDevice); - } -#endif -#ifdef MAL_HAS_WINMM - if (pDevice->pContext->backend == mal_backend_winmm) { - result = mal_device__break_main_loop__winmm(pDevice); - } -#endif -#ifdef MAL_HAS_ALSA - if (pDevice->pContext->backend == mal_backend_alsa) { - result = mal_device__break_main_loop__alsa(pDevice); - } -#endif -#ifdef MAL_HAS_OSS - if (pDevice->pContext->backend == mal_backend_oss) { - result = mal_device__break_main_loop__oss(pDevice); - } -#endif -#ifdef MAL_HAS_OPENAL - if (pDevice->pContext->backend == mal_backend_openal) { - result = mal_device__break_main_loop__openal(pDevice); - } -#endif -#ifdef MAL_HAS_NULL - if (pDevice->pContext->backend == mal_backend_null) { - result = mal_device__break_main_loop__null(pDevice); - } -#endif - - return result; -} - -static mal_result mal_device__main_loop(mal_device* pDevice) -{ - mal_assert(pDevice != NULL); - - mal_result result = MAL_NO_BACKEND; -#ifdef MAL_HAS_WASAPI - if (pDevice->pContext->backend == mal_backend_wasapi) { - result = mal_device__main_loop__wasapi(pDevice); - } -#endif -#ifdef MAL_HAS_DSOUND - if (pDevice->pContext->backend == mal_backend_dsound) { - result = mal_device__main_loop__dsound(pDevice); - } -#endif -#ifdef MAL_HAS_WINMM - if (pDevice->pContext->backend == mal_backend_winmm) { - result = mal_device__main_loop__winmm(pDevice); - } -#endif -#ifdef MAL_HAS_ALSA - if (pDevice->pContext->backend == mal_backend_alsa) { - result = mal_device__main_loop__alsa(pDevice); - } -#endif -#ifdef MAL_HAS_OSS - if (pDevice->pContext->backend == mal_backend_oss) { - result = mal_device__main_loop__oss(pDevice); - } -#endif -#ifdef MAL_HAS_OPENAL - if (pDevice->pContext->backend == mal_backend_openal) { - result = mal_device__main_loop__openal(pDevice); - } -#endif -#ifdef MAL_HAS_NULL - if (pDevice->pContext->backend == mal_backend_null) { - result = mal_device__main_loop__null(pDevice); - } -#endif - - return result; -} mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) { @@ -9191,59 +20028,98 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) mal_assert(pDevice != NULL); #ifdef MAL_WIN32 - mal_CoInitializeEx(pDevice->pContext, NULL, 0); // 0 = COINIT_MULTITHREADED + mal_CoInitializeEx(pDevice->pContext, NULL, MAL_COINIT_VALUE); #endif - // This is only used to prevent posting onStop() when the device is first initialized. - mal_bool32 skipNextStopEvent = MAL_TRUE; + // When the device is being initialized it's initial state is set to MAL_STATE_UNINITIALIZED. Before returning from + // mal_device_init(), the state needs to be set to something valid. In mini_al the device's default state immediately + // after initialization is stopped, so therefore we need to mark the device as such. mini_al will wait on the worker + // thread to signal an event to know when the worker thread is ready for action. + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + mal_event_signal(&pDevice->stopEvent); for (;;) { - // At the start of iteration the device is stopped - we must explicitly mark it as such. - mal_device__stop_backend(pDevice); - - if (!skipNextStopEvent) { - mal_stop_proc onStop = pDevice->onStop; - if (onStop) { - onStop(pDevice); - } - } else { - skipNextStopEvent = MAL_FALSE; - } - - - // Let the other threads know that the device has stopped. - mal_device__set_state(pDevice, MAL_STATE_STOPPED); - mal_event_signal(&pDevice->stopEvent); - - // We use an event to wait for a request to wake up. + // We wait on an event to know when something has requested that the device be started and the main loop entered. mal_event_wait(&pDevice->wakeupEvent); // Default result code. pDevice->workResult = MAL_SUCCESS; - // Just break if we're terminating. + // If the reason for the wake up is that we are terminating, just break from the loop. if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) { break; } - - // Getting here means we just started the device and we need to wait for the device to - // either deliver us data (recording) or request more data (playback). + // Getting to this point means the device is wanting to get started. The function that has requested that the device + // be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event + // in both the success and error case. It's important that the state of the device is set _before_ signaling the event. mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTING); - pDevice->workResult = mal_device__start_backend(pDevice); + pDevice->workResult = pDevice->pContext->onDeviceStart(pDevice); if (pDevice->workResult != MAL_SUCCESS) { + mal_device__set_state(pDevice, MAL_STATE_STOPPED); mal_event_signal(&pDevice->startEvent); continue; } - // The thread that requested the device to start playing is waiting for this thread to start the - // device for real, which is now. + // At this point the device should be started. mal_device__set_state(pDevice, MAL_STATE_STARTED); mal_event_signal(&pDevice->startEvent); - // Now we just enter the main loop. The main loop can be broken with mal_device__break_main_loop(). - mal_device__main_loop(pDevice); + + // Now we just enter the main loop. When the main loop is terminated the device needs to be marked as stopped. This can + // be broken with mal_device__break_main_loop(). + mal_result mainLoopResult = pDevice->pContext->onDeviceMainLoop(pDevice); + if (mainLoopResult != MAL_SUCCESS && pDevice->isDefaultDevice && mal_device__get_state(pDevice) == MAL_STATE_STARTED && !pDevice->exclusiveMode) { + // Something has failed during the main loop. It could be that the device has been lost. If it's the default device, + // we can try switching over to the new default device by uninitializing and reinitializing. + mal_result reinitResult = MAL_ERROR; + if (pDevice->pContext->onDeviceReinit) { + reinitResult = pDevice->pContext->onDeviceReinit(pDevice); + } else { + pDevice->pContext->onDeviceStop(pDevice); + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + + pDevice->pContext->onDeviceUninit(pDevice); + mal_device__set_state(pDevice, MAL_STATE_UNINITIALIZED); + + reinitResult = pDevice->pContext->onDeviceInit(pDevice->pContext, pDevice->type, NULL, &pDevice->initConfig, pDevice); + } + + // Perform the post initialization setup just in case the data conversion pipeline needs to be reinitialized. + if (reinitResult == MAL_SUCCESS) { + mal_device__post_init_setup(pDevice); + } + + // If reinitialization was successful, loop back to the start. + if (reinitResult == MAL_SUCCESS) { + mal_device__set_state(pDevice, MAL_STATE_STARTING); // <-- The device is restarting. + mal_event_signal(&pDevice->wakeupEvent); + continue; + } + } + + + // Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this + // may have actually already happened above if the device was lost and mini_al has attempted to re-initialize the device. In this case we + // don't want to be doing this a second time. + if (mal_device__get_state(pDevice) != MAL_STATE_UNINITIALIZED) { + pDevice->pContext->onDeviceStop(pDevice); + } + + // After the device has stopped, make sure an event is posted. + mal_stop_proc onStop = pDevice->onStop; + if (onStop) { + onStop(pDevice); + } + + // A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. Note that + // it's possible that the device has been uninitialized which means we need to _not_ change the status to stopped. We cannot go from an + // uninitialized state to stopped state. + if (mal_device__get_state(pDevice) != MAL_STATE_UNINITIALIZED) { + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + mal_event_signal(&pDevice->stopEvent); + } } // Make sure we aren't continuously waiting on a stop event. @@ -9271,6 +20147,7 @@ mal_result mal_context_uninit_backend_apis__win32(mal_context* pContext) mal_CoUninitialize(pContext); mal_dlclose(pContext->win32.hUser32DLL); mal_dlclose(pContext->win32.hOle32DLL); + mal_dlclose(pContext->win32.hAdvapi32DLL); return MAL_SUCCESS; } @@ -9289,6 +20166,7 @@ mal_result mal_context_init_backend_apis__win32(mal_context* pContext) pContext->win32.CoCreateInstance = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "CoCreateInstance"); pContext->win32.CoTaskMemFree = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "CoTaskMemFree"); pContext->win32.PropVariantClear = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "PropVariantClear"); + pContext->win32.StringFromGUID2 = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "StringFromGUID2"); // User32.dll @@ -9299,15 +20177,30 @@ mal_result mal_context_init_backend_apis__win32(mal_context* pContext) pContext->win32.GetForegroundWindow = (mal_proc)mal_dlsym(pContext->win32.hUser32DLL, "GetForegroundWindow"); pContext->win32.GetDesktopWindow = (mal_proc)mal_dlsym(pContext->win32.hUser32DLL, "GetDesktopWindow"); + + + // Advapi32.dll + pContext->win32.hAdvapi32DLL = mal_dlopen("advapi32.dll"); + if (pContext->win32.hAdvapi32DLL == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.RegOpenKeyExA = (mal_proc)mal_dlsym(pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (mal_proc)mal_dlsym(pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (mal_proc)mal_dlsym(pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #endif - mal_CoInitializeEx(pContext, NULL, 0); // 0 = COINIT_MULTITHREADED + mal_CoInitializeEx(pContext, NULL, MAL_COINIT_VALUE); return MAL_SUCCESS; } #else mal_result mal_context_uninit_backend_apis__nix(mal_context* pContext) { +#if defined(MAL_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MAL_NO_RUNTIME_LINKING) mal_dlclose(pContext->posix.pthreadSO); +#else + (void)pContext; +#endif return MAL_SUCCESS; } @@ -9315,7 +20208,7 @@ mal_result mal_context_uninit_backend_apis__nix(mal_context* pContext) mal_result mal_context_init_backend_apis__nix(mal_context* pContext) { // pthread -#if !defined(MAL_NO_RUNTIME_LINKING) +#if defined(MAL_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MAL_NO_RUNTIME_LINKING) const char* libpthreadFileNames[] = { "libpthread.so", "libpthread.so.0", @@ -9333,27 +20226,39 @@ mal_result mal_context_init_backend_apis__nix(mal_context* pContext) return MAL_FAILED_TO_INIT_BACKEND; } - pContext->posix.pthread_create = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_create"); - pContext->posix.pthread_join = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_join"); - pContext->posix.pthread_mutex_init = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_init"); - pContext->posix.pthread_mutex_destroy = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_destroy"); - pContext->posix.pthread_mutex_lock = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_lock"); - pContext->posix.pthread_mutex_unlock = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_unlock"); - pContext->posix.pthread_cond_init = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_init"); - pContext->posix.pthread_cond_destroy = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_destroy"); - pContext->posix.pthread_cond_wait = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_wait"); - pContext->posix.pthread_cond_signal = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_signal"); + pContext->posix.pthread_create = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_create"); + pContext->posix.pthread_join = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_join"); + pContext->posix.pthread_mutex_init = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_init"); + pContext->posix.pthread_mutex_destroy = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_destroy"); + pContext->posix.pthread_mutex_lock = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_lock"); + pContext->posix.pthread_mutex_unlock = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_unlock"); + pContext->posix.pthread_cond_init = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_init"); + pContext->posix.pthread_cond_destroy = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_destroy"); + pContext->posix.pthread_cond_wait = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_wait"); + pContext->posix.pthread_cond_signal = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_signal"); + pContext->posix.pthread_attr_init = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_attr_init"); + pContext->posix.pthread_attr_destroy = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_attr_destroy"); + pContext->posix.pthread_attr_setschedpolicy = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_attr_setschedpolicy"); + pContext->posix.pthread_attr_getschedparam = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_attr_getschedparam"); + pContext->posix.pthread_attr_setschedparam = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_attr_setschedparam"); #else - pContext->posix.pthread_create = (mal_proc)pthread_create; - pContext->posix.pthread_join = (mal_proc)pthread_join; - pContext->posix.pthread_mutex_init = (mal_proc)pthread_mutex_init; - pContext->posix.pthread_mutex_destroy = (mal_proc)pthread_mutex_destroy; - pContext->posix.pthread_mutex_lock = (mal_proc)pthread_mutex_lock; - pContext->posix.pthread_mutex_unlock = (mal_proc)pthread_mutex_unlock; - pContext->posix.pthread_cond_init = (mal_proc)pthread_cond_init; - pContext->posix.pthread_cond_destroy = (mal_proc)pthread_cond_destroy; - pContext->posix.pthread_cond_wait = (mal_proc)pthread_cond_wait; - pContext->posix.pthread_cond_signal = (mal_proc)pthread_cond_signal; + pContext->posix.pthread_create = (mal_proc)pthread_create; + pContext->posix.pthread_join = (mal_proc)pthread_join; + pContext->posix.pthread_mutex_init = (mal_proc)pthread_mutex_init; + pContext->posix.pthread_mutex_destroy = (mal_proc)pthread_mutex_destroy; + pContext->posix.pthread_mutex_lock = (mal_proc)pthread_mutex_lock; + pContext->posix.pthread_mutex_unlock = (mal_proc)pthread_mutex_unlock; + pContext->posix.pthread_cond_init = (mal_proc)pthread_cond_init; + pContext->posix.pthread_cond_destroy = (mal_proc)pthread_cond_destroy; + pContext->posix.pthread_cond_wait = (mal_proc)pthread_cond_wait; + pContext->posix.pthread_cond_signal = (mal_proc)pthread_cond_signal; + pContext->posix.pthread_attr_init = (mal_proc)pthread_attr_init; + pContext->posix.pthread_attr_destroy = (mal_proc)pthread_attr_destroy; +#if !defined(__EMSCRIPTEN__) + pContext->posix.pthread_attr_setschedpolicy = (mal_proc)pthread_attr_setschedpolicy; + pContext->posix.pthread_attr_getschedparam = (mal_proc)pthread_attr_getschedparam; + pContext->posix.pthread_attr_setschedparam = (mal_proc)pthread_attr_setschedparam; +#endif #endif return MAL_SUCCESS; @@ -9362,7 +20267,7 @@ mal_result mal_context_init_backend_apis__nix(mal_context* pContext) mal_result mal_context_init_backend_apis(mal_context* pContext) { - mal_result result = MAL_NO_BACKEND; + mal_result result; #ifdef MAL_WIN32 result = mal_context_init_backend_apis__win32(pContext); #else @@ -9374,7 +20279,7 @@ mal_result mal_context_init_backend_apis(mal_context* pContext) mal_result mal_context_uninit_backend_apis(mal_context* pContext) { - mal_result result = MAL_NO_BACKEND; + mal_result result; #ifdef MAL_WIN32 result = mal_context_uninit_backend_apis__win32(pContext); #else @@ -9384,9 +20289,18 @@ mal_result mal_context_uninit_backend_apis(mal_context* pContext) return result; } -mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pConfig, mal_context* pContext) + +mal_bool32 mal_context_is_backend_asynchronous(mal_context* pContext) { - if (pContext == NULL) return MAL_INVALID_ARGS; + return pContext->isBackendAsynchronous; +} + +mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pConfig, mal_context* pContext) +{ + if (pContext == NULL) { + return MAL_INVALID_ARGS; + } + mal_zero_object(pContext); // Always make sure the config is set first to ensure properties are available as soon as possible. @@ -9402,27 +20316,17 @@ mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, con return result; } - static mal_backend defaultBackends[] = { - mal_backend_wasapi, - mal_backend_dsound, - mal_backend_winmm, - mal_backend_alsa, - mal_backend_oss, - mal_backend_opensl, - mal_backend_openal, - mal_backend_sdl, - mal_backend_null - }; - - if (backends == NULL) { - backends = defaultBackends; - backendCount = sizeof(defaultBackends) / sizeof(defaultBackends[0]); + mal_backend* pBackendsToIterate = (mal_backend*)backends; + mal_uint32 backendsToIterateCount = backendCount; + if (pBackendsToIterate == NULL) { + pBackendsToIterate = (mal_backend*)g_malDefaultBackends; + backendsToIterateCount = mal_countof(g_malDefaultBackends); } - mal_assert(backends != NULL); + mal_assert(pBackendsToIterate != NULL); - for (mal_uint32 iBackend = 0; iBackend < backendCount; ++iBackend) { - mal_backend backend = backends[iBackend]; + for (mal_uint32 iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { + mal_backend backend = pBackendsToIterate[iBackend]; result = MAL_NO_BACKEND; switch (backend) { @@ -9450,6 +20354,36 @@ mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, con result = mal_context_init__alsa(pContext); } break; #endif + #ifdef MAL_HAS_PULSEAUDIO + case mal_backend_pulseaudio: + { + result = mal_context_init__pulse(pContext); + } break; + #endif + #ifdef MAL_HAS_JACK + case mal_backend_jack: + { + result = mal_context_init__jack(pContext); + } break; + #endif + #ifdef MAL_HAS_COREAUDIO + case mal_backend_coreaudio: + { + result = mal_context_init__coreaudio(pContext); + } break; + #endif + #ifdef MAL_HAS_SNDIO + case mal_backend_sndio: + { + result = mal_context_init__sndio(pContext); + } break; + #endif + #ifdef MAL_HAS_AUDIO4 + case mal_backend_audio4: + { + result = mal_context_init__audio4(pContext); + } break; + #endif #ifdef MAL_HAS_OSS case mal_backend_oss: { @@ -9486,172 +20420,218 @@ mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, con // If this iteration was successful, return. if (result == MAL_SUCCESS) { + result = mal_mutex_init(pContext, &pContext->deviceEnumLock); + if (result != MAL_SUCCESS) { + mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. mal_context_get_devices() is not thread safe.", MAL_FAILED_TO_CREATE_MUTEX); + } + result = mal_mutex_init(pContext, &pContext->deviceInfoLock); + if (result != MAL_SUCCESS) { + mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. mal_context_get_device_info() is not thread safe.", MAL_FAILED_TO_CREATE_MUTEX); + } + pContext->backend = backend; return result; } } + // If we get here it means an error occurred. mal_zero_object(pContext); // Safety. return MAL_NO_BACKEND; } mal_result mal_context_uninit(mal_context* pContext) { - if (pContext == NULL) return MAL_INVALID_ARGS; - - switch (pContext->backend) { - #ifdef MAL_HAS_WASAPI - case mal_backend_wasapi: - { - return mal_context_uninit__wasapi(pContext); - } break; - #endif - #ifdef MAL_HAS_DSOUND - case mal_backend_dsound: - { - return mal_context_uninit__dsound(pContext); - } break; - #endif - #ifdef MAL_HAS_WINMM - case mal_backend_winmm: - { - return mal_context_uninit__winmm(pContext); - } break; - #endif - #ifdef MAL_HAS_ALSA - case mal_backend_alsa: - { - return mal_context_uninit__alsa(pContext); - } break; - #endif - #ifdef MAL_HAS_OSS - case mal_backend_oss: - { - return mal_context_uninit__oss(pContext); - } break; - #endif - #ifdef MAL_HAS_OPENSL - case mal_backend_opensl: - { - return mal_context_uninit__opensl(pContext); - } break; - #endif - #ifdef MAL_HAS_OPENAL - case mal_backend_openal: - { - return mal_context_uninit__openal(pContext); - } break; - #endif - #ifdef MAL_HAS_SDL - case mal_backend_sdl: - { - return mal_context_uninit__sdl(pContext); - } break; - #endif - #ifdef MAL_HAS_NULL - case mal_backend_null: - { - return mal_context_uninit__null(pContext); - } break; - #endif - - default: break; + if (pContext == NULL) { + return MAL_INVALID_ARGS; } + pContext->onUninit(pContext); + mal_context_uninit_backend_apis(pContext); + mal_mutex_uninit(&pContext->deviceEnumLock); + mal_mutex_uninit(&pContext->deviceInfoLock); + mal_free(pContext->pDeviceInfos); - mal_assert(MAL_FALSE); - return MAL_NO_BACKEND; + return MAL_SUCCESS; } -mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +mal_result mal_context_enumerate_devices(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) { - if (pCount == NULL) return mal_post_error(NULL, "mal_enumerate_devices() called with invalid arguments (pCount == 0).", MAL_INVALID_ARGS); - - // The output buffer needs to be initialized to zero. - if (pInfo != NULL) { - mal_zero_memory(pInfo, (*pCount) * sizeof(*pInfo)); + if (pContext == NULL || pContext->onEnumDevices == NULL || callback == NULL) { + return MAL_INVALID_ARGS; } - switch (pContext->backend) + mal_result result; + mal_mutex_lock(&pContext->deviceEnumLock); { - #ifdef MAL_HAS_WASAPI - case mal_backend_wasapi: - { - return mal_enumerate_devices__wasapi(pContext, type, pCount, pInfo); - } break; - #endif - #ifdef MAL_HAS_DSOUND - case mal_backend_dsound: - { - return mal_enumerate_devices__dsound(pContext, type, pCount, pInfo); - } break; - #endif - #ifdef MAL_HAS_WINMM - case mal_backend_winmm: - { - return mal_enumerate_devices__winmm(pContext, type, pCount, pInfo); - } break; - #endif - #ifdef MAL_HAS_ALSA - case mal_backend_alsa: - { - return mal_enumerate_devices__alsa(pContext, type, pCount, pInfo); - } break; - #endif - #ifdef MAL_HAS_OSS - case mal_backend_oss: - { - return mal_enumerate_devices__oss(pContext, type, pCount, pInfo); - } break; - #endif - #ifdef MAL_HAS_OPENSL - case mal_backend_opensl: - { - return mal_enumerate_devices__opensl(pContext, type, pCount, pInfo); - } break; - #endif - #ifdef MAL_HAS_OPENAL - case mal_backend_openal: - { - return mal_enumerate_devices__openal(pContext, type, pCount, pInfo); - } break; - #endif - #ifdef MAL_HAS_SDL - case mal_backend_sdl: - { - return mal_enumerate_devices__sdl(pContext, type, pCount, pInfo); - } break; - #endif - #ifdef MAL_HAS_NULL - case mal_backend_null: - { - return mal_enumerate_devices__null(pContext, type, pCount, pInfo); - } break; - #endif + result = pContext->onEnumDevices(pContext, callback, pUserData); + } + mal_mutex_unlock(&pContext->deviceEnumLock); - default: break; + return result; +} + + +mal_bool32 mal_context_get_devices__enum_callback(mal_context* pContext, mal_device_type type, const mal_device_info* pInfo, void* pUserData) +{ + (void)pUserData; + + // We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device + // it's just appended to the end. If it's a playback device it's inserted just before the first capture device. + + // First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a + // simple fixed size increment for buffer expansion. + const mal_uint32 bufferExpansionCount = 2; + const mal_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; + + if (pContext->deviceInfoCapacity >= totalDeviceInfoCount) { + mal_uint32 newCapacity = totalDeviceInfoCount + bufferExpansionCount; + mal_device_info* pNewInfos = (mal_device_info*)mal_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity); + if (pNewInfos == NULL) { + return MAL_FALSE; // Out of memory. + } + + pContext->pDeviceInfos = pNewInfos; + pContext->deviceInfoCapacity = newCapacity; } - mal_assert(MAL_FALSE); - return MAL_NO_BACKEND; + if (type == mal_device_type_playback) { + // Playback. Insert just before the first capture device. + + // The first thing to do is move all of the capture devices down a slot. + mal_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; + for (size_t iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { + pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; + } + + // Now just insert where the first capture device was before moving it down a slot. + pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; + pContext->playbackDeviceInfoCount += 1; + } else { + // Capture. Insert at the end. + pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; + pContext->captureDeviceInfoCount += 1; + } + + return MAL_TRUE; } +mal_result mal_context_get_devices(mal_context* pContext, mal_device_info** ppPlaybackDeviceInfos, mal_uint32* pPlaybackDeviceCount, mal_device_info** ppCaptureDeviceInfos, mal_uint32* pCaptureDeviceCount) +{ + // Safety. + if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; + if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; + if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; + if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; + + if (pContext == NULL || pContext->onEnumDevices == NULL) { + return MAL_INVALID_ARGS; + } + + // Note that we don't use mal_context_enumerate_devices() here because we want to do locking at a higher level. + mal_result result; + mal_mutex_lock(&pContext->deviceEnumLock); + { + // Reset everything first. + pContext->playbackDeviceInfoCount = 0; + pContext->captureDeviceInfoCount = 0; + + // Now enumerate over available devices. + result = pContext->onEnumDevices(pContext, mal_context_get_devices__enum_callback, NULL); + if (result == MAL_SUCCESS) { + // Playback devices. + if (ppPlaybackDeviceInfos != NULL) { + *ppPlaybackDeviceInfos = pContext->pDeviceInfos; + } + if (pPlaybackDeviceCount != NULL) { + *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; + } + + // Capture devices. + if (ppCaptureDeviceInfos != NULL) { + *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; // Capture devices come after playback devices. + } + if (pCaptureDeviceCount != NULL) { + *pCaptureDeviceCount = pContext->captureDeviceInfoCount; + } + } + } + mal_mutex_unlock(&pContext->deviceEnumLock); + + return result; +} + +mal_result mal_context_get_device_info(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + // NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. + if (pContext == NULL || pDeviceInfo == NULL) { + return MAL_INVALID_ARGS; + } + + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + + // Help the backend out by copying over the device ID if we have one. + if (pDeviceID != NULL) { + mal_copy_memory(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); + } + + // The backend may have an optimized device info retrieval function. If so, try that first. + if (pContext->onGetDeviceInfo != NULL) { + mal_result result; + mal_mutex_lock(&pContext->deviceInfoLock); + { + result = pContext->onGetDeviceInfo(pContext, type, pDeviceID, shareMode, &deviceInfo); + } + mal_mutex_unlock(&pContext->deviceInfoLock); + + // Clamp ranges. + deviceInfo.minChannels = mal_max(deviceInfo.minChannels, MAL_MIN_CHANNELS); + deviceInfo.maxChannels = mal_min(deviceInfo.maxChannels, MAL_MAX_CHANNELS); + deviceInfo.minSampleRate = mal_max(deviceInfo.minSampleRate, MAL_MIN_SAMPLE_RATE); + deviceInfo.maxSampleRate = mal_min(deviceInfo.maxSampleRate, MAL_MAX_SAMPLE_RATE); + + *pDeviceInfo = deviceInfo; + return result; + } + + // Getting here means onGetDeviceInfo has not been set. + return MAL_ERROR; +} + + mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice) { - if (pDevice == NULL) { - return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); - } - if (pConfig == NULL) { - return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pConfig == NULL).", MAL_INVALID_ARGS); + if (pContext == NULL) { + return mal_device_init_ex(NULL, 0, NULL, type, pDeviceID, pConfig, pUserData, pDevice); + } + + + if (pDevice == NULL) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_init() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); + } + + // The config is allowed to be NULL, in which case we default to mal_device_config_init_default(). + mal_device_config config; + if (pConfig == NULL) { + config = mal_device_config_init_default(); + } else { + config = *pConfig; + } + + // Basic config validation. + if (config.channels > MAL_MAX_CHANNELS) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_init() called with an invalid config. Channel count cannot exceed 32.", MAL_INVALID_DEVICE_CONFIG); + } + if (!mal__is_channel_map_valid(config.channelMap, config.channels)) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_init() called with invalid config. Channel map is invalid.", MAL_INVALID_DEVICE_CONFIG); } - // Make a copy of the config to ensure we don't override the caller's object. - mal_device_config config = *pConfig; mal_zero_object(pDevice); pDevice->pContext = pContext; + pDevice->initConfig = config; // Set the user data and log callback ASAP to ensure it is available for the entire initialization process. pDevice->pUserData = pUserData; @@ -9665,34 +20645,37 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi } } - - if (pContext == NULL) { - return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pContext == NULL).", MAL_INVALID_ARGS); + if (pDeviceID == NULL) { + pDevice->isDefaultDevice = MAL_TRUE; } - // Basic config validation. + // When passing in 0 for the format/channels/rate/chmap it means the device will be using whatever is chosen by the backend. If everything is set + // to defaults it means the format conversion pipeline will run on a fast path where data transfer is just passed straight through to the backend. + if (config.format == mal_format_unknown) { + config.format = MAL_DEFAULT_FORMAT; + pDevice->usingDefaultFormat = MAL_TRUE; + } if (config.channels == 0) { - return mal_post_error(pDevice, "mal_device_init() called with an invalid config. Channel count must be greater than 0.", MAL_INVALID_DEVICE_CONFIG); + config.channels = MAL_DEFAULT_CHANNELS; + pDevice->usingDefaultChannels = MAL_TRUE; } - if (config.channels > MAL_MAX_CHANNELS) { - return mal_post_error(pDevice, "mal_device_init() called with an invalid config. Channel count cannot exceed 18.", MAL_INVALID_DEVICE_CONFIG); - } - if (config.sampleRate == 0) { - return mal_post_error(pDevice, "mal_device_init() called with an invalid config. Sample rate must be greater than 0.", MAL_INVALID_DEVICE_CONFIG); + config.sampleRate = MAL_DEFAULT_SAMPLE_RATE; + pDevice->usingDefaultSampleRate = MAL_TRUE; } - - if (!mal__is_channel_map_valid(pConfig->channelMap, pConfig->channels)) { - return mal_post_error(pDevice, "mal_device_init() called with invalid arguments. Channel map is invalid.", MAL_INVALID_DEVICE_CONFIG); + if (config.channelMap[0] == MAL_CHANNEL_NONE) { + pDevice->usingDefaultChannelMap = MAL_TRUE; } - // Default buffer size and periods. - if (config.bufferSizeInFrames == 0) { - config.bufferSizeInFrames = (config.sampleRate/1000) * MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS; + // Default buffer size. + if (config.bufferSizeInMilliseconds == 0 && config.bufferSizeInFrames == 0) { + config.bufferSizeInMilliseconds = (config.performanceProfile == mal_performance_profile_low_latency) ? MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY : MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE; pDevice->usingDefaultBufferSize = MAL_TRUE; } + + // Default periods. if (config.periods == 0) { config.periods = MAL_DEFAULT_PERIODS; pDevice->usingDefaultPeriods = MAL_TRUE; @@ -9701,9 +20684,10 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi pDevice->type = type; pDevice->format = config.format; pDevice->channels = config.channels; - mal_copy_memory(config.channelMap, config.channelMap, sizeof(config.channelMap[0]) * config.channels); pDevice->sampleRate = config.sampleRate; + mal_copy_memory(pDevice->channelMap, config.channelMap, sizeof(config.channelMap[0]) * config.channels); pDevice->bufferSizeInFrames = config.bufferSizeInFrames; + pDevice->bufferSizeInMilliseconds = config.bufferSizeInMilliseconds; pDevice->periods = config.periods; // The internal format, channel count and sample rate can be modified by the backend. @@ -9713,7 +20697,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi mal_copy_memory(pDevice->internalChannelMap, pDevice->channelMap, sizeof(pDevice->channelMap)); if (mal_mutex_init(pContext, &pDevice->lock) != MAL_SUCCESS) { - return mal_post_error(pDevice, "Failed to create mutex.", MAL_FAILED_TO_CREATE_MUTEX); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to create mutex.", MAL_FAILED_TO_CREATE_MUTEX); } // When the device is started, the worker thread is the one that does the actual startup of the backend device. We @@ -9723,86 +20707,28 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi // semaphore is also used to wake up the worker thread. if (mal_event_init(pContext, &pDevice->wakeupEvent) != MAL_SUCCESS) { mal_mutex_uninit(&pDevice->lock); - return mal_post_error(pDevice, "Failed to create worker thread wakeup event.", MAL_FAILED_TO_CREATE_EVENT); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to create worker thread wakeup event.", MAL_FAILED_TO_CREATE_EVENT); } if (mal_event_init(pContext, &pDevice->startEvent) != MAL_SUCCESS) { mal_event_uninit(&pDevice->wakeupEvent); mal_mutex_uninit(&pDevice->lock); - return mal_post_error(pDevice, "Failed to create worker thread start event.", MAL_FAILED_TO_CREATE_EVENT); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to create worker thread start event.", MAL_FAILED_TO_CREATE_EVENT); } if (mal_event_init(pContext, &pDevice->stopEvent) != MAL_SUCCESS) { mal_event_uninit(&pDevice->startEvent); mal_event_uninit(&pDevice->wakeupEvent); mal_mutex_uninit(&pDevice->lock); - return mal_post_error(pDevice, "Failed to create worker thread stop event.", MAL_FAILED_TO_CREATE_EVENT); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to create worker thread stop event.", MAL_FAILED_TO_CREATE_EVENT); } - mal_result result = MAL_NO_BACKEND; - switch (pContext->backend) - { - #ifdef MAL_HAS_WASAPI - case mal_backend_wasapi: - { - result = mal_device_init__wasapi(pContext, type, pDeviceID, &config, pDevice); - } break; - #endif - #ifdef MAL_HAS_DSOUND - case mal_backend_dsound: - { - result = mal_device_init__dsound(pContext, type, pDeviceID, &config, pDevice); - } break; - #endif - #ifdef MAL_HAS_WINMM - case mal_backend_winmm: - { - result = mal_device_init__winmm(pContext, type, pDeviceID, &config, pDevice); - } break; - #endif - #ifdef MAL_HAS_ALSA - case mal_backend_alsa: - { - result = mal_device_init__alsa(pContext, type, pDeviceID, &config, pDevice); - } break; - #endif - #ifdef MAL_HAS_OSS - case mal_backend_oss: - { - result = mal_device_init__oss(pContext, type, pDeviceID, &config, pDevice); - } break; - #endif - #ifdef MAL_HAS_OPENSL - case mal_backend_opensl: - { - result = mal_device_init__opensl(pContext, type, pDeviceID, &config, pDevice); - } break; - #endif - #ifdef MAL_HAS_OPENAL - case mal_backend_openal: - { - result = mal_device_init__openal(pContext, type, pDeviceID, &config, pDevice); - } break; - #endif - #ifdef MAL_HAS_SDL - case mal_backend_sdl: - { - result = mal_device_init__sdl(pContext, type, pDeviceID, &config, pDevice); - } break; - #endif - #ifdef MAL_HAS_NULL - case mal_backend_null: - { - result = mal_device_init__null(pContext, type, pDeviceID, &config, pDevice); - } break; - #endif - - default: break; - } - + mal_result result = pContext->onDeviceInit(pContext, type, pDeviceID, &config, pDevice); if (result != MAL_SUCCESS) { return MAL_NO_BACKEND; // The error message will have been posted with mal_post_error() by the source of the error so don't bother calling it here. } + mal_device__post_init_setup(pDevice); + // If the backend did not fill out a name for the device, try a generic method. if (pDevice->name[0] == '\0') { @@ -9810,9 +20736,9 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi // We failed to get the device name, so fall back to some generic names. if (pDeviceID == NULL) { if (type == mal_device_type_playback) { - mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Default Playback Device", (size_t)-1); + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { - mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Default Capture Device", (size_t)-1); + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } } else { if (type == mal_device_type_playback) { @@ -9825,41 +20751,12 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi } - // We need a DSP object which is where samples are moved through in order to convert them to the - // format required by the backend. - mal_dsp_config dspConfig; - dspConfig.cacheSizeInFrames = pDevice->bufferSizeInFrames; - if (type == mal_device_type_playback) { - dspConfig.formatIn = pDevice->format; - dspConfig.channelsIn = pDevice->channels; - dspConfig.sampleRateIn = pDevice->sampleRate; - mal_copy_memory(dspConfig.channelMapIn, pDevice->channelMap, sizeof(dspConfig.channelMapIn)); - dspConfig.formatOut = pDevice->internalFormat; - dspConfig.channelsOut = pDevice->internalChannels; - dspConfig.sampleRateOut = pDevice->internalSampleRate; - mal_copy_memory(dspConfig.channelMapOut, pDevice->internalChannelMap, sizeof(dspConfig.channelMapOut)); - mal_dsp_init(&dspConfig, mal_device__on_read_from_client, pDevice, &pDevice->dsp); - } else { - dspConfig.formatIn = pDevice->internalFormat; - dspConfig.channelsIn = pDevice->internalChannels; - dspConfig.sampleRateIn = pDevice->internalSampleRate; - mal_copy_memory(dspConfig.channelMapIn, pDevice->internalChannelMap, sizeof(dspConfig.channelMapIn)); - dspConfig.formatOut = pDevice->format; - dspConfig.channelsOut = pDevice->channels; - dspConfig.sampleRateOut = pDevice->sampleRate; - mal_copy_memory(dspConfig.channelMapOut, pDevice->channelMap, sizeof(dspConfig.channelMapOut)); - mal_dsp_init(&dspConfig, mal_device__on_read_from_device, pDevice, &pDevice->dsp); - } - - - - // Some backends don't require the worker thread. - if (pContext->backend != mal_backend_opensl && pContext->backend != mal_backend_sdl) { + if (!mal_context_is_backend_asynchronous(pContext)) { // The worker thread. if (mal_thread_create(pContext, &pDevice->thread, mal_worker_thread, pDevice) != MAL_SUCCESS) { mal_device_uninit(pDevice); - return mal_post_error(pDevice, "Failed to create worker thread.", MAL_FAILED_TO_CREATE_THREAD); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to create worker thread.", MAL_FAILED_TO_CREATE_THREAD); } // Wait for the worker thread to put the device into it's stopped state for real. @@ -9868,10 +20765,56 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi mal_device__set_state(pDevice, MAL_STATE_STOPPED); } + +#ifdef MAL_DEBUG_OUTPUT + printf("[WASAPI] %s (%s)\n", pDevice->name, (pDevice->type == mal_device_type_playback) ? "Playback" : "Capture"); + printf(" Format: %s -> %s\n", mal_get_format_name(pDevice->format), mal_get_format_name(pDevice->internalFormat)); + printf(" Channels: %d -> %d\n", pDevice->channels, pDevice->internalChannels); + printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->internalSampleRate); +#endif + + mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STOPPED); return MAL_SUCCESS; } +mal_result mal_device_init_ex(const mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pContextConfig, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice) +{ + mal_context* pContext = (mal_context*)mal_malloc(sizeof(*pContext)); + if (pContext == NULL) { + return MAL_OUT_OF_MEMORY; + } + + mal_backend* pBackendsToIterate = (mal_backend*)backends; + mal_uint32 backendsToIterateCount = backendCount; + if (pBackendsToIterate == NULL) { + pBackendsToIterate = (mal_backend*)g_malDefaultBackends; + backendsToIterateCount = mal_countof(g_malDefaultBackends); + } + + mal_result result = MAL_NO_BACKEND; + + for (mal_uint32 iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { + result = mal_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); + if (result == MAL_SUCCESS) { + result = mal_device_init(pContext, type, pDeviceID, pConfig, pUserData, pDevice); + if (result == MAL_SUCCESS) { + break; // Success. + } else { + mal_context_uninit(pContext); // Failure. + } + } + } + + if (result != MAL_SUCCESS) { + mal_free(pContext); + return result; + } + + pDevice->isOwnerOfContext = MAL_TRUE; + return result; +} + void mal_device_uninit(mal_device* pDevice) { if (!mal_device__is_initialized(pDevice)) return; @@ -9888,61 +20831,22 @@ void mal_device_uninit(mal_device* pDevice) mal_device__set_state(pDevice, MAL_STATE_UNINITIALIZED); // Wake up the worker thread and wait for it to properly terminate. - if (pDevice->pContext->backend != mal_backend_opensl && pDevice->pContext->backend != mal_backend_sdl) { + if (!mal_context_is_backend_asynchronous(pDevice->pContext)) { mal_event_signal(&pDevice->wakeupEvent); mal_thread_wait(&pDevice->thread); } + pDevice->pContext->onDeviceUninit(pDevice); + mal_event_uninit(&pDevice->stopEvent); mal_event_uninit(&pDevice->startEvent); mal_event_uninit(&pDevice->wakeupEvent); mal_mutex_uninit(&pDevice->lock); -#ifdef MAL_HAS_WASAPI - if (pDevice->pContext->backend == mal_backend_wasapi) { - mal_device_uninit__wasapi(pDevice); + if (pDevice->isOwnerOfContext) { + mal_context_uninit(pDevice->pContext); + mal_free(pDevice->pContext); } -#endif -#ifdef MAL_HAS_DSOUND - if (pDevice->pContext->backend == mal_backend_dsound) { - mal_device_uninit__dsound(pDevice); - } -#endif -#ifdef MAL_HAS_WINMM - if (pDevice->pContext->backend == mal_backend_winmm) { - mal_device_uninit__winmm(pDevice); - } -#endif -#ifdef MAL_HAS_ALSA - if (pDevice->pContext->backend == mal_backend_alsa) { - mal_device_uninit__alsa(pDevice); - } -#endif -#ifdef MAL_HAS_OSS - if (pDevice->pContext->backend == mal_backend_oss) { - mal_device_uninit__oss(pDevice); - } -#endif -#ifdef MAL_HAS_OPENSL - if (pDevice->pContext->backend == mal_backend_opensl) { - mal_device_uninit__opensl(pDevice); - } -#endif -#ifdef MAL_HAS_OPENAL - if (pDevice->pContext->backend == mal_backend_openal) { - mal_device_uninit__openal(pDevice); - } -#endif -#ifdef MAL_HAS_SDL - if (pDevice->pContext->backend == mal_backend_sdl) { - mal_device_uninit__sdl(pDevice); - } -#endif -#ifdef MAL_HAS_NULL - if (pDevice->pContext->backend == mal_backend_null) { - mal_device_uninit__null(pDevice); - } -#endif mal_zero_object(pDevice); } @@ -9967,8 +20871,8 @@ void mal_device_set_stop_callback(mal_device* pDevice, mal_stop_proc proc) mal_result mal_device_start(mal_device* pDevice) { - if (pDevice == NULL) return mal_post_error(pDevice, "mal_device_start() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); - if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); + if (pDevice == NULL) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); + if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); mal_result result = MAL_ERROR; mal_mutex_lock(&pDevice->lock); @@ -9977,40 +20881,30 @@ mal_result mal_device_start(mal_device* pDevice) // a bug with the application. if (mal_device__get_state(pDevice) == MAL_STATE_STARTING) { mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, "mal_device_start() called while another thread is already starting it.", MAL_DEVICE_ALREADY_STARTING); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called while another thread is already starting it.", MAL_DEVICE_ALREADY_STARTING); } if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, "mal_device_start() called for a device that's already started.", MAL_DEVICE_ALREADY_STARTED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for a device that's already started.", MAL_DEVICE_ALREADY_STARTED); } // The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy. if (mal_device__get_state(pDevice) != MAL_STATE_STOPPED) { mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, "mal_device_start() called while another thread is in the process of stopping it.", MAL_DEVICE_BUSY); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called while another thread is in the process of stopping it.", MAL_DEVICE_BUSY); } mal_device__set_state(pDevice, MAL_STATE_STARTING); // Asynchronous backends need to be handled differently. -#ifdef MAL_HAS_OPENSL - if (pDevice->pContext->backend == mal_backend_opensl) { - result = mal_device__start_backend__opensl(pDevice); + if (mal_context_is_backend_asynchronous(pDevice->pContext)) { + result = pDevice->pContext->onDeviceStart(pDevice); if (result == MAL_SUCCESS) { mal_device__set_state(pDevice, MAL_STATE_STARTED); } - } else -#endif -#ifdef MAL_HAS_SDL - if (pDevice->pContext->backend == mal_backend_sdl) { - result = mal_device__start_backend__sdl(pDevice); - if (result == MAL_SUCCESS) { - mal_device__set_state(pDevice, MAL_STATE_STARTED); - } - } else -#endif - // Synchronous backends. - { + } else { + // Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the + // thread and then wait for the start event. mal_event_signal(&pDevice->wakeupEvent); // Wait for the worker thread to finish starting the device. Note that the worker thread will be the one @@ -10026,8 +20920,8 @@ mal_result mal_device_start(mal_device* pDevice) mal_result mal_device_stop(mal_device* pDevice) { - if (pDevice == NULL) return mal_post_error(pDevice, "mal_device_stop() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); - if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); + if (pDevice == NULL) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); + if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); mal_result result = MAL_ERROR; mal_mutex_lock(&pDevice->lock); @@ -10036,17 +20930,17 @@ mal_result mal_device_stop(mal_device* pDevice) // a bug with the application. if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) { mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, "mal_device_stop() called while another thread is already stopping it.", MAL_DEVICE_ALREADY_STOPPING); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called while another thread is already stopping it.", MAL_DEVICE_ALREADY_STOPPING); } if (mal_device__get_state(pDevice) == MAL_STATE_STOPPED) { mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, "mal_device_stop() called for a device that's already stopped.", MAL_DEVICE_ALREADY_STOPPED); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for a device that's already stopped.", MAL_DEVICE_ALREADY_STOPPED); } // The device needs to be in a started state. If it's not, we just let the caller know the device is busy. if (mal_device__get_state(pDevice) != MAL_STATE_STARTED) { mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, "mal_device_stop() called while another thread is in the process of starting it.", MAL_DEVICE_BUSY); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called while another thread is in the process of starting it.", MAL_DEVICE_BUSY); } mal_device__set_state(pDevice, MAL_STATE_STOPPING); @@ -10054,21 +20948,14 @@ mal_result mal_device_stop(mal_device* pDevice) // There's no need to wake up the thread like we do when starting. // Asynchronous backends need to be handled differently. -#ifdef MAL_HAS_OPENSL - if (pDevice->pContext->backend == mal_backend_opensl) { - mal_device__stop_backend__opensl(pDevice); - } else -#endif -#ifdef MAL_HAS_SDL - if (pDevice->pContext->backend == mal_backend_sdl) { - mal_device__stop_backend__sdl(pDevice); - } else -#endif - // Synchronous backends. - { + if (mal_context_is_backend_asynchronous(pDevice->pContext)) { + pDevice->pContext->onDeviceStop(pDevice); + } else { + // Synchronous backends. + // When we get here the worker thread is likely in a wait state while waiting for the backend device to deliver or request // audio data. We need to force these to return as quickly as possible. - mal_device__break_main_loop(pDevice); + pDevice->pContext->onDeviceBreakMainLoop(pDevice); // We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be // the one who puts the device into the stopped state. Don't call mal_device__set_state() here. @@ -10090,20 +20977,7 @@ mal_bool32 mal_device_is_started(mal_device* pDevice) mal_uint32 mal_device_get_buffer_size_in_bytes(mal_device* pDevice) { if (pDevice == NULL) return 0; - return pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); -} - -mal_uint32 mal_get_sample_size_in_bytes(mal_format format) -{ - mal_uint32 sizes[] = { - 0, // unknown - 1, // u8 - 2, // s16 - 3, // s24 - 4, // s32 - 4, // f32 - }; - return sizes[format]; + return pDevice->bufferSizeInFrames * pDevice->channels * mal_get_bytes_per_sample(pDevice->format); } mal_context_config mal_context_config_init(mal_log_proc onLog) @@ -10116,84 +20990,4153 @@ mal_context_config mal_context_config_init(mal_log_proc onLog) return config; } -mal_device_config mal_device_config_init(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback, mal_send_proc onSendCallback) + +mal_device_config mal_device_config_init_default() { mal_device_config config; mal_zero_object(&config); + return config; +} + +mal_device_config mal_device_config_init_default_capture(mal_recv_proc onRecvCallback) +{ + mal_device_config config = mal_device_config_init_default(); + config.onRecvCallback = onRecvCallback; + + return config; +} + +mal_device_config mal_device_config_init_default_playback(mal_send_proc onSendCallback) +{ + mal_device_config config = mal_device_config_init_default(); + config.onSendCallback = onSendCallback; + + return config; +} + + +mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_recv_proc onRecvCallback, mal_send_proc onSendCallback) +{ + mal_device_config config = mal_device_config_init_default(); + config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.onRecvCallback = onRecvCallback; config.onSendCallback = onSendCallback; - switch (channels) - { - case 1: - { - config.channelMap[0] = MAL_CHANNEL_FRONT_CENTER; - } break; - - case 2: - { - config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - } break; - - case 3: - { - config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - config.channelMap[2] = MAL_CHANNEL_LFE; - } break; - - case 4: - { - config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - config.channelMap[2] = MAL_CHANNEL_BACK_LEFT; - config.channelMap[3] = MAL_CHANNEL_BACK_RIGHT; - } break; - - case 5: - { - config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - config.channelMap[2] = MAL_CHANNEL_BACK_LEFT; - config.channelMap[3] = MAL_CHANNEL_BACK_RIGHT; - config.channelMap[4] = MAL_CHANNEL_LFE; - } break; - - case 6: - { - config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - config.channelMap[2] = MAL_CHANNEL_FRONT_CENTER; - config.channelMap[3] = MAL_CHANNEL_LFE; - config.channelMap[4] = MAL_CHANNEL_BACK_LEFT; - config.channelMap[5] = MAL_CHANNEL_BACK_RIGHT; - } break; - - case 8: - { - config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; - config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; - config.channelMap[2] = MAL_CHANNEL_FRONT_CENTER; - config.channelMap[3] = MAL_CHANNEL_LFE; - config.channelMap[4] = MAL_CHANNEL_BACK_LEFT; - config.channelMap[5] = MAL_CHANNEL_BACK_RIGHT; - config.channelMap[6] = MAL_CHANNEL_SIDE_LEFT; - config.channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; - } break; - - default: - { - // Just leave it all blank in this case. This will use the same mapping as the device's native mapping. - } break; + if (channels > 0) { + if (channelMap == NULL) { + if (channels > 8) { + mal_zero_memory(config.channelMap, sizeof(mal_channel)*MAL_MAX_CHANNELS); + } else { + mal_get_standard_channel_map(mal_standard_channel_map_default, channels, config.channelMap); + } + } else { + mal_copy_memory(config.channelMap, channelMap, sizeof(config.channelMap)); + } + } else { + mal_zero_memory(config.channelMap, sizeof(mal_channel)*MAL_MAX_CHANNELS); } return config; } +#endif // MAL_NO_DEVICE_IO + + +void mal_get_standard_channel_map_microsoft(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + // Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config + switch (channels) + { + case 1: + { + channelMap[0] = MAL_CHANNEL_MONO; + } break; + + case 2: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + } break; + + case 3: // Not defined, but best guess. + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 4: + { +#ifndef MAL_USE_QUAD_MICROSOFT_CHANNEL_MAP + // Surround. Using the Surround profile has the advantage of the 3rd channel (MAL_CHANNEL_FRONT_CENTER) mapping nicely + // with higher channel counts. + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_BACK_CENTER; +#else + // Quad. + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; +#endif + } break; + + case 5: // Not defined, but best guess. + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_BACK_LEFT; + channelMap[4] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 6: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_LFE; + channelMap[4] = MAL_CHANNEL_SIDE_LEFT; + channelMap[5] = MAL_CHANNEL_SIDE_RIGHT; + } break; + + case 7: // Not defined, but best guess. + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_LFE; + channelMap[4] = MAL_CHANNEL_BACK_CENTER; + channelMap[5] = MAL_CHANNEL_SIDE_LEFT; + channelMap[6] = MAL_CHANNEL_SIDE_RIGHT; + } break; + + case 8: + default: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_LFE; + channelMap[4] = MAL_CHANNEL_BACK_LEFT; + channelMap[5] = MAL_CHANNEL_BACK_RIGHT; + channelMap[6] = MAL_CHANNEL_SIDE_LEFT; + channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + } break; + } + + // Remainder. + if (channels > 8) { + for (mal_uint32 iChannel = 8; iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-8)); + } + } +} + +void mal_get_standard_channel_map_alsa(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + switch (channels) + { + case 1: + { + channelMap[0] = MAL_CHANNEL_MONO; + } break; + + case 2: + { + channelMap[0] = MAL_CHANNEL_LEFT; + channelMap[1] = MAL_CHANNEL_RIGHT; + } break; + + case 3: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 4: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 5: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 6: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + channelMap[5] = MAL_CHANNEL_LFE; + } break; + + case 7: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + channelMap[5] = MAL_CHANNEL_LFE; + channelMap[6] = MAL_CHANNEL_BACK_CENTER; + } break; + + case 8: + default: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + channelMap[5] = MAL_CHANNEL_LFE; + channelMap[6] = MAL_CHANNEL_SIDE_LEFT; + channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + } break; + } + + // Remainder. + if (channels > 8) { + for (mal_uint32 iChannel = 8; iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-8)); + } + } +} + +void mal_get_standard_channel_map_rfc3551(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + switch (channels) + { + case 1: + { + channelMap[0] = MAL_CHANNEL_MONO; + } break; + + case 2: + { + channelMap[0] = MAL_CHANNEL_LEFT; + channelMap[1] = MAL_CHANNEL_RIGHT; + } break; + + case 3: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 4: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_CENTER; + channelMap[2] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[3] = MAL_CHANNEL_BACK_CENTER; + } break; + + case 5: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_BACK_LEFT; + channelMap[4] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 6: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_SIDE_LEFT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[4] = MAL_CHANNEL_SIDE_RIGHT; + channelMap[5] = MAL_CHANNEL_BACK_CENTER; + } break; + } + + // Remainder. + if (channels > 8) { + for (mal_uint32 iChannel = 6; iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-6)); + } + } +} + +void mal_get_standard_channel_map_flac(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + switch (channels) + { + case 1: + { + channelMap[0] = MAL_CHANNEL_MONO; + } break; + + case 2: + { + channelMap[0] = MAL_CHANNEL_LEFT; + channelMap[1] = MAL_CHANNEL_RIGHT; + } break; + + case 3: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 4: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 5: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_BACK_LEFT; + channelMap[4] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 6: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_LFE; + channelMap[4] = MAL_CHANNEL_BACK_LEFT; + channelMap[5] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 7: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_LFE; + channelMap[4] = MAL_CHANNEL_BACK_CENTER; + channelMap[5] = MAL_CHANNEL_SIDE_LEFT; + channelMap[6] = MAL_CHANNEL_SIDE_RIGHT; + } break; + + case 8: + default: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_LFE; + channelMap[4] = MAL_CHANNEL_BACK_LEFT; + channelMap[5] = MAL_CHANNEL_BACK_RIGHT; + channelMap[6] = MAL_CHANNEL_SIDE_LEFT; + channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + } break; + } + + // Remainder. + if (channels > 8) { + for (mal_uint32 iChannel = 8; iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-8)); + } + } +} + +void mal_get_standard_channel_map_vorbis(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + // In Vorbis' type 0 channel mapping, the first two channels are not always the standard left/right - it + // will have the center speaker where the right usually goes. Why?! + switch (channels) + { + case 1: + { + channelMap[0] = MAL_CHANNEL_MONO; + } break; + + case 2: + { + channelMap[0] = MAL_CHANNEL_LEFT; + channelMap[1] = MAL_CHANNEL_RIGHT; + } break; + + case 3: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_CENTER; + channelMap[2] = MAL_CHANNEL_FRONT_RIGHT; + } break; + + case 4: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 5: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_CENTER; + channelMap[2] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[3] = MAL_CHANNEL_BACK_LEFT; + channelMap[4] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 6: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_CENTER; + channelMap[2] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[3] = MAL_CHANNEL_BACK_LEFT; + channelMap[4] = MAL_CHANNEL_BACK_RIGHT; + channelMap[5] = MAL_CHANNEL_LFE; + } break; + + case 7: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_CENTER; + channelMap[2] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[3] = MAL_CHANNEL_SIDE_LEFT; + channelMap[4] = MAL_CHANNEL_SIDE_RIGHT; + channelMap[5] = MAL_CHANNEL_BACK_CENTER; + channelMap[6] = MAL_CHANNEL_LFE; + } break; + + case 8: + default: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_CENTER; + channelMap[2] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[3] = MAL_CHANNEL_SIDE_LEFT; + channelMap[4] = MAL_CHANNEL_SIDE_RIGHT; + channelMap[5] = MAL_CHANNEL_BACK_LEFT; + channelMap[6] = MAL_CHANNEL_BACK_RIGHT; + channelMap[7] = MAL_CHANNEL_LFE; + } break; + } + + // Remainder. + if (channels > 8) { + for (mal_uint32 iChannel = 8; iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-8)); + } + } +} + +void mal_get_standard_channel_map_sound4(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + switch (channels) + { + case 1: + { + channelMap[0] = MAL_CHANNEL_MONO; + } break; + + case 2: + { + channelMap[0] = MAL_CHANNEL_LEFT; + channelMap[1] = MAL_CHANNEL_RIGHT; + } break; + + case 3: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_CENTER; + } break; + + case 4: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 5: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 6: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + channelMap[5] = MAL_CHANNEL_LFE; + } break; + + case 7: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + channelMap[5] = MAL_CHANNEL_BACK_CENTER; + channelMap[6] = MAL_CHANNEL_LFE; + } break; + + case 8: + default: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + channelMap[5] = MAL_CHANNEL_LFE; + channelMap[6] = MAL_CHANNEL_SIDE_LEFT; + channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + } break; + } + + // Remainder. + if (channels > 8) { + for (mal_uint32 iChannel = 8; iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-8)); + } + } +} + +void mal_get_standard_channel_map_sndio(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + switch (channels) + { + case 1: + { + channelMap[0] = MAL_CHANNEL_MONO; + } break; + + case 2: + { + channelMap[0] = MAL_CHANNEL_LEFT; + channelMap[1] = MAL_CHANNEL_RIGHT; + } break; + + case 3: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 4: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 5: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 6: + default: + { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + channelMap[5] = MAL_CHANNEL_LFE; + } break; + } + + // Remainder. + if (channels > 6) { + for (mal_uint32 iChannel = 6; iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-6)); + } + } +} + +void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + switch (standardChannelMap) + { + case mal_standard_channel_map_alsa: + { + mal_get_standard_channel_map_alsa(channels, channelMap); + } break; + + case mal_standard_channel_map_rfc3551: + { + mal_get_standard_channel_map_rfc3551(channels, channelMap); + } break; + + case mal_standard_channel_map_flac: + { + mal_get_standard_channel_map_flac(channels, channelMap); + } break; + + case mal_standard_channel_map_vorbis: + { + mal_get_standard_channel_map_vorbis(channels, channelMap); + } break; + + case mal_standard_channel_map_sound4: + { + mal_get_standard_channel_map_sound4(channels, channelMap); + } break; + + case mal_standard_channel_map_sndio: + { + mal_get_standard_channel_map_sndio(channels, channelMap); + } break; + + case mal_standard_channel_map_microsoft: + default: + { + mal_get_standard_channel_map_microsoft(channels, channelMap); + } break; + } +} + +void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels) +{ + if (pOut != NULL && pIn != NULL && channels > 0) { + mal_copy_memory(pOut, pIn, sizeof(*pOut) * channels); + } +} + +mal_bool32 mal_channel_map_valid(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + if (channelMap == NULL) { + return MAL_FALSE; + } + + // A channel count of 0 is invalid. + if (channels == 0) { + return MAL_FALSE; + } + + // It does not make sense to have a mono channel when there is more than 1 channel. + if (channels > 1) { + for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { + if (channelMap[iChannel] == MAL_CHANNEL_MONO) { + return MAL_FALSE; + } + } + } + + return MAL_TRUE; +} + +mal_bool32 mal_channel_map_equal(mal_uint32 channels, const mal_channel channelMapA[MAL_MAX_CHANNELS], const mal_channel channelMapB[MAL_MAX_CHANNELS]) +{ + if (channelMapA == channelMapB) { + return MAL_FALSE; + } + + if (channels == 0 || channels > MAL_MAX_CHANNELS) { + return MAL_FALSE; + } + + for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { + if (channelMapA[iChannel] != channelMapB[iChannel]) { + return MAL_FALSE; + } + } + + return MAL_TRUE; +} + +mal_bool32 mal_channel_map_blank(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { + if (channelMap[iChannel] != MAL_CHANNEL_NONE) { + return MAL_FALSE; + } + } + + return MAL_TRUE; +} + +mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS], mal_channel channelPosition) +{ + for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { + if (channelMap[iChannel] == channelPosition) { + return MAL_TRUE; + } + } + + return MAL_FALSE; +} + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Format Conversion. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//#define MAL_USE_REFERENCE_CONVERSION_APIS 1 +//#define MAL_USE_SSE + +void mal_copy_memory_64(void* dst, const void* src, mal_uint64 sizeInBytes) +{ +#if 0xFFFFFFFFFFFFFFFF <= MAL_SIZE_MAX + mal_copy_memory(dst, src, (size_t)sizeInBytes); +#else + while (sizeInBytes > 0) { + mal_uint64 bytesToCopyNow = sizeInBytes; + if (bytesToCopyNow > MAL_SIZE_MAX) { + bytesToCopyNow = MAL_SIZE_MAX; + } + + mal_copy_memory(dst, src, (size_t)bytesToCopyNow); // Safe cast to size_t. + + sizeInBytes -= bytesToCopyNow; + dst = ( void*)(( mal_uint8*)dst + bytesToCopyNow); + src = (const void*)((const mal_uint8*)src + bytesToCopyNow); + } +#endif +} + +void mal_zero_memory_64(void* dst, mal_uint64 sizeInBytes) +{ +#if 0xFFFFFFFFFFFFFFFF <= MAL_SIZE_MAX + mal_zero_memory(dst, (size_t)sizeInBytes); +#else + while (sizeInBytes > 0) { + mal_uint64 bytesToZeroNow = sizeInBytes; + if (bytesToZeroNow > MAL_SIZE_MAX) { + bytesToZeroNow = MAL_SIZE_MAX; + } + + mal_zero_memory(dst, (size_t)bytesToZeroNow); // Safe cast to size_t. + + sizeInBytes -= bytesToZeroNow; + dst = (void*)((mal_uint8*)dst + bytesToZeroNow); + } +#endif +} + + +// u8 +void mal_pcm_u8_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + mal_copy_memory_64(dst, src, count * sizeof(mal_uint8)); +} + + +void mal_pcm_u8_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int16* dst_s16 = (mal_int16*)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int16 x = src_u8[i]; + x = x - 128; + x = x << 8; + dst_s16[i] = x; + } +} + +void mal_pcm_u8_to_s16__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s16__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_u8_to_s16__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_u8_to_s16__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_u8_to_s16__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s16__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_u8_to_s16__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_u8_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_u8_to_s16__reference(dst, src, count, ditherMode); +#else + mal_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_u8_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_s24 = (mal_uint8*)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int16 x = src_u8[i]; + x = x - 128; + + dst_s24[i*3+0] = 0; + dst_s24[i*3+1] = 0; + dst_s24[i*3+2] = (mal_uint8)((mal_int8)x); + } +} + +void mal_pcm_u8_to_s24__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_u8_to_s24__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_u8_to_s24__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_u8_to_s24__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s24__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_u8_to_s24__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_u8_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_u8_to_s24__reference(dst, src, count, ditherMode); +#else + mal_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_u8_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int32* dst_s32 = (mal_int32*)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = src_u8[i]; + x = x - 128; + x = x << 24; + dst_s32[i] = x; + } +} + +void mal_pcm_u8_to_s32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_u8_to_s32__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_u8_to_s32__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_u8_to_s32__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s32__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_u8_to_s32__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_u8_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_u8_to_s32__reference(dst, src, count, ditherMode); +#else + mal_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_u8_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + float* dst_f32 = (float*)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)src_u8[i]; + x = x * 0.00784313725490196078f; // 0..255 to 0..2 + x = x - 1; // 0..2 to -1..1 + + dst_f32[i] = x; + } +} + +void mal_pcm_u8_to_f32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_u8_to_f32__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_u8_to_f32__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_u8_to_f32__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_f32__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_u8_to_f32__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_u8_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_u8_to_f32__reference(dst, src, count, ditherMode); +#else + mal_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +#endif +} + + + +void mal_pcm_interleave_u8__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_uint8** src_u8 = (const mal_uint8**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; + } + } +} + +void mal_pcm_interleave_u8__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_uint8** src_u8 = (const mal_uint8**)src; + + if (channels == 1) { + mal_copy_memory_64(dst, src[0], frameCount * sizeof(mal_uint8)); + } else if (channels == 2) { + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; + dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; + } + } else { + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; + } + } + } +} + +void mal_pcm_interleave_u8(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_u8__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_u8__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_u8__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8** dst_u8 = (mal_uint8**)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; + } + } +} + +void mal_pcm_deinterleave_u8__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_u8(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); +#endif +} + + +// s16 +void mal_pcm_s16_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + if (ditherMode == mal_dither_mode_none) { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int16 x = src_s16[i]; + x = x >> 8; + x = x + 128; + dst_u8[i] = (mal_uint8)x; + } + } else { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int16 x = src_s16[i]; + + // Dither. Don't overflow. + mal_int32 dither = mal_dither_s32(ditherMode, -0x80, 0x7F); + if ((x + dither) <= 0x7FFF) { + x = (mal_int16)(x + dither); + } else { + x = 0x7FFF; + } + + x = x >> 8; + x = x + 128; + dst_u8[i] = (mal_uint8)x; + } + } +} + +void mal_pcm_s16_to_u8__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s16_to_u8__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s16_to_u8__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s16_to_u8__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_u8__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s16_to_u8__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s16_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s16_to_u8__reference(dst, src, count, ditherMode); +#else + mal_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_s16_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + mal_copy_memory_64(dst, src, count * sizeof(mal_int16)); +} + + +void mal_pcm_s16_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_s24 = (mal_uint8*)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s24[i*3+0] = 0; + dst_s24[i*3+1] = (mal_uint8)(src_s16[i] & 0xFF); + dst_s24[i*3+2] = (mal_uint8)(src_s16[i] >> 8); + } +} + +void mal_pcm_s16_to_s24__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s16_to_s24__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s16_to_s24__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s16_to_s24__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s24__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s16_to_s24__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s16_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s16_to_s24__reference(dst, src, count, ditherMode); +#else + mal_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_s16_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int32* dst_s32 = (mal_int32*)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s32[i] = src_s16[i] << 16; + } +} + +void mal_pcm_s16_to_s32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s16_to_s32__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s16_to_s32__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s16_to_s32__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s32__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s16_to_s32__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s16_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s16_to_s32__reference(dst, src, count, ditherMode); +#else + mal_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_s16_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + float* dst_f32 = (float*)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)src_s16[i]; + +#if 0 + // The accurate way. + x = x + 32768.0f; // -32768..32767 to 0..65535 + x = x * 0.00003051804379339284f; // 0..65536 to 0..2 + x = x - 1; // 0..2 to -1..1 +#else + // The fast way. + x = x * 0.000030517578125f; // -32768..32767 to -1..0.999969482421875 +#endif + + dst_f32[i] = x; + } +} + +void mal_pcm_s16_to_f32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s16_to_f32__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s16_to_f32__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s16_to_f32__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_f32__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s16_to_f32__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s16_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s16_to_f32__reference(dst, src, count, ditherMode); +#else + mal_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_interleave_s16__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_int16* dst_s16 = (mal_int16*)dst; + const mal_int16** src_s16 = (const mal_int16**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; + } + } +} + +void mal_pcm_interleave_s16__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_interleave_s16__reference(dst, src, frameCount, channels); +} + +void mal_pcm_interleave_s16(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_s16__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_s16__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_s16__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_int16** dst_s16 = (mal_int16**)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; + } + } +} + +void mal_pcm_deinterleave_s16__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_s16(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); +#endif +} + + +// s24 +void mal_pcm_s24_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_uint8* src_s24 = (const mal_uint8*)src; + + if (ditherMode == mal_dither_mode_none) { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int8 x = (mal_int8)src_s24[i*3 + 2] + 128; + dst_u8[i] = (mal_uint8)x; + } + } else { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = (mal_int32)(((mal_uint32)(src_s24[i*3+0]) << 8) | ((mal_uint32)(src_s24[i*3+1]) << 16) | ((mal_uint32)(src_s24[i*3+2])) << 24); + + // Dither. Don't overflow. + mal_int32 dither = mal_dither_s32(ditherMode, -0x800000, 0x7FFFFF); + if ((mal_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 24; + x = x + 128; + dst_u8[i] = (mal_uint8)x; + } + } +} + +void mal_pcm_s24_to_u8__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s24_to_u8__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s24_to_u8__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s24_to_u8__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_u8__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s24_to_u8__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s24_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s24_to_u8__reference(dst, src, count, ditherMode); +#else + mal_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_s24_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_int16* dst_s16 = (mal_int16*)dst; + const mal_uint8* src_s24 = (const mal_uint8*)src; + + if (ditherMode == mal_dither_mode_none) { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_uint16 dst_lo = ((mal_uint16)src_s24[i*3 + 1]); + mal_uint16 dst_hi = ((mal_uint16)src_s24[i*3 + 2]) << 8; + dst_s16[i] = (mal_int16)dst_lo | dst_hi; + } + } else { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = (mal_int32)(((mal_uint32)(src_s24[i*3+0]) << 8) | ((mal_uint32)(src_s24[i*3+1]) << 16) | ((mal_uint32)(src_s24[i*3+2])) << 24); + + // Dither. Don't overflow. + mal_int32 dither = mal_dither_s32(ditherMode, -0x8000, 0x7FFF); + if ((mal_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 16; + dst_s16[i] = (mal_int16)x; + } + } +} + +void mal_pcm_s24_to_s16__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s16__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s24_to_s16__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s24_to_s16__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s24_to_s16__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s16__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s24_to_s16__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s24_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s24_to_s16__reference(dst, src, count, ditherMode); +#else + mal_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_s24_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_copy_memory_64(dst, src, count * 3); +} + + +void mal_pcm_s24_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int32* dst_s32 = (mal_int32*)dst; + const mal_uint8* src_s24 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s32[i] = (mal_int32)(((mal_uint32)(src_s24[i*3+0]) << 8) | ((mal_uint32)(src_s24[i*3+1]) << 16) | ((mal_uint32)(src_s24[i*3+2])) << 24); + } +} + +void mal_pcm_s24_to_s32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s24_to_s32__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s24_to_s32__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s24_to_s32__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s32__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s24_to_s32__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s24_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s24_to_s32__reference(dst, src, count, ditherMode); +#else + mal_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_s24_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + float* dst_f32 = (float*)dst; + const mal_uint8* src_s24 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)(((mal_int32)(((mal_uint32)(src_s24[i*3+0]) << 8) | ((mal_uint32)(src_s24[i*3+1]) << 16) | ((mal_uint32)(src_s24[i*3+2])) << 24)) >> 8); + +#if 0 + // The accurate way. + x = x + 8388608.0f; // -8388608..8388607 to 0..16777215 + x = x * 0.00000011920929665621f; // 0..16777215 to 0..2 + x = x - 1; // 0..2 to -1..1 +#else + // The fast way. + x = x * 0.00000011920928955078125f; // -8388608..8388607 to -1..0.999969482421875 +#endif + + dst_f32[i] = x; + } +} + +void mal_pcm_s24_to_f32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s24_to_f32__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s24_to_f32__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s24_to_f32__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_f32__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s24_to_f32__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s24_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s24_to_f32__reference(dst, src, count, ditherMode); +#else + mal_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_interleave_s24__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8* dst8 = (mal_uint8*)dst; + const mal_uint8** src8 = (const mal_uint8**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; + dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; + dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; + } + } +} + +void mal_pcm_interleave_s24__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_interleave_s24__reference(dst, src, frameCount, channels); +} + +void mal_pcm_interleave_s24(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_s24__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_s24__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_s24__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8** dst8 = (mal_uint8**)dst; + const mal_uint8* src8 = (const mal_uint8*)src; + + mal_uint32 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; + dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; + dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; + } + } +} + +void mal_pcm_deinterleave_s24__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_s24(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); +#endif +} + + + +// s32 +void mal_pcm_s32_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + if (ditherMode == mal_dither_mode_none) { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = src_s32[i]; + x = x >> 24; + x = x + 128; + dst_u8[i] = (mal_uint8)x; + } + } else { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = src_s32[i]; + + // Dither. Don't overflow. + mal_int32 dither = mal_dither_s32(ditherMode, -0x800000, 0x7FFFFF); + if ((mal_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 24; + x = x + 128; + dst_u8[i] = (mal_uint8)x; + } + } +} + +void mal_pcm_s32_to_u8__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s32_to_u8__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s32_to_u8__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s32_to_u8__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_u8__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s32_to_u8__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s32_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s32_to_u8__reference(dst, src, count, ditherMode); +#else + mal_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_s32_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_int16* dst_s16 = (mal_int16*)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + if (ditherMode == mal_dither_mode_none) { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = src_s32[i]; + x = x >> 16; + dst_s16[i] = (mal_int16)x; + } + } else { + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = src_s32[i]; + + // Dither. Don't overflow. + mal_int32 dither = mal_dither_s32(ditherMode, -0x8000, 0x7FFF); + if ((mal_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 16; + dst_s16[i] = (mal_int16)x; + } + } +} + +void mal_pcm_s32_to_s16__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s16__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s32_to_s16__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s32_to_s16__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s32_to_s16__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s16__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s32_to_s16__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s32_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s32_to_s16__reference(dst, src, count, ditherMode); +#else + mal_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_s32_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; // No dithering for s32 -> s24. + + mal_uint8* dst_s24 = (mal_uint8*)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_uint32 x = (mal_uint32)src_s32[i]; + dst_s24[i*3+0] = (mal_uint8)((x & 0x0000FF00) >> 8); + dst_s24[i*3+1] = (mal_uint8)((x & 0x00FF0000) >> 16); + dst_s24[i*3+2] = (mal_uint8)((x & 0xFF000000) >> 24); + } +} + +void mal_pcm_s32_to_s24__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s32_to_s24__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s32_to_s24__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s32_to_s24__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s24__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s32_to_s24__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s32_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s32_to_s24__reference(dst, src, count, ditherMode); +#else + mal_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_s32_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_copy_memory_64(dst, src, count * sizeof(mal_int32)); +} + + +void mal_pcm_s32_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; // No dithering for s32 -> f32. + + float* dst_f32 = (float*)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + double x = src_s32[i]; + +#if 0 + x = x + 2147483648.0; + x = x * 0.0000000004656612873077392578125; + x = x - 1; +#else + x = x / 2147483648.0; +#endif + + dst_f32[i] = (float)x; + } +} + +void mal_pcm_s32_to_f32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_s32_to_f32__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_s32_to_f32__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_s32_to_f32__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_f32__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_s32_to_f32__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s32_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s32_to_f32__reference(dst, src, count, ditherMode); +#else + mal_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_interleave_s32__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_int32* dst_s32 = (mal_int32*)dst; + const mal_int32** src_s32 = (const mal_int32**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; + } + } +} + +void mal_pcm_interleave_s32__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_interleave_s32__reference(dst, src, frameCount, channels); +} + +void mal_pcm_interleave_s32(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_s32__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_s32__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_s32__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_int32** dst_s32 = (mal_int32**)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; + } + } +} + +void mal_pcm_deinterleave_s32__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_s32(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); +#endif +} + + +// f32 +void mal_pcm_f32_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_uint8* dst_u8 = (mal_uint8*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != mal_dither_mode_none) { + ditherMin = 1.0f / -128; + ditherMax = 1.0f / 127; + } + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = x + mal_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + x = x + 1; // -1..1 to 0..2 + x = x * 127.5f; // 0..2 to 0..255 + + dst_u8[i] = (mal_uint8)x; + } +} + +void mal_pcm_f32_to_u8__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_f32_to_u8__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_f32_to_u8__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_f32_to_u8__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_u8__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_f32_to_u8__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_f32_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_f32_to_u8__reference(dst, src, count, ditherMode); +#else + mal_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_f32_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_int16* dst_s16 = (mal_int16*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != mal_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = x + mal_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + +#if 0 + // The accurate way. + x = x + 1; // -1..1 to 0..2 + x = x * 32767.5f; // 0..2 to 0..65535 + x = x - 32768.0f; // 0...65535 to -32768..32767 +#else + // The fast way. + x = x * 32767.0f; // -1..1 to -32767..32767 +#endif + + dst_s16[i] = (mal_int16)x; + } +} + +void mal_pcm_f32_to_s16__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_int16* dst_s16 = (mal_int16*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != mal_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + mal_uint64 i = 0; + + // Unrolled. + mal_uint64 count4 = count >> 2; + for (mal_uint64 i4 = 0; i4 < count4; i4 += 1) { + float d0 = mal_dither_f32(ditherMode, ditherMin, ditherMax); + float d1 = mal_dither_f32(ditherMode, ditherMin, ditherMax); + float d2 = mal_dither_f32(ditherMode, ditherMin, ditherMax); + float d3 = mal_dither_f32(ditherMode, ditherMin, ditherMax); + + float x0 = src_f32[i+0]; + float x1 = src_f32[i+1]; + float x2 = src_f32[i+2]; + float x3 = src_f32[i+3]; + + x0 = x0 + d0; + x1 = x1 + d1; + x2 = x2 + d2; + x3 = x3 + d3; + + x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); + x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); + x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); + x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); + + x0 = x0 * 32767.0f; + x1 = x1 * 32767.0f; + x2 = x2 * 32767.0f; + x3 = x3 * 32767.0f; + + dst_s16[i+0] = (mal_int16)x0; + dst_s16[i+1] = (mal_int16)x1; + dst_s16[i+2] = (mal_int16)x2; + dst_s16[i+3] = (mal_int16)x3; + + i += 4; + } + + // Leftover. + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + mal_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + x = x * 32767.0f; // -1..1 to -32767..32767 + + dst_s16[i] = (mal_int16)x; + } +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_f32_to_s16__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + // Both the input and output buffers need to be aligned to 16 bytes. + if ((((mal_uintptr)dst & 15) != 0) || (((mal_uintptr)src & 15) != 0)) { + mal_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + + mal_int16* dst_s16 = (mal_int16*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != mal_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + mal_uint64 i = 0; + + // SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. + mal_uint64 count8 = count >> 3; + for (mal_uint64 i8 = 0; i8 < count8; i8 += 1) { + __m128 d0; + __m128 d1; + if (ditherMode == mal_dither_mode_none) { + d0 = _mm_set1_ps(0); + d1 = _mm_set1_ps(0); + } else if (ditherMode == mal_dither_mode_rectangle) { + d0 = _mm_set_ps( + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax) + ); + d1 = _mm_set_ps( + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax) + ); + } else { + d0 = _mm_set_ps( + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax) + ); + d1 = _mm_set_ps( + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax) + ); + } + + __m128 x0 = *((__m128*)(src_f32 + i) + 0); + __m128 x1 = *((__m128*)(src_f32 + i) + 1); + + x0 = _mm_add_ps(x0, d0); + x1 = _mm_add_ps(x1, d1); + + x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); + x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); + + _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); + + i += 8; + } + + + // Leftover. + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + mal_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + x = x * 32767.0f; // -1..1 to -32767..32767 + + dst_s16[i] = (mal_int16)x; + } +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_f32_to_s16__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + // Both the input and output buffers need to be aligned to 32 bytes. + if ((((mal_uintptr)dst & 31) != 0) || (((mal_uintptr)src & 31) != 0)) { + mal_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + + mal_int16* dst_s16 = (mal_int16*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != mal_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + mal_uint64 i = 0; + + // AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. + mal_uint64 count16 = count >> 4; + for (mal_uint64 i16 = 0; i16 < count16; i16 += 1) { + __m256 d0; + __m256 d1; + if (ditherMode == mal_dither_mode_none) { + d0 = _mm256_set1_ps(0); + d1 = _mm256_set1_ps(0); + } else if (ditherMode == mal_dither_mode_rectangle) { + d0 = _mm256_set_ps( + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax) + ); + d1 = _mm256_set_ps( + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax), + mal_dither_f32_rectangle(ditherMin, ditherMax) + ); + } else { + d0 = _mm256_set_ps( + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax) + ); + d1 = _mm256_set_ps( + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax), + mal_dither_f32_triangle(ditherMin, ditherMax) + ); + } + + __m256 x0 = *((__m256*)(src_f32 + i) + 0); + __m256 x1 = *((__m256*)(src_f32 + i) + 1); + + x0 = _mm256_add_ps(x0, d0); + x1 = _mm256_add_ps(x1, d1); + + x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f)); + x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f)); + + // Computing the final result is a little more complicated for AVX2 than SSE2. + __m256i i0 = _mm256_cvttps_epi32(x0); + __m256i i1 = _mm256_cvttps_epi32(x1); + __m256i p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32); + __m256i p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48); + __m256i r = _mm256_packs_epi32(p0, p1); + + _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r); + + i += 16; + } + + + // Leftover. + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + mal_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + x = x * 32767.0f; // -1..1 to -32767..32767 + + dst_s16[i] = (mal_int16)x; + } +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_f32_to_s16__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + // TODO: Convert this from AVX to AVX-512. + mal_pcm_f32_to_s16__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_f32_to_s16__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + // Both the input and output buffers need to be aligned to 16 bytes. + if ((((mal_uintptr)dst & 15) != 0) || (((mal_uintptr)src & 15) != 0)) { + mal_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + + mal_int16* dst_s16 = (mal_int16*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != mal_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + mal_uint64 i = 0; + + // NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. + mal_uint64 count8 = count >> 3; + for (mal_uint64 i8 = 0; i8 < count8; i8 += 1) { + float32x4_t d0; + float32x4_t d1; + if (ditherMode == mal_dither_mode_none) { + d0 = vmovq_n_f32(0); + d1 = vmovq_n_f32(0); + } else if (ditherMode == mal_dither_mode_rectangle) { + float d0v[4]; + d0v[0] = mal_dither_f32_rectangle(ditherMin, ditherMax); + d0v[1] = mal_dither_f32_rectangle(ditherMin, ditherMax); + d0v[2] = mal_dither_f32_rectangle(ditherMin, ditherMax); + d0v[3] = mal_dither_f32_rectangle(ditherMin, ditherMax); + d0 = vld1q_f32(d0v); + + float d1v[4]; + d1v[0] = mal_dither_f32_rectangle(ditherMin, ditherMax); + d1v[1] = mal_dither_f32_rectangle(ditherMin, ditherMax); + d1v[2] = mal_dither_f32_rectangle(ditherMin, ditherMax); + d1v[3] = mal_dither_f32_rectangle(ditherMin, ditherMax); + d1 = vld1q_f32(d1v); + } else { + float d0v[4]; + d0v[0] = mal_dither_f32_triangle(ditherMin, ditherMax); + d0v[1] = mal_dither_f32_triangle(ditherMin, ditherMax); + d0v[2] = mal_dither_f32_triangle(ditherMin, ditherMax); + d0v[3] = mal_dither_f32_triangle(ditherMin, ditherMax); + d0 = vld1q_f32(d0v); + + float d1v[4]; + d1v[0] = mal_dither_f32_triangle(ditherMin, ditherMax); + d1v[1] = mal_dither_f32_triangle(ditherMin, ditherMax); + d1v[2] = mal_dither_f32_triangle(ditherMin, ditherMax); + d1v[3] = mal_dither_f32_triangle(ditherMin, ditherMax); + d1 = vld1q_f32(d1v); + } + + float32x4_t x0 = *((float32x4_t*)(src_f32 + i) + 0); + float32x4_t x1 = *((float32x4_t*)(src_f32 + i) + 1); + + x0 = vaddq_f32(x0, d0); + x1 = vaddq_f32(x1, d1); + + x0 = vmulq_n_f32(x0, 32767.0f); + x1 = vmulq_n_f32(x1, 32767.0f); + + int32x4_t i0 = vcvtq_s32_f32(x0); + int32x4_t i1 = vcvtq_s32_f32(x1); + *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); + + i += 8; + } + + + // Leftover. + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + mal_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + x = x * 32767.0f; // -1..1 to -32767..32767 + + dst_s16[i] = (mal_int16)x; + } +} +#endif + +void mal_pcm_f32_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_f32_to_s16__reference(dst, src, count, ditherMode); +#else + mal_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_f32_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; // No dithering for f32 -> s24. + + mal_uint8* dst_s24 = (mal_uint8*)dst; + const float* src_f32 = (const float*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + +#if 0 + // The accurate way. + x = x + 1; // -1..1 to 0..2 + x = x * 8388607.5f; // 0..2 to 0..16777215 + x = x - 8388608.0f; // 0..16777215 to -8388608..8388607 +#else + // The fast way. + x = x * 8388607.0f; // -1..1 to -8388607..8388607 +#endif + + mal_int32 r = (mal_int32)x; + dst_s24[(i*3)+0] = (mal_uint8)((r & 0x0000FF) >> 0); + dst_s24[(i*3)+1] = (mal_uint8)((r & 0x00FF00) >> 8); + dst_s24[(i*3)+2] = (mal_uint8)((r & 0xFF0000) >> 16); + } +} + +void mal_pcm_f32_to_s24__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_f32_to_s24__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_f32_to_s24__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_f32_to_s24__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s24__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_f32_to_s24__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_f32_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_f32_to_s24__reference(dst, src, count, ditherMode); +#else + mal_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_f32_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; // No dithering for f32 -> s32. + + mal_int32* dst_s32 = (mal_int32*)dst; + const float* src_f32 = (const float*)src; + + mal_uint32 i; + for (i = 0; i < count; i += 1) { + double x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + +#if 0 + // The accurate way. + x = x + 1; // -1..1 to 0..2 + x = x * 2147483647.5; // 0..2 to 0..4294967295 + x = x - 2147483648.0; // 0...4294967295 to -2147483648..2147483647 +#else + // The fast way. + x = x * 2147483647.0; // -1..1 to -2147483647..2147483647 +#endif + + dst_s32[i] = (mal_int32)x; + } +} + +void mal_pcm_f32_to_s32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_pcm_f32_to_s32__sse2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX2) +void mal_pcm_f32_to_s32__avx2(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_AVX512) +void mal_pcm_f32_to_s32__avx512(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s32__avx2(dst, src, count, ditherMode); +} +#endif +#if defined(MAL_SUPPORT_NEON) +void mal_pcm_f32_to_s32__neon(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_f32_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_f32_to_s32__reference(dst, src, count, ditherMode); +#else + mal_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +#endif +} + + +void mal_pcm_f32_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_copy_memory_64(dst, src, count * sizeof(float)); +} + + +void mal_pcm_interleave_f32__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + float* dst_f32 = (float*)dst; + const float** src_f32 = (const float**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; + } + } +} + +void mal_pcm_interleave_f32__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_interleave_f32__reference(dst, src, frameCount, channels); +} + +void mal_pcm_interleave_f32(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_f32__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_f32__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_f32__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + float** dst_f32 = (float**)dst; + const float* src_f32 = (const float*)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; + } + } +} + +void mal_pcm_deinterleave_f32__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_f32(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_format_converter_init_callbacks__default(mal_format_converter* pConverter) +{ + mal_assert(pConverter != NULL); + + switch (pConverter->config.formatIn) + { + case mal_format_u8: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_u8_to_u8; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_u8_to_s16; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_u8_to_s24; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_u8_to_s32; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_u8_to_f32; + } + } break; + + case mal_format_s16: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s16_to_u8; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s16_to_s16; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s16_to_s24; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s16_to_s32; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s16_to_f32; + } + } break; + + case mal_format_s24: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s24_to_u8; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s24_to_s16; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s24_to_s24; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s24_to_s32; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s24_to_f32; + } + } break; + + case mal_format_s32: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s32_to_u8; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s32_to_s16; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s32_to_s24; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s32_to_s32; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s32_to_f32; + } + } break; + + case mal_format_f32: + default: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_f32_to_u8; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_f32_to_s16; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_f32_to_s24; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_f32_to_s32; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_f32_to_f32; + } + } break; + } +} + +#if defined(MAL_SUPPORT_SSE2) +void mal_format_converter_init_callbacks__sse2(mal_format_converter* pConverter) +{ + mal_assert(pConverter != NULL); + + switch (pConverter->config.formatIn) + { + case mal_format_u8: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_u8_to_u8; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_u8_to_s16__sse2; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_u8_to_s24__sse2; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_u8_to_s32__sse2; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_u8_to_f32__sse2; + } + } break; + + case mal_format_s16: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s16_to_u8__sse2; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s16_to_s16; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s16_to_s24__sse2; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s16_to_s32__sse2; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s16_to_f32__sse2; + } + } break; + + case mal_format_s24: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s24_to_u8__sse2; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s24_to_s16__sse2; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s24_to_s24; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s24_to_s32__sse2; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s24_to_f32__sse2; + } + } break; + + case mal_format_s32: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s32_to_u8__sse2; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s32_to_s16__sse2; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s32_to_s24__sse2; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s32_to_s32; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s32_to_f32__sse2; + } + } break; + + case mal_format_f32: + default: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_f32_to_u8__sse2; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_f32_to_s16__sse2; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_f32_to_s24__sse2; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_f32_to_s32__sse2; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_f32_to_f32; + } + } break; + } +} +#endif + +#if defined(MAL_SUPPORT_AVX2) +void mal_format_converter_init_callbacks__avx2(mal_format_converter* pConverter) +{ + mal_assert(pConverter != NULL); + + switch (pConverter->config.formatIn) + { + case mal_format_u8: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_u8_to_u8; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_u8_to_s16__avx2; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_u8_to_s24__avx2; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_u8_to_s32__avx2; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_u8_to_f32__avx2; + } + } break; + + case mal_format_s16: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s16_to_u8__avx2; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s16_to_s16; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s16_to_s24__avx2; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s16_to_s32__avx2; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s16_to_f32__avx2; + } + } break; + + case mal_format_s24: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s24_to_u8__avx2; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s24_to_s16__avx2; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s24_to_s24; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s24_to_s32__avx2; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s24_to_f32__avx2; + } + } break; + + case mal_format_s32: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s32_to_u8__avx2; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s32_to_s16__avx2; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s32_to_s24__avx2; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s32_to_s32; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s32_to_f32__avx2; + } + } break; + + case mal_format_f32: + default: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_f32_to_u8__avx2; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_f32_to_s16__avx2; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_f32_to_s24__avx2; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_f32_to_s32__avx2; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_f32_to_f32; + } + } break; + } +} +#endif + +#if defined(MAL_SUPPORT_AVX512) +void mal_format_converter_init_callbacks__avx512(mal_format_converter* pConverter) +{ + mal_assert(pConverter != NULL); + + switch (pConverter->config.formatIn) + { + case mal_format_u8: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_u8_to_u8; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_u8_to_s16__avx512; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_u8_to_s24__avx512; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_u8_to_s32__avx512; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_u8_to_f32__avx512; + } + } break; + + case mal_format_s16: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s16_to_u8__avx512; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s16_to_s16; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s16_to_s24__avx512; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s16_to_s32__avx512; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s16_to_f32__avx512; + } + } break; + + case mal_format_s24: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s24_to_u8__avx512; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s24_to_s16__avx512; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s24_to_s24; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s24_to_s32__avx512; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s24_to_f32__avx512; + } + } break; + + case mal_format_s32: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s32_to_u8__avx512; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s32_to_s16__avx512; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s32_to_s24__avx512; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s32_to_s32; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s32_to_f32__avx512; + } + } break; + + case mal_format_f32: + default: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_f32_to_u8__avx512; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_f32_to_s16__avx512; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_f32_to_s24__avx512; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_f32_to_s32__avx512; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_f32_to_f32; + } + } break; + } +} +#endif + +#if defined(MAL_SUPPORT_NEON) +void mal_format_converter_init_callbacks__neon(mal_format_converter* pConverter) +{ + mal_assert(pConverter != NULL); + + switch (pConverter->config.formatIn) + { + case mal_format_u8: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_u8_to_u8; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_u8_to_s16__neon; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_u8_to_s24__neon; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_u8_to_s32__neon; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_u8_to_f32__neon; + } + } break; + + case mal_format_s16: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s16_to_u8__neon; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s16_to_s16; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s16_to_s24__neon; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s16_to_s32__neon; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s16_to_f32__neon; + } + } break; + + case mal_format_s24: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s24_to_u8__neon; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s24_to_s16__neon; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s24_to_s24; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s24_to_s32__neon; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s24_to_f32__neon; + } + } break; + + case mal_format_s32: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s32_to_u8__neon; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s32_to_s16__neon; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s32_to_s24__neon; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s32_to_s32; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s32_to_f32__neon; + } + } break; + + case mal_format_f32: + default: + { + if (pConverter->config.formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_f32_to_u8__neon; + } else if (pConverter->config.formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_f32_to_s16__neon; + } else if (pConverter->config.formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_f32_to_s24__neon; + } else if (pConverter->config.formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_f32_to_s32__neon; + } else if (pConverter->config.formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_f32_to_f32; + } + } break; + } +} +#endif + +mal_result mal_format_converter_init(const mal_format_converter_config* pConfig, mal_format_converter* pConverter) +{ + if (pConverter == NULL) { + return MAL_INVALID_ARGS; + } + mal_zero_object(pConverter); + + if (pConfig == NULL) { + return MAL_INVALID_ARGS; + } + + pConverter->config = *pConfig; + + // SIMD + pConverter->useSSE2 = mal_has_sse2() && !pConfig->noSSE2; + pConverter->useAVX2 = mal_has_avx2() && !pConfig->noAVX2; + pConverter->useAVX512 = mal_has_avx512f() && !pConfig->noAVX512; + pConverter->useNEON = mal_has_neon() && !pConfig->noNEON; + +#if defined(MAL_SUPPORT_AVX512) + if (pConverter->useAVX512) { + mal_format_converter_init_callbacks__avx512(pConverter); + } else +#endif +#if defined(MAL_SUPPORT_AVX2) + if (pConverter->useAVX2) { + mal_format_converter_init_callbacks__avx2(pConverter); + } else +#endif +#if defined(MAL_SUPPORT_SSE2) + if (pConverter->useSSE2) { + mal_format_converter_init_callbacks__sse2(pConverter); + } else +#endif +#if defined(MAL_SUPPORT_NEON) + if (pConverter->useNEON) { + mal_format_converter_init_callbacks__neon(pConverter); + } else +#endif + { + mal_format_converter_init_callbacks__default(pConverter); + } + + switch (pConfig->formatOut) + { + case mal_format_u8: + { + pConverter->onInterleavePCM = mal_pcm_interleave_u8; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_u8; + } break; + case mal_format_s16: + { + pConverter->onInterleavePCM = mal_pcm_interleave_s16; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_s16; + } break; + case mal_format_s24: + { + pConverter->onInterleavePCM = mal_pcm_interleave_s24; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_s24; + } break; + case mal_format_s32: + { + pConverter->onInterleavePCM = mal_pcm_interleave_s32; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_s32; + } break; + case mal_format_f32: + default: + { + pConverter->onInterleavePCM = mal_pcm_interleave_f32; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_f32; + } break; + } + + return MAL_SUCCESS; +} + +mal_uint64 mal_format_converter_read(mal_format_converter* pConverter, mal_uint64 frameCount, void* pFramesOut, void* pUserData) +{ + if (pConverter == NULL || pFramesOut == NULL) { + return 0; + } + + mal_uint64 totalFramesRead = 0; + mal_uint32 sampleSizeIn = mal_get_bytes_per_sample(pConverter->config.formatIn); + mal_uint32 sampleSizeOut = mal_get_bytes_per_sample(pConverter->config.formatOut); + //mal_uint32 frameSizeIn = sampleSizeIn * pConverter->config.channels; + mal_uint32 frameSizeOut = sampleSizeOut * pConverter->config.channels; + mal_uint8* pNextFramesOut = (mal_uint8*)pFramesOut; + + if (pConverter->config.onRead != NULL) { + // Input data is interleaved. + if (pConverter->config.formatIn == pConverter->config.formatOut) { + // Pass through. + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > 0xFFFFFFFF) { + framesToReadRightNow = 0xFFFFFFFF; + } + + mal_uint32 framesJustRead = (mal_uint32)pConverter->config.onRead(pConverter, (mal_uint32)framesToReadRightNow, pNextFramesOut, pUserData); + if (framesJustRead == 0) { + break; + } + + totalFramesRead += framesJustRead; + pNextFramesOut += framesJustRead * frameSizeOut; + + if (framesJustRead < framesToReadRightNow) { + break; + } + } + } else { + // Conversion required. + MAL_ALIGN(MAL_SIMD_ALIGNMENT) mal_uint8 temp[MAL_MAX_CHANNELS * MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + mal_assert(sizeof(temp) <= 0xFFFFFFFF); + + mal_uint32 maxFramesToReadAtATime = sizeof(temp) / sampleSizeIn / pConverter->config.channels; + + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > maxFramesToReadAtATime) { + framesToReadRightNow = maxFramesToReadAtATime; + } + + mal_uint32 framesJustRead = (mal_uint32)pConverter->config.onRead(pConverter, (mal_uint32)framesToReadRightNow, temp, pUserData); + if (framesJustRead == 0) { + break; + } + + pConverter->onConvertPCM(pNextFramesOut, temp, framesJustRead*pConverter->config.channels, pConverter->config.ditherMode); + + totalFramesRead += framesJustRead; + pNextFramesOut += framesJustRead * frameSizeOut; + + if (framesJustRead < framesToReadRightNow) { + break; + } + } + } + } else { + // Input data is deinterleaved. If a conversion is required we need to do an intermediary step. + MAL_ALIGN(MAL_SIMD_ALIGNMENT) mal_uint8 tempSamplesOfOutFormat[MAL_MAX_CHANNELS * MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + mal_assert(sizeof(tempSamplesOfOutFormat) <= 0xFFFFFFFFF); + + void* ppTempSamplesOfOutFormat[MAL_MAX_CHANNELS]; + size_t splitBufferSizeOut; + mal_split_buffer(tempSamplesOfOutFormat, sizeof(tempSamplesOfOutFormat), pConverter->config.channels, MAL_SIMD_ALIGNMENT, (void**)&ppTempSamplesOfOutFormat, &splitBufferSizeOut); + + mal_uint32 maxFramesToReadAtATime = (mal_uint32)(splitBufferSizeOut / sampleSizeIn); + + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > maxFramesToReadAtATime) { + framesToReadRightNow = maxFramesToReadAtATime; + } + + mal_uint32 framesJustRead = 0; + + if (pConverter->config.formatIn == pConverter->config.formatOut) { + // Only interleaving. + framesJustRead = (mal_uint32)pConverter->config.onReadDeinterleaved(pConverter, (mal_uint32)framesToReadRightNow, ppTempSamplesOfOutFormat, pUserData); + if (framesJustRead == 0) { + break; + } + } else { + // Interleaving + Conversion. Convert first, then interleave. + MAL_ALIGN(MAL_SIMD_ALIGNMENT) mal_uint8 tempSamplesOfInFormat[MAL_MAX_CHANNELS * MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + + void* ppTempSamplesOfInFormat[MAL_MAX_CHANNELS]; + size_t splitBufferSizeIn; + mal_split_buffer(tempSamplesOfInFormat, sizeof(tempSamplesOfInFormat), pConverter->config.channels, MAL_SIMD_ALIGNMENT, (void**)&ppTempSamplesOfInFormat, &splitBufferSizeIn); + + if (framesToReadRightNow > (splitBufferSizeIn / sampleSizeIn)) { + framesToReadRightNow = (splitBufferSizeIn / sampleSizeIn); + } + + framesJustRead = (mal_uint32)pConverter->config.onReadDeinterleaved(pConverter, (mal_uint32)framesToReadRightNow, ppTempSamplesOfInFormat, pUserData); + if (framesJustRead == 0) { + break; + } + + for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) { + pConverter->onConvertPCM(ppTempSamplesOfOutFormat[iChannel], ppTempSamplesOfInFormat[iChannel], framesJustRead, pConverter->config.ditherMode); + } + } + + pConverter->onInterleavePCM(pNextFramesOut, (const void**)ppTempSamplesOfOutFormat, framesJustRead, pConverter->config.channels); + + totalFramesRead += framesJustRead; + pNextFramesOut += framesJustRead * frameSizeOut; + + if (framesJustRead < framesToReadRightNow) { + break; + } + } + } + + return totalFramesRead; +} + +mal_uint64 mal_format_converter_read_deinterleaved(mal_format_converter* pConverter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData) +{ + if (pConverter == NULL || ppSamplesOut == NULL) { + return 0; + } + + mal_uint64 totalFramesRead = 0; + mal_uint32 sampleSizeIn = mal_get_bytes_per_sample(pConverter->config.formatIn); + mal_uint32 sampleSizeOut = mal_get_bytes_per_sample(pConverter->config.formatOut); + + mal_uint8* ppNextSamplesOut[MAL_MAX_CHANNELS]; + mal_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pConverter->config.channels); + + if (pConverter->config.onRead != NULL) { + // Input data is interleaved. + MAL_ALIGN(MAL_SIMD_ALIGNMENT) mal_uint8 tempSamplesOfOutFormat[MAL_MAX_CHANNELS * MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + mal_assert(sizeof(tempSamplesOfOutFormat) <= 0xFFFFFFFF); + + mal_uint32 maxFramesToReadAtATime = sizeof(tempSamplesOfOutFormat) / sampleSizeIn / pConverter->config.channels; + + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > maxFramesToReadAtATime) { + framesToReadRightNow = maxFramesToReadAtATime; + } + + mal_uint32 framesJustRead = 0; + + if (pConverter->config.formatIn == pConverter->config.formatOut) { + // Only de-interleaving. + framesJustRead = (mal_uint32)pConverter->config.onRead(pConverter, (mal_uint32)framesToReadRightNow, tempSamplesOfOutFormat, pUserData); + if (framesJustRead == 0) { + break; + } + } else { + // De-interleaving + Conversion. Convert first, then de-interleave. + MAL_ALIGN(MAL_SIMD_ALIGNMENT) mal_uint8 tempSamplesOfInFormat[sizeof(tempSamplesOfOutFormat)]; + + framesJustRead = (mal_uint32)pConverter->config.onRead(pConverter, (mal_uint32)framesToReadRightNow, tempSamplesOfInFormat, pUserData); + if (framesJustRead == 0) { + break; + } + + pConverter->onConvertPCM(tempSamplesOfOutFormat, tempSamplesOfInFormat, framesJustRead * pConverter->config.channels, pConverter->config.ditherMode); + } + + pConverter->onDeinterleavePCM((void**)ppNextSamplesOut, tempSamplesOfOutFormat, framesJustRead, pConverter->config.channels); + + totalFramesRead += framesJustRead; + for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) { + ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut; + } + + if (framesJustRead < framesToReadRightNow) { + break; + } + } + } else { + // Input data is deinterleaved. + if (pConverter->config.formatIn == pConverter->config.formatOut) { + // Pass through. + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > 0xFFFFFFFF) { + framesToReadRightNow = 0xFFFFFFFF; + } + + mal_uint32 framesJustRead = (mal_uint32)pConverter->config.onReadDeinterleaved(pConverter, (mal_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pUserData); + if (framesJustRead == 0) { + break; + } + + totalFramesRead += framesJustRead; + for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) { + ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut; + } + + if (framesJustRead < framesToReadRightNow) { + break; + } + } + } else { + // Conversion required. + MAL_ALIGN(MAL_SIMD_ALIGNMENT) mal_uint8 temp[MAL_MAX_CHANNELS][MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + mal_assert(sizeof(temp) <= 0xFFFFFFFF); + + void* ppTemp[MAL_MAX_CHANNELS]; + size_t splitBufferSize; + mal_split_buffer(temp, sizeof(temp), pConverter->config.channels, MAL_SIMD_ALIGNMENT, (void**)&ppTemp, &splitBufferSize); + + mal_uint32 maxFramesToReadAtATime = (mal_uint32)(splitBufferSize / sampleSizeIn); + + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > maxFramesToReadAtATime) { + framesToReadRightNow = maxFramesToReadAtATime; + } + + mal_uint32 framesJustRead = (mal_uint32)pConverter->config.onReadDeinterleaved(pConverter, (mal_uint32)framesToReadRightNow, ppTemp, pUserData); + if (framesJustRead == 0) { + break; + } + + for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) { + pConverter->onConvertPCM(ppNextSamplesOut[iChannel], ppTemp[iChannel], framesJustRead, pConverter->config.ditherMode); + ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut; + } + + totalFramesRead += framesJustRead; + + if (framesJustRead < framesToReadRightNow) { + break; + } + } + } + } + + return totalFramesRead; +} + + +mal_format_converter_config mal_format_converter_config_init_new() +{ + mal_format_converter_config config; + mal_zero_object(&config); + + return config; +} + +mal_format_converter_config mal_format_converter_config_init(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_proc onRead, void* pUserData) +{ + mal_format_converter_config config = mal_format_converter_config_init_new(); + config.formatIn = formatIn; + config.formatOut = formatOut; + config.channels = channels; + config.onRead = onRead; + config.onReadDeinterleaved = NULL; + config.pUserData = pUserData; + + return config; +} + +mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_deinterleaved_proc onReadDeinterleaved, void* pUserData) +{ + mal_format_converter_config config = mal_format_converter_config_init(formatIn, formatOut, channels, NULL, pUserData); + config.onReadDeinterleaved = onReadDeinterleaved; + + return config; +} + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Channel Routing +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// -X = Left, +X = Right +// -Y = Bottom, +Y = Top +// -Z = Front, +Z = Back +typedef struct +{ + float x; + float y; + float z; +} mal_vec3; + +static MAL_INLINE mal_vec3 mal_vec3f(float x, float y, float z) +{ + mal_vec3 r; + r.x = x; + r.y = y; + r.z = z; + + return r; +} + +static MAL_INLINE mal_vec3 mal_vec3_add(mal_vec3 a, mal_vec3 b) +{ + return mal_vec3f( + a.x + b.x, + a.y + b.y, + a.z + b.z + ); +} + +static MAL_INLINE mal_vec3 mal_vec3_sub(mal_vec3 a, mal_vec3 b) +{ + return mal_vec3f( + a.x - b.x, + a.y - b.y, + a.z - b.z + ); +} + +static MAL_INLINE mal_vec3 mal_vec3_mul(mal_vec3 a, mal_vec3 b) +{ + return mal_vec3f( + a.x * b.x, + a.y * b.y, + a.z * b.z + ); +} + +static MAL_INLINE mal_vec3 mal_vec3_div(mal_vec3 a, mal_vec3 b) +{ + return mal_vec3f( + a.x / b.x, + a.y / b.y, + a.z / b.z + ); +} + +static MAL_INLINE float mal_vec3_dot(mal_vec3 a, mal_vec3 b) +{ + return a.x*b.x + a.y*b.y + a.z*b.z; +} + +static MAL_INLINE float mal_vec3_length2(mal_vec3 a) +{ + return mal_vec3_dot(a, a); +} + +static MAL_INLINE float mal_vec3_length(mal_vec3 a) +{ + return (float)sqrt(mal_vec3_length2(a)); +} + +static MAL_INLINE mal_vec3 mal_vec3_normalize(mal_vec3 a) +{ + float len = 1 / mal_vec3_length(a); + + mal_vec3 r; + r.x = a.x * len; + r.y = a.y * len; + r.z = a.z * len; + + return r; +} + +static MAL_INLINE float mal_vec3_distance(mal_vec3 a, mal_vec3 b) +{ + return mal_vec3_length(mal_vec3_sub(a, b)); +} + + +#define MAL_PLANE_LEFT 0 +#define MAL_PLANE_RIGHT 1 +#define MAL_PLANE_FRONT 2 +#define MAL_PLANE_BACK 3 +#define MAL_PLANE_BOTTOM 4 +#define MAL_PLANE_TOP 5 + +float g_malChannelPlaneRatios[MAL_CHANNEL_POSITION_COUNT][6] = { + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_NONE + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_MONO + { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_LEFT + { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT + { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_CENTER + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_LFE + { 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f}, // MAL_CHANNEL_BACK_LEFT + { 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f}, // MAL_CHANNEL_BACK_RIGHT + { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_LEFT_CENTER + { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT_CENTER + { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_CENTER + { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_SIDE_LEFT + { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_SIDE_RIGHT + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, // MAL_CHANNEL_TOP_CENTER + { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, // MAL_CHANNEL_TOP_FRONT_LEFT + { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, // MAL_CHANNEL_TOP_FRONT_CENTER + { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, // MAL_CHANNEL_TOP_FRONT_RIGHT + { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, // MAL_CHANNEL_TOP_BACK_LEFT + { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, // MAL_CHANNEL_TOP_BACK_CENTER + { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, // MAL_CHANNEL_TOP_BACK_RIGHT + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_0 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_1 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_2 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_3 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_4 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_5 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_6 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_7 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_8 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_9 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_10 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_11 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_12 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_13 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_14 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_15 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_16 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_17 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_18 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_19 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_20 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_21 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_22 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_23 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_24 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_25 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_26 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_27 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_28 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_29 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_30 + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_AUX_31 +}; + +float mal_calculate_channel_position_planar_weight(mal_channel channelPositionA, mal_channel channelPositionB) +{ + // Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to + // the following output configuration: + // + // - front/left + // - side/left + // - back/left + // + // The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount + // of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. + // + // Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left + // speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted + // from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would + // receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between + // the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works + // across 3 spatial dimensions. + // + // The first thing to do is figure out how each speaker's volume is spread over each of plane: + // - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane + // - side/left: 1 plane (left only) = 1/1 = entire volume from left plane + // - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane + // - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane + // + // The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other + // channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be + // taken by the other to produce the final contribution. + + // Contribution = Sum(Volume to Give * Volume to Take) + float contribution = + g_malChannelPlaneRatios[channelPositionA][0] * g_malChannelPlaneRatios[channelPositionB][0] + + g_malChannelPlaneRatios[channelPositionA][1] * g_malChannelPlaneRatios[channelPositionB][1] + + g_malChannelPlaneRatios[channelPositionA][2] * g_malChannelPlaneRatios[channelPositionB][2] + + g_malChannelPlaneRatios[channelPositionA][3] * g_malChannelPlaneRatios[channelPositionB][3] + + g_malChannelPlaneRatios[channelPositionA][4] * g_malChannelPlaneRatios[channelPositionB][4] + + g_malChannelPlaneRatios[channelPositionA][5] * g_malChannelPlaneRatios[channelPositionB][5]; + + return contribution; +} + +float mal_channel_router__calculate_input_channel_planar_weight(const mal_channel_router* pRouter, mal_channel channelPositionIn, mal_channel channelPositionOut) +{ + mal_assert(pRouter != NULL); + (void)pRouter; + + return mal_calculate_channel_position_planar_weight(channelPositionIn, channelPositionOut); +} + +mal_bool32 mal_channel_router__is_spatial_channel_position(const mal_channel_router* pRouter, mal_channel channelPosition) +{ + mal_assert(pRouter != NULL); + (void)pRouter; + + if (channelPosition == MAL_CHANNEL_NONE || channelPosition == MAL_CHANNEL_MONO || channelPosition == MAL_CHANNEL_LFE) { + return MAL_FALSE; + } + + for (int i = 0; i < 6; ++i) { + if (g_malChannelPlaneRatios[channelPosition][i] != 0) { + return MAL_TRUE; + } + } + + return MAL_FALSE; +} + +mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal_channel_router* pRouter) +{ + if (pRouter == NULL) { + return MAL_INVALID_ARGS; + } + + mal_zero_object(pRouter); + + if (pConfig == NULL) { + return MAL_INVALID_ARGS; + } + if (pConfig->onReadDeinterleaved == NULL) { + return MAL_INVALID_ARGS; + } + + if (!mal_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) { + return MAL_INVALID_ARGS; // Invalid input channel map. + } + if (!mal_channel_map_valid(pConfig->channelsOut, pConfig->channelMapOut)) { + return MAL_INVALID_ARGS; // Invalid output channel map. + } + + pRouter->config = *pConfig; + + // SIMD + pRouter->useSSE2 = mal_has_sse2() && !pConfig->noSSE2; + pRouter->useAVX2 = mal_has_avx2() && !pConfig->noAVX2; + pRouter->useAVX512 = mal_has_avx512f() && !pConfig->noAVX512; + pRouter->useNEON = mal_has_neon() && !pConfig->noNEON; + + // If the input and output channels and channel maps are the same we should use a passthrough. + if (pRouter->config.channelsIn == pRouter->config.channelsOut) { + if (mal_channel_map_equal(pRouter->config.channelsIn, pRouter->config.channelMapIn, pRouter->config.channelMapOut)) { + pRouter->isPassthrough = MAL_TRUE; + } + if (mal_channel_map_blank(pRouter->config.channelsIn, pRouter->config.channelMapIn) || mal_channel_map_blank(pRouter->config.channelsOut, pRouter->config.channelMapOut)) { + pRouter->isPassthrough = MAL_TRUE; + } + } + + // Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules: + // + // 1) If it's a passthrough, do nothing - it's just a simple memcpy(). + // 2) If the channel counts are the same and every channel position in the input map is present in the output map, use a + // simple shuffle. An example might be different 5.1 channel layouts. + // 3) Otherwise channels are blended based on spatial locality. + if (!pRouter->isPassthrough) { + if (pRouter->config.channelsIn == pRouter->config.channelsOut) { + mal_bool32 areAllChannelPositionsPresent = MAL_TRUE; + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_bool32 isInputChannelPositionInOutput = MAL_FALSE; + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + if (pRouter->config.channelMapIn[iChannelIn] == pRouter->config.channelMapOut[iChannelOut]) { + isInputChannelPositionInOutput = MAL_TRUE; + break; + } + } + + if (!isInputChannelPositionInOutput) { + areAllChannelPositionsPresent = MAL_FALSE; + break; + } + } + + if (areAllChannelPositionsPresent) { + pRouter->isSimpleShuffle = MAL_TRUE; + + // All the router will be doing is rearranging channels which means all we need to do is use a shuffling table which is just + // a mapping between the index of the input channel to the index of the output channel. + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + if (pRouter->config.channelMapIn[iChannelIn] == pRouter->config.channelMapOut[iChannelOut]) { + pRouter->shuffleTable[iChannelIn] = (mal_uint8)iChannelOut; + break; + } + } + } + } + } + } + + + // Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple + // shuffling. We use different algorithms for calculating weights depending on our mixing mode. + // + // In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just + // map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel + // map, nothing will be heard! + + // In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; + + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; + + if (channelPosIn == channelPosOut) { + pRouter->weights[iChannelIn][iChannelOut] = 1; + } + } + } + + // The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since + // they were handled in the pass above. + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; + + if (channelPosIn == MAL_CHANNEL_MONO) { + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; + + if (channelPosOut != MAL_CHANNEL_NONE && channelPosOut != MAL_CHANNEL_MONO && channelPosOut != MAL_CHANNEL_LFE) { + pRouter->weights[iChannelIn][iChannelOut] = 1; + } + } + } + } + + // The output mono channel is the average of all non-none, non-mono and non-lfe input channels. + { + mal_uint32 len = 0; + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; + + if (channelPosIn != MAL_CHANNEL_NONE && channelPosIn != MAL_CHANNEL_MONO && channelPosIn != MAL_CHANNEL_LFE) { + len += 1; + } + } + + if (len > 0) { + float monoWeight = 1.0f / len; + + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; + + if (channelPosOut == MAL_CHANNEL_MONO) { + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; + + if (channelPosIn != MAL_CHANNEL_NONE && channelPosIn != MAL_CHANNEL_MONO && channelPosIn != MAL_CHANNEL_LFE) { + pRouter->weights[iChannelIn][iChannelOut] += monoWeight; + } + } + } + } + } + } + + + // Input and output channels that are not present on the other side need to be blended in based on spatial locality. + if (pRouter->config.mixingMode != mal_channel_mix_mode_simple) { + // Unmapped input channels. + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; + + if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) { + if (!mal_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) { + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; + + if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) { + float weight = 0; + if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) { + weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut); + } + + // Only apply the weight if we haven't already got some contribution from the respective channels. + if (pRouter->weights[iChannelIn][iChannelOut] == 0) { + pRouter->weights[iChannelIn][iChannelOut] = weight; + } + } + } + } + } + } + + // Unmapped output channels. + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; + + if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) { + if (!mal_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) { + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; + + if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) { + float weight = 0; + if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) { + weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut); + } + + // Only apply the weight if we haven't already got some contribution from the respective channels. + if (pRouter->weights[iChannelIn][iChannelOut] == 0) { + pRouter->weights[iChannelIn][iChannelOut] = weight; + } + } + } + } + } + } + } + + return MAL_SUCCESS; +} + +static MAL_INLINE mal_bool32 mal_channel_router__can_use_sse2(mal_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn) +{ + return pRouter->useSSE2 && (((mal_uintptr)pSamplesOut & 15) == 0) && (((mal_uintptr)pSamplesIn & 15) == 0); +} + +static MAL_INLINE mal_bool32 mal_channel_router__can_use_avx2(mal_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn) +{ + return pRouter->useAVX2 && (((mal_uintptr)pSamplesOut & 31) == 0) && (((mal_uintptr)pSamplesIn & 31) == 0); +} + +static MAL_INLINE mal_bool32 mal_channel_router__can_use_avx512(mal_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn) +{ + return pRouter->useAVX512 && (((mal_uintptr)pSamplesOut & 63) == 0) && (((mal_uintptr)pSamplesIn & 63) == 0); +} + +static MAL_INLINE mal_bool32 mal_channel_router__can_use_neon(mal_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn) +{ + return pRouter->useNEON && (((mal_uintptr)pSamplesOut & 15) == 0) && (((mal_uintptr)pSamplesIn & 15) == 0); +} + +void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 frameCount, float** ppSamplesOut, const float** ppSamplesIn) +{ + mal_assert(pRouter != NULL); + mal_assert(pRouter->isPassthrough == MAL_FALSE); + + if (pRouter->isSimpleShuffle) { + // A shuffle is just a re-arrangement of channels and does not require any arithmetic. + mal_assert(pRouter->config.channelsIn == pRouter->config.channelsOut); + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_uint32 iChannelOut = pRouter->shuffleTable[iChannelIn]; + mal_copy_memory_64(ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn], frameCount * sizeof(float)); + } + } else { + // This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. + + // Clear. + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + mal_zero_memory_64(ppSamplesOut[iChannelOut], frameCount * sizeof(float)); + } + + // Accumulate. + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + mal_uint64 iFrame = 0; +#if defined(MAL_SUPPORT_NEON) + if (mal_channel_router__can_use_neon(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) { + float32x4_t weight = vmovq_n_f32(pRouter->weights[iChannelIn][iChannelOut]); + + mal_uint64 frameCount4 = frameCount/4; + for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) { + float32x4_t* pO = (float32x4_t*)ppSamplesOut[iChannelOut] + iFrame4; + float32x4_t* pI = (float32x4_t*)ppSamplesIn [iChannelIn ] + iFrame4; + *pO = vaddq_f32(*pO, vmulq_f32(*pI, weight)); + } + + iFrame += frameCount4*4; + } + else +#endif +#if defined(MAL_SUPPORT_AVX512) + if (mal_channel_router__can_use_avx512(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) { + __m512 weight = _mm512_set1_ps(pRouter->weights[iChannelIn][iChannelOut]); + + mal_uint64 frameCount16 = frameCount/16; + for (mal_uint64 iFrame16 = 0; iFrame16 < frameCount16; iFrame16 += 1) { + __m512* pO = (__m512*)ppSamplesOut[iChannelOut] + iFrame16; + __m512* pI = (__m512*)ppSamplesIn [iChannelIn ] + iFrame16; + *pO = _mm512_add_ps(*pO, _mm512_mul_ps(*pI, weight)); + } + + iFrame += frameCount16*16; + } + else +#endif +#if defined(MAL_SUPPORT_AVX2) + if (mal_channel_router__can_use_avx2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) { + __m256 weight = _mm256_set1_ps(pRouter->weights[iChannelIn][iChannelOut]); + + mal_uint64 frameCount8 = frameCount/8; + for (mal_uint64 iFrame8 = 0; iFrame8 < frameCount8; iFrame8 += 1) { + __m256* pO = (__m256*)ppSamplesOut[iChannelOut] + iFrame8; + __m256* pI = (__m256*)ppSamplesIn [iChannelIn ] + iFrame8; + *pO = _mm256_add_ps(*pO, _mm256_mul_ps(*pI, weight)); + } + + iFrame += frameCount8*8; + } + else +#endif +#if defined(MAL_SUPPORT_SSE2) + if (mal_channel_router__can_use_sse2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) { + __m128 weight = _mm_set1_ps(pRouter->weights[iChannelIn][iChannelOut]); + + mal_uint64 frameCount4 = frameCount/4; + for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) { + __m128* pO = (__m128*)ppSamplesOut[iChannelOut] + iFrame4; + __m128* pI = (__m128*)ppSamplesIn [iChannelIn ] + iFrame4; + *pO = _mm_add_ps(*pO, _mm_mul_ps(*pI, weight)); + } + + iFrame += frameCount4*4; + } else +#endif + { // Reference. + float weight0 = pRouter->weights[iChannelIn][iChannelOut]; + float weight1 = pRouter->weights[iChannelIn][iChannelOut]; + float weight2 = pRouter->weights[iChannelIn][iChannelOut]; + float weight3 = pRouter->weights[iChannelIn][iChannelOut]; + + mal_uint64 frameCount4 = frameCount/4; + for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) { + ppSamplesOut[iChannelOut][iFrame+0] += ppSamplesIn[iChannelIn][iFrame+0] * weight0; + ppSamplesOut[iChannelOut][iFrame+1] += ppSamplesIn[iChannelIn][iFrame+1] * weight1; + ppSamplesOut[iChannelOut][iFrame+2] += ppSamplesIn[iChannelIn][iFrame+2] * weight2; + ppSamplesOut[iChannelOut][iFrame+3] += ppSamplesIn[iChannelIn][iFrame+3] * weight3; + iFrame += 4; + } + } + + // Leftover. + for (; iFrame < frameCount; ++iFrame) { + ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->weights[iChannelIn][iChannelOut]; + } + } + } + } +} + +mal_uint64 mal_channel_router_read_deinterleaved(mal_channel_router* pRouter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData) +{ + if (pRouter == NULL || ppSamplesOut == NULL) { + return 0; + } + + // Fast path for a passthrough. + if (pRouter->isPassthrough) { + if (frameCount <= 0xFFFFFFFF) { + return (mal_uint32)pRouter->config.onReadDeinterleaved(pRouter, (mal_uint32)frameCount, ppSamplesOut, pUserData); + } else { + float* ppNextSamplesOut[MAL_MAX_CHANNELS]; + mal_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(float*) * pRouter->config.channelsOut); + + mal_uint64 totalFramesRead = 0; + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > 0xFFFFFFFF) { + framesToReadRightNow = 0xFFFFFFFF; + } + + mal_uint32 framesJustRead = (mal_uint32)pRouter->config.onReadDeinterleaved(pRouter, (mal_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pUserData); + if (framesJustRead == 0) { + break; + } + + totalFramesRead += framesJustRead; + for (mal_uint32 iChannel = 0; iChannel < pRouter->config.channelsOut; ++iChannel) { + ppNextSamplesOut[iChannel] += framesJustRead; + } + + if (framesJustRead < framesToReadRightNow) { + break; + } + } + } + } + + // Slower path for a non-passthrough. + float* ppNextSamplesOut[MAL_MAX_CHANNELS]; + mal_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(float*) * pRouter->config.channelsOut); + + MAL_ALIGN(MAL_SIMD_ALIGNMENT) float temp[MAL_MAX_CHANNELS * 256]; + mal_assert(sizeof(temp) <= 0xFFFFFFFF); + + float* ppTemp[MAL_MAX_CHANNELS]; + size_t maxBytesToReadPerFrameEachIteration; + mal_split_buffer(temp, sizeof(temp), pRouter->config.channelsIn, MAL_SIMD_ALIGNMENT, (void**)&ppTemp, &maxBytesToReadPerFrameEachIteration); + + size_t maxFramesToReadEachIteration = maxBytesToReadPerFrameEachIteration/sizeof(float); + + mal_uint64 totalFramesRead = 0; + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > maxFramesToReadEachIteration) { + framesToReadRightNow = maxFramesToReadEachIteration; + } + + mal_uint32 framesJustRead = pRouter->config.onReadDeinterleaved(pRouter, (mal_uint32)framesToReadRightNow, (void**)ppTemp, pUserData); + if (framesJustRead == 0) { + break; + } + + mal_channel_router__do_routing(pRouter, framesJustRead, (float**)ppNextSamplesOut, (const float**)ppTemp); // <-- Real work is done here. + + totalFramesRead += framesJustRead; + if (totalFramesRead < frameCount) { + for (mal_uint32 iChannel = 0; iChannel < pRouter->config.channelsIn; iChannel += 1) { + ppNextSamplesOut[iChannel] += framesJustRead; + } + } + + if (framesJustRead < framesToReadRightNow) { + break; + } + } + + return totalFramesRead; +} + +mal_channel_router_config mal_channel_router_config_init(mal_uint32 channelsIn, const mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint32 channelsOut, const mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_channel_mix_mode mixingMode, mal_channel_router_read_deinterleaved_proc onRead, void* pUserData) +{ + mal_channel_router_config config; + mal_zero_object(&config); + + config.channelsIn = channelsIn; + for (mal_uint32 iChannel = 0; iChannel < channelsIn; ++iChannel) { + config.channelMapIn[iChannel] = channelMapIn[iChannel]; + } + + config.channelsOut = channelsOut; + for (mal_uint32 iChannel = 0; iChannel < channelsOut; ++iChannel) { + config.channelMapOut[iChannel] = channelMapOut[iChannel]; + } + + config.mixingMode = mixingMode; + config.onReadDeinterleaved = onRead; + config.pUserData = pUserData; + + return config; +} + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -10202,255 +25145,382 @@ mal_device_config mal_device_config_init(mal_format format, mal_uint32 channels, // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void mal_src_cache_init(mal_src* pSRC, mal_src_cache* pCache) +#define mal_floorf(x) ((float)floor((double)(x))) +#define mal_sinf(x) ((float)sin((double)(x))) +#define mal_cosf(x) ((float)cos((double)(x))) + +static MAL_INLINE double mal_sinc(double x) +{ + if (x != 0) { + return sin(MAL_PI_D*x) / (MAL_PI_D*x); + } else { + return 1; + } +} + +#define mal_sincf(x) ((float)mal_sinc((double)(x))) + +mal_uint64 mal_calculate_frame_count_after_src(mal_uint32 sampleRateOut, mal_uint32 sampleRateIn, mal_uint64 frameCountIn) +{ + double srcRatio = (double)sampleRateOut / sampleRateIn; + double frameCountOutF = frameCountIn * srcRatio; + + mal_uint64 frameCountOut = (mal_uint64)frameCountOutF; + + // If the output frame count is fractional, make sure we add an extra frame to ensure there's enough room for that last sample. + if ((frameCountOutF - frameCountOut) > 0.0) { + frameCountOut += 1; + } + + return frameCountOut; +} + + +mal_uint64 mal_src_read_deinterleaved__passthrough(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); +mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); +mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData); + +void mal_src__build_sinc_table__sinc(mal_src* pSRC) { mal_assert(pSRC != NULL); - mal_assert(pCache != NULL); - pCache->pSRC = pSRC; - pCache->cachedFrameCount = 0; - pCache->iNextFrame = 0; + pSRC->sinc.table[0] = 1.0f; + for (mal_uint32 i = 1; i < mal_countof(pSRC->sinc.table); i += 1) { + double x = i*MAL_PI_D / MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION; + pSRC->sinc.table[i] = (float)(sin(x)/x); + } } -mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCount, float* pFramesOut) +void mal_src__build_sinc_table__rectangular(mal_src* pSRC) { - mal_assert(pCache != NULL); - mal_assert(pCache->pSRC != NULL); - mal_assert(pCache->pSRC->onRead != NULL); - mal_assert(frameCount > 0); - mal_assert(pFramesOut != NULL); + // This is the same as the base sinc table. + mal_src__build_sinc_table__sinc(pSRC); +} - mal_uint32 channels = pCache->pSRC->config.channels; +void mal_src__build_sinc_table__hann(mal_src* pSRC) +{ + mal_src__build_sinc_table__sinc(pSRC); - mal_uint32 totalFramesRead = 0; - while (frameCount > 0) { - // If there's anything in memory go ahead and copy that over first. - mal_uint32 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame; - mal_uint32 framesToReadFromMemory = frameCount; - if (framesToReadFromMemory > framesRemainingInMemory) { - framesToReadFromMemory = framesRemainingInMemory; - } + for (mal_uint32 i = 0; i < mal_countof(pSRC->sinc.table); i += 1) { + double x = pSRC->sinc.table[i]; + double N = MAL_SRC_SINC_MAX_WINDOW_WIDTH*2; + double n = ((double)(i) / MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION) + MAL_SRC_SINC_MAX_WINDOW_WIDTH; + double w = 0.5 * (1 - cos((2*MAL_PI_D*n) / (N))); - mal_copy_memory(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, framesToReadFromMemory * channels * sizeof(float)); - pCache->iNextFrame += framesToReadFromMemory; + pSRC->sinc.table[i] = (float)(x * w); + } +} - totalFramesRead += framesToReadFromMemory; - frameCount -= framesToReadFromMemory; - if (frameCount == 0) { - break; - } - - - // At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data. - mal_assert(frameCount > 0); - pFramesOut += framesToReadFromMemory * channels; - - pCache->iNextFrame = 0; - pCache->cachedFrameCount = 0; - if (pCache->pSRC->config.formatIn == mal_format_f32) { - // No need for a conversion - read straight into the cache. - mal_uint32 framesToReadFromClient = mal_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels; - if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) { - framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; - } - - pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData); - } else { - // A format conversion is required which means we need to use an intermediary buffer. - mal_uint8 pIntermediaryBuffer[sizeof(pCache->pCachedFrames)]; - mal_uint32 framesToReadFromClient = mal_min(mal_buffer_frame_capacity(pIntermediaryBuffer, channels, pCache->pSRC->config.formatIn), mal_buffer_frame_capacity(pCache->pCachedFrames, channels, mal_format_f32)); - if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) { - framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; - } - - pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pIntermediaryBuffer, pCache->pSRC->pUserData); - - // Convert to f32. - mal_pcm_convert(pCache->pCachedFrames, mal_format_f32, pIntermediaryBuffer, pCache->pSRC->config.formatIn, pCache->cachedFrameCount * channels); - } - - - // Get out of this loop if nothing was able to be retrieved. - if (pCache->cachedFrameCount == 0) { - break; - } +mal_result mal_src_init(const mal_src_config* pConfig, mal_src* pSRC) +{ + if (pSRC == NULL) { + return MAL_INVALID_ARGS; } - return totalFramesRead; -} - - -mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); -mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); - -mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC) -{ - if (pSRC == NULL) return MAL_INVALID_ARGS; mal_zero_object(pSRC); - if (pConfig == NULL || onRead == NULL) return MAL_INVALID_ARGS; - if (pConfig->channels == 0 || pConfig->channels > MAL_MAX_CHANNELS) return MAL_INVALID_ARGS; - - pSRC->config = *pConfig; - pSRC->onRead = onRead; - pSRC->pUserData = pUserData; - - if (pSRC->config.cacheSizeInFrames > MAL_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) { - pSRC->config.cacheSizeInFrames = MAL_SRC_CACHE_SIZE_IN_FRAMES; + if (pConfig == NULL || pConfig->onReadDeinterleaved == NULL) { + return MAL_INVALID_ARGS; + } + if (pConfig->channels == 0 || pConfig->channels > MAL_MAX_CHANNELS) { + return MAL_INVALID_ARGS; } - mal_src_cache_init(pSRC, &pSRC->cache); + pSRC->config = *pConfig; + + // SIMD + pSRC->useSSE2 = mal_has_sse2() && !pConfig->noSSE2; + pSRC->useAVX2 = mal_has_avx2() && !pConfig->noAVX2; + pSRC->useAVX512 = mal_has_avx512f() && !pConfig->noAVX512; + pSRC->useNEON = mal_has_neon() && !pConfig->noNEON; + + if (pSRC->config.algorithm == mal_src_algorithm_sinc) { + // Make sure the window width within bounds. + if (pSRC->config.sinc.windowWidth == 0) { + pSRC->config.sinc.windowWidth = MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH; + } + if (pSRC->config.sinc.windowWidth < MAL_SRC_SINC_MIN_WINDOW_WIDTH) { + pSRC->config.sinc.windowWidth = MAL_SRC_SINC_MIN_WINDOW_WIDTH; + } + if (pSRC->config.sinc.windowWidth > MAL_SRC_SINC_MAX_WINDOW_WIDTH) { + pSRC->config.sinc.windowWidth = MAL_SRC_SINC_MAX_WINDOW_WIDTH; + } + + // Set up the lookup table. + switch (pSRC->config.sinc.windowFunction) { + case mal_src_sinc_window_function_hann: mal_src__build_sinc_table__hann(pSRC); break; + case mal_src_sinc_window_function_rectangular: mal_src__build_sinc_table__rectangular(pSRC); break; + default: return MAL_INVALID_ARGS; // <-- Hitting this means the window function is unknown to mini_al. + } + } + + return MAL_SUCCESS; +} + +mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn) +{ + if (pSRC == NULL) { + return MAL_INVALID_ARGS; + } + + // Must have a sample rate of > 0. + if (sampleRateIn == 0) { + return MAL_INVALID_ARGS; + } + + mal_atomic_exchange_32(&pSRC->config.sampleRateIn, sampleRateIn); return MAL_SUCCESS; } mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut) { - if (pSRC == NULL) return MAL_INVALID_ARGS; + if (pSRC == NULL) { + return MAL_INVALID_ARGS; + } // Must have a sample rate of > 0. if (sampleRateOut == 0) { return MAL_INVALID_ARGS; } - pSRC->config.sampleRateOut = sampleRateOut; + mal_atomic_exchange_32(&pSRC->config.sampleRateOut, sampleRateOut); return MAL_SUCCESS; } -mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut) +mal_result mal_src_set_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut) { - return mal_src_read_frames_ex(pSRC, frameCount, pFramesOut, MAL_FALSE); + if (pSRC == NULL) { + return MAL_INVALID_ARGS; + } + + // Must have a sample rate of > 0. + if (sampleRateIn == 0 || sampleRateOut == 0) { + return MAL_INVALID_ARGS; + } + + mal_atomic_exchange_32(&pSRC->config.sampleRateIn, sampleRateIn); + mal_atomic_exchange_32(&pSRC->config.sampleRateOut, sampleRateOut); + + return MAL_SUCCESS; } -mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) +mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData) { - if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0; + if (pSRC == NULL || frameCount == 0 || ppSamplesOut == NULL) { + return 0; + } mal_src_algorithm algorithm = pSRC->config.algorithm; - - // Always use passthrough if the sample rates are the same. if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) { - algorithm = mal_src_algorithm_none; + //algorithm = mal_src_algorithm_none; } - // Could just use a function pointer instead of a switch for this... - switch (algorithm) - { - case mal_src_algorithm_none: return mal_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush); - case mal_src_algorithm_linear: return mal_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush); - default: return 0; + // Can use a function pointer for this. + switch (algorithm) { + case mal_src_algorithm_none: return mal_src_read_deinterleaved__passthrough(pSRC, frameCount, ppSamplesOut, pUserData); + case mal_src_algorithm_linear: return mal_src_read_deinterleaved__linear( pSRC, frameCount, ppSamplesOut, pUserData); + case mal_src_algorithm_sinc: return mal_src_read_deinterleaved__sinc( pSRC, frameCount, ppSamplesOut, pUserData); + default: break; } + + // Should never get here. + return 0; } -mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) +mal_uint64 mal_src_read_deinterleaved__passthrough(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData) { - mal_assert(pSRC != NULL); - mal_assert(frameCount > 0); - mal_assert(pFramesOut != NULL); - - (void)flush; // Passthrough need not care about flushing. - - // Fast path. No need for data conversion - just pass right through. - if (pSRC->config.formatIn == pSRC->config.formatOut) { - return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData); - } - - // Slower path. Need to do a format conversion. - mal_uint32 totalFramesRead = 0; - while (frameCount > 0) { - mal_uint8 pStagingBuffer[MAL_MAX_CHANNELS * 2048]; - mal_uint32 stagingBufferSizeInFrames = sizeof(pStagingBuffer) / mal_get_sample_size_in_bytes(pSRC->config.formatIn) / pSRC->config.channels; - mal_uint32 framesToRead = stagingBufferSizeInFrames; - if (framesToRead > frameCount) { - framesToRead = frameCount; + if (frameCount <= 0xFFFFFFFF) { + return pSRC->config.onReadDeinterleaved(pSRC, (mal_uint32)frameCount, ppSamplesOut, pUserData); + } else { + float* ppNextSamplesOut[MAL_MAX_CHANNELS]; + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) { + ppNextSamplesOut[iChannel] = (float*)ppSamplesOut[iChannel]; } - mal_uint32 framesRead = pSRC->onRead(pSRC, framesToRead, pStagingBuffer, pSRC->pUserData); - if (framesRead == 0) { - break; - } - - mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pStagingBuffer, pSRC->config.formatIn, framesRead * pSRC->config.channels); - - pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut)); - frameCount -= framesRead; - totalFramesRead += framesRead; - } - - return totalFramesRead; -} - -mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) -{ - mal_assert(pSRC != NULL); - mal_assert(frameCount > 0); - mal_assert(pFramesOut != NULL); - - // For linear SRC, the bin is only 2 frames: 1 prior, 1 future. - - // Load the bin if necessary. - if (!pSRC->linear.isPrevFramesLoaded) { - mal_uint32 framesRead = mal_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin); - if (framesRead == 0) { - return 0; - } - pSRC->linear.isPrevFramesLoaded = MAL_TRUE; - } - if (!pSRC->linear.isNextFramesLoaded) { - mal_uint32 framesRead = mal_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin + pSRC->config.channels); - if (framesRead == 0) { - return 0; - } - pSRC->linear.isNextFramesLoaded = MAL_TRUE; - } - - float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; - - mal_uint32 totalFramesRead = 0; - while (frameCount > 0) { - // The bin is where the previous and next frames are located. - float* pPrevFrame = pSRC->bin; - float* pNextFrame = pSRC->bin + pSRC->config.channels; - - float pFrame[MAL_MAX_CHANNELS]; - mal_blend_f32(pFrame, pPrevFrame, pNextFrame, pSRC->linear.alpha, pSRC->config.channels); - - pSRC->linear.alpha += factor; - - // The new alpha value is how we determine whether or not we need to read fresh frames. - mal_uint32 framesToReadFromClient = (mal_uint32)pSRC->linear.alpha; - pSRC->linear.alpha = pSRC->linear.alpha - framesToReadFromClient; - - for (mal_uint32 i = 0; i < framesToReadFromClient; ++i) { - for (mal_uint32 j = 0; j < pSRC->config.channels; ++j) { - pPrevFrame[j] = pNextFrame[j]; + mal_uint64 totalFramesRead = 0; + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = frameCount - totalFramesRead; + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > 0xFFFFFFFF) { + framesToReadRightNow = 0xFFFFFFFF; } - mal_uint32 framesRead = mal_src_cache_read_frames(&pSRC->cache, 1, pNextFrame); - if (framesRead == 0) { - for (mal_uint32 j = 0; j < pSRC->config.channels; ++j) { - pNextFrame[j] = 0; - } + mal_uint32 framesJustRead = (mal_uint32)pSRC->config.onReadDeinterleaved(pSRC, (mal_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pUserData); + if (framesJustRead == 0) { + break; + } - if (pSRC->linear.isNextFramesLoaded) { - pSRC->linear.isNextFramesLoaded = MAL_FALSE; - } else { - if (flush) { - pSRC->linear.isPrevFramesLoaded = MAL_FALSE; - } - } + totalFramesRead += framesJustRead; + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) { + ppNextSamplesOut[iChannel] += framesJustRead; + } + if (framesJustRead < framesToReadRightNow) { break; } } - mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pFrame, mal_format_f32, 1 * pSRC->config.channels); + return totalFramesRead; + } +} - pFramesOut = (mal_uint8*)pFramesOut + (1 * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut)); - frameCount -= 1; - totalFramesRead += 1; +mal_uint64 mal_src_read_deinterleaved__linear(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData) +{ + mal_assert(pSRC != NULL); + mal_assert(frameCount > 0); + mal_assert(ppSamplesOut != NULL); - // If there's no frames available we need to get out of this loop. - if (!pSRC->linear.isNextFramesLoaded && (!flush || !pSRC->linear.isPrevFramesLoaded)) { + float* ppNextSamplesOut[MAL_MAX_CHANNELS]; + mal_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pSRC->config.channels); + + + float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; + + mal_uint32 maxFrameCountPerChunkIn = mal_countof(pSRC->linear.input[0]); + + mal_uint64 totalFramesRead = 0; + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = frameCount - totalFramesRead; + mal_uint64 framesToRead = framesRemaining; + if (framesToRead > 16384) { + framesToRead = 16384; // <-- Keep this small because we're using 32-bit floats for calculating sample positions and I don't want to run out of precision with huge sample counts. + } + + + // Read Input Data + // =============== + float tBeg = pSRC->linear.timeIn; + float tEnd = tBeg + (framesToRead*factor); + + mal_uint32 framesToReadFromClient = (mal_uint32)(tEnd) + 1 + 1; // +1 to make tEnd 1-based and +1 because we always need to an extra sample for interpolation. + if (framesToReadFromClient >= maxFrameCountPerChunkIn) { + framesToReadFromClient = maxFrameCountPerChunkIn; + } + + float* ppSamplesFromClient[MAL_MAX_CHANNELS]; + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) { + ppSamplesFromClient[iChannel] = pSRC->linear.input[iChannel] + pSRC->linear.leftoverFrames; + } + + mal_uint32 framesReadFromClient = 0; + if (framesToReadFromClient > pSRC->linear.leftoverFrames) { + framesReadFromClient = (mal_uint32)pSRC->config.onReadDeinterleaved(pSRC, (mal_uint32)framesToReadFromClient - pSRC->linear.leftoverFrames, (void**)ppSamplesFromClient, pUserData); + } + + framesReadFromClient += pSRC->linear.leftoverFrames; // <-- You can sort of think of it as though we've re-read the leftover samples from the client. + if (framesReadFromClient < 2) { + break; + } + + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) { + ppSamplesFromClient[iChannel] = pSRC->linear.input[iChannel]; + } + + + // Write Output Data + // ================= + + // At this point we have a bunch of frames that the client has given to us for processing. From this we can determine the maximum number of output frames + // that can be processed from this input. We want to output as many samples as possible from our input data. + float tAvailable = framesReadFromClient - tBeg - 1; // Subtract 1 because the last input sample is needed for interpolation and cannot be included in the output sample count calculation. + + mal_uint32 maxOutputFramesToRead = (mal_uint32)(tAvailable / factor); + if (maxOutputFramesToRead == 0) { + maxOutputFramesToRead = 1; + } + if (maxOutputFramesToRead > framesToRead) { + maxOutputFramesToRead = (mal_uint32)framesToRead; + } + + // Output frames are always read in groups of 4 because I'm planning on using this as a reference for some SIMD-y stuff later. + mal_uint32 maxOutputFramesToRead4 = maxOutputFramesToRead/4; + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) { + float t0 = pSRC->linear.timeIn + factor*0; + float t1 = pSRC->linear.timeIn + factor*1; + float t2 = pSRC->linear.timeIn + factor*2; + float t3 = pSRC->linear.timeIn + factor*3; + + for (mal_uint32 iFrameOut = 0; iFrameOut < maxOutputFramesToRead4; iFrameOut += 1) { + float iPrevSample0 = (float)floor(t0); + float iPrevSample1 = (float)floor(t1); + float iPrevSample2 = (float)floor(t2); + float iPrevSample3 = (float)floor(t3); + + float iNextSample0 = iPrevSample0 + 1; + float iNextSample1 = iPrevSample1 + 1; + float iNextSample2 = iPrevSample2 + 1; + float iNextSample3 = iPrevSample3 + 1; + + float alpha0 = t0 - iPrevSample0; + float alpha1 = t1 - iPrevSample1; + float alpha2 = t2 - iPrevSample2; + float alpha3 = t3 - iPrevSample3; + + float prevSample0 = ppSamplesFromClient[iChannel][(mal_uint32)iPrevSample0]; + float prevSample1 = ppSamplesFromClient[iChannel][(mal_uint32)iPrevSample1]; + float prevSample2 = ppSamplesFromClient[iChannel][(mal_uint32)iPrevSample2]; + float prevSample3 = ppSamplesFromClient[iChannel][(mal_uint32)iPrevSample3]; + + float nextSample0 = ppSamplesFromClient[iChannel][(mal_uint32)iNextSample0]; + float nextSample1 = ppSamplesFromClient[iChannel][(mal_uint32)iNextSample1]; + float nextSample2 = ppSamplesFromClient[iChannel][(mal_uint32)iNextSample2]; + float nextSample3 = ppSamplesFromClient[iChannel][(mal_uint32)iNextSample3]; + + ppNextSamplesOut[iChannel][iFrameOut*4 + 0] = mal_mix_f32_fast(prevSample0, nextSample0, alpha0); + ppNextSamplesOut[iChannel][iFrameOut*4 + 1] = mal_mix_f32_fast(prevSample1, nextSample1, alpha1); + ppNextSamplesOut[iChannel][iFrameOut*4 + 2] = mal_mix_f32_fast(prevSample2, nextSample2, alpha2); + ppNextSamplesOut[iChannel][iFrameOut*4 + 3] = mal_mix_f32_fast(prevSample3, nextSample3, alpha3); + + t0 += factor*4; + t1 += factor*4; + t2 += factor*4; + t3 += factor*4; + } + + float t = pSRC->linear.timeIn + (factor*maxOutputFramesToRead4*4); + for (mal_uint32 iFrameOut = (maxOutputFramesToRead4*4); iFrameOut < maxOutputFramesToRead; iFrameOut += 1) { + float iPrevSample = (float)floor(t); + float iNextSample = iPrevSample + 1; + float alpha = t - iPrevSample; + + mal_assert(iPrevSample < mal_countof(pSRC->linear.input[iChannel])); + mal_assert(iNextSample < mal_countof(pSRC->linear.input[iChannel])); + + float prevSample = ppSamplesFromClient[iChannel][(mal_uint32)iPrevSample]; + float nextSample = ppSamplesFromClient[iChannel][(mal_uint32)iNextSample]; + + ppNextSamplesOut[iChannel][iFrameOut] = mal_mix_f32_fast(prevSample, nextSample, alpha); + + t += factor; + } + + ppNextSamplesOut[iChannel] += maxOutputFramesToRead; + } + + totalFramesRead += maxOutputFramesToRead; + + + // Residual + // ======== + float tNext = pSRC->linear.timeIn + (maxOutputFramesToRead*factor); + + pSRC->linear.timeIn = tNext; + mal_assert(tNext <= framesReadFromClient+1); + + mal_uint32 iNextFrame = (mal_uint32)floor(tNext); + pSRC->linear.leftoverFrames = framesReadFromClient - iNextFrame; + pSRC->linear.timeIn = tNext - iNextFrame; + + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) { + for (mal_uint32 iFrame = 0; iFrame < pSRC->linear.leftoverFrames; ++iFrame) { + float sample = ppSamplesFromClient[iChannel][framesReadFromClient-pSRC->linear.leftoverFrames + iFrame]; + ppSamplesFromClient[iChannel][iFrame] = sample; + } + } + + + // Exit the loop if we've found everything from the client. + if (framesReadFromClient < framesToReadFromClient) { break; } } @@ -10459,6 +25529,534 @@ mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void } +mal_src_config mal_src_config_init_new() +{ + mal_src_config config; + mal_zero_object(&config); + + return config; +} + +mal_src_config mal_src_config_init(mal_uint32 sampleRateIn, mal_uint32 sampleRateOut, mal_uint32 channels, mal_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData) +{ + mal_src_config config = mal_src_config_init_new(); + config.sampleRateIn = sampleRateIn; + config.sampleRateOut = sampleRateOut; + config.channels = channels; + config.onReadDeinterleaved = onReadDeinterleaved; + config.pUserData = pUserData; + + return config; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Sinc Sample Rate Conversion +// =========================== +// +// The sinc SRC algorithm uses a windowed sinc to perform interpolation of samples. Currently, mini_al's implementation supports rectangular and Hann window +// methods. +// +// Whenever an output sample is being computed, it looks at a sub-section of the input samples. I've called this sub-section in the code below the "window", +// which I realize is a bit ambigous with the mathematical "window", but it works for me when I need to conceptualize things in my head. The window is made up +// of two halves. The first half contains past input samples (initialized to zero), and the second half contains future input samples. As time moves forward +// and input samples are consumed, the window moves forward. The larger the window, the better the quality at the expense of slower processing. The window is +// limited the range [MAL_SRC_SINC_MIN_WINDOW_WIDTH, MAL_SRC_SINC_MAX_WINDOW_WIDTH] and defaults to MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH. +// +// Input samples are cached for efficiency (to prevent frequently requesting tiny numbers of samples from the client). When the window gets to the end of the +// cache, it's moved back to the start, and more samples are read from the client. If the client has no more data to give, the cache is filled with zeros and +// the last of the input samples will be consumed. Once the last of the input samples have been consumed, no more samples will be output. +// +// +// When reading output samples, we always first read whatever is already in the input cache. Only when the cache has been fully consumed do we read more data +// from the client. +// +// To access samples in the input buffer you do so relative to the window. When the window itself is at position 0, the first item in the buffer is accessed +// with "windowPos + windowWidth". Generally, to access any sample relative to the window you do "windowPos + windowWidth + sampleIndexRelativeToWindow". +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Comment this to disable interpolation of table lookups. Less accurate, but faster. +#define MAL_USE_SINC_TABLE_INTERPOLATION + +// Retrieves a sample from the input buffer's window. Values >= 0 retrieve future samples. Negative values return past samples. +static MAL_INLINE float mal_src_sinc__get_input_sample_from_window(const mal_src* pSRC, mal_uint32 channel, mal_uint32 windowPosInSamples, mal_int32 sampleIndex) +{ + mal_assert(pSRC != NULL); + mal_assert(channel < pSRC->config.channels); + mal_assert(sampleIndex >= -(mal_int32)pSRC->config.sinc.windowWidth); + mal_assert(sampleIndex < (mal_int32)pSRC->config.sinc.windowWidth); + + // The window should always be contained within the input cache. + mal_assert(windowPosInSamples < mal_countof(pSRC->sinc.input[0]) - pSRC->config.sinc.windowWidth); + + return pSRC->sinc.input[channel][windowPosInSamples + pSRC->config.sinc.windowWidth + sampleIndex]; +} + +static MAL_INLINE float mal_src_sinc__interpolation_factor(const mal_src* pSRC, float x) +{ + mal_assert(pSRC != NULL); + + float xabs = (float)fabs(x); + //if (xabs >= MAL_SRC_SINC_MAX_WINDOW_WIDTH /*pSRC->config.sinc.windowWidth*/) { + // xabs = 1; // <-- A non-zero integer will always return 0. + //} + + xabs = xabs * MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION; + mal_int32 ixabs = (mal_int32)xabs; + +#if defined(MAL_USE_SINC_TABLE_INTERPOLATION) + float a = xabs - ixabs; + return mal_mix_f32_fast(pSRC->sinc.table[ixabs], pSRC->sinc.table[ixabs+1], a); +#else + return pSRC->sinc.table[ixabs]; +#endif +} + +#if defined(MAL_SUPPORT_SSE2) +static MAL_INLINE __m128 mal_fabsf_sse2(__m128 x) +{ + return _mm_and_ps(_mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF)), x); +} + +static MAL_INLINE __m128 mal_truncf_sse2(__m128 x) +{ + return _mm_cvtepi32_ps(_mm_cvttps_epi32(x)); +} + +static MAL_INLINE __m128 mal_src_sinc__interpolation_factor__sse2(const mal_src* pSRC, __m128 x) +{ + //__m128 windowWidth128 = _mm_set1_ps(MAL_SRC_SINC_MAX_WINDOW_WIDTH); + __m128 resolution128 = _mm_set1_ps(MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION); + //__m128 one = _mm_set1_ps(1); + + __m128 xabs = mal_fabsf_sse2(x); + + // if (MAL_SRC_SINC_MAX_WINDOW_WIDTH <= xabs) xabs = 1 else xabs = xabs; + //__m128 xcmp = _mm_cmp_ps(windowWidth128, xabs, 2); // 2 = Less than or equal = _mm_cmple_ps. + //xabs = _mm_or_ps(_mm_and_ps(one, xcmp), _mm_andnot_ps(xcmp, xabs)); // xabs = (xcmp) ? 1 : xabs; + + xabs = _mm_mul_ps(xabs, resolution128); + __m128i ixabs = _mm_cvttps_epi32(xabs); + + int* ixabsv = (int*)&ixabs; + + __m128 lo = _mm_set_ps( + pSRC->sinc.table[ixabsv[3]], + pSRC->sinc.table[ixabsv[2]], + pSRC->sinc.table[ixabsv[1]], + pSRC->sinc.table[ixabsv[0]] + ); + + __m128 hi = _mm_set_ps( + pSRC->sinc.table[ixabsv[3]+1], + pSRC->sinc.table[ixabsv[2]+1], + pSRC->sinc.table[ixabsv[1]+1], + pSRC->sinc.table[ixabsv[0]+1] + ); + + __m128 a = _mm_sub_ps(xabs, _mm_cvtepi32_ps(ixabs)); + __m128 r = mal_mix_f32_fast__sse2(lo, hi, a); + + return r; +} +#endif + +#if defined(MAL_SUPPORT_AVX2) +static MAL_INLINE __m256 mal_fabsf_avx2(__m256 x) +{ + return _mm256_and_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0x7FFFFFFF)), x); +} + +#if 0 +static MAL_INLINE __m256 mal_src_sinc__interpolation_factor__avx2(const mal_src* pSRC, __m256 x) +{ + //__m256 windowWidth256 = _mm256_set1_ps(MAL_SRC_SINC_MAX_WINDOW_WIDTH); + __m256 resolution256 = _mm256_set1_ps(MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION); + //__m256 one = _mm256_set1_ps(1); + + __m256 xabs = mal_fabsf_avx2(x); + + // if (MAL_SRC_SINC_MAX_WINDOW_WIDTH <= xabs) xabs = 1 else xabs = xabs; + //__m256 xcmp = _mm256_cmp_ps(windowWidth256, xabs, 2); // 2 = Less than or equal = _mm_cmple_ps. + //xabs = _mm256_or_ps(_mm256_and_ps(one, xcmp), _mm256_andnot_ps(xcmp, xabs)); // xabs = (xcmp) ? 1 : xabs; + + xabs = _mm256_mul_ps(xabs, resolution256); + + __m256i ixabs = _mm256_cvttps_epi32(xabs); + __m256 a = _mm256_sub_ps(xabs, _mm256_cvtepi32_ps(ixabs)); + + + int* ixabsv = (int*)&ixabs; + + __m256 lo = _mm256_set_ps( + pSRC->sinc.table[ixabsv[7]], + pSRC->sinc.table[ixabsv[6]], + pSRC->sinc.table[ixabsv[5]], + pSRC->sinc.table[ixabsv[4]], + pSRC->sinc.table[ixabsv[3]], + pSRC->sinc.table[ixabsv[2]], + pSRC->sinc.table[ixabsv[1]], + pSRC->sinc.table[ixabsv[0]] + ); + + __m256 hi = _mm256_set_ps( + pSRC->sinc.table[ixabsv[7]+1], + pSRC->sinc.table[ixabsv[6]+1], + pSRC->sinc.table[ixabsv[5]+1], + pSRC->sinc.table[ixabsv[4]+1], + pSRC->sinc.table[ixabsv[3]+1], + pSRC->sinc.table[ixabsv[2]+1], + pSRC->sinc.table[ixabsv[1]+1], + pSRC->sinc.table[ixabsv[0]+1] + ); + + __m256 r = mal_mix_f32_fast__avx2(lo, hi, a); + + return r; +} +#endif + +#endif + +#if defined(MAL_SUPPORT_NEON) +static MAL_INLINE float32x4_t mal_fabsf_neon(float32x4_t x) +{ + return vabdq_f32(vmovq_n_f32(0), x); +} + +static MAL_INLINE float32x4_t mal_src_sinc__interpolation_factor__neon(const mal_src* pSRC, float32x4_t x) +{ + float32x4_t xabs = mal_fabsf_neon(x); + xabs = vmulq_n_f32(xabs, MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION); + + int32x4_t ixabs = vcvtq_s32_f32(xabs); + + int* ixabsv = (int*)&ixabs; + + float lo[4]; + lo[0] = pSRC->sinc.table[ixabsv[0]]; + lo[1] = pSRC->sinc.table[ixabsv[1]]; + lo[2] = pSRC->sinc.table[ixabsv[2]]; + lo[3] = pSRC->sinc.table[ixabsv[3]]; + + float hi[4]; + hi[0] = pSRC->sinc.table[ixabsv[0]+1]; + hi[1] = pSRC->sinc.table[ixabsv[1]+1]; + hi[2] = pSRC->sinc.table[ixabsv[2]+1]; + hi[3] = pSRC->sinc.table[ixabsv[3]+1]; + + float32x4_t a = vsubq_f32(xabs, vcvtq_f32_s32(ixabs)); + float32x4_t r = mal_mix_f32_fast__neon(vld1q_f32(lo), vld1q_f32(hi), a); + + return r; +} +#endif + +mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData) +{ + mal_assert(pSRC != NULL); + mal_assert(frameCount > 0); + mal_assert(ppSamplesOut != NULL); + + float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; + float inverseFactor = 1/factor; + + mal_int32 windowWidth = (mal_int32)pSRC->config.sinc.windowWidth; + mal_int32 windowWidth2 = windowWidth*2; + + // There are cases where it's actually more efficient to increase the window width so that it's aligned with the respective + // SIMD pipeline being used. + mal_int32 windowWidthSIMD = windowWidth; + if (pSRC->useNEON) { + windowWidthSIMD = (windowWidthSIMD + 1) & ~(1); + } else if (pSRC->useAVX512) { + windowWidthSIMD = (windowWidthSIMD + 7) & ~(7); + } else if (pSRC->useAVX2) { + windowWidthSIMD = (windowWidthSIMD + 3) & ~(3); + } else if (pSRC->useSSE2) { + windowWidthSIMD = (windowWidthSIMD + 1) & ~(1); + } + + mal_int32 windowWidthSIMD2 = windowWidthSIMD*2; + (void)windowWidthSIMD2; // <-- Silence a warning when SIMD is disabled. + + float* ppNextSamplesOut[MAL_MAX_CHANNELS]; + mal_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pSRC->config.channels); + + float _windowSamplesUnaligned[MAL_SRC_SINC_MAX_WINDOW_WIDTH*2 + MAL_SIMD_ALIGNMENT]; + float* windowSamples = (float*)(((mal_uintptr)_windowSamplesUnaligned + MAL_SIMD_ALIGNMENT-1) & ~(MAL_SIMD_ALIGNMENT-1)); + mal_zero_memory(windowSamples, MAL_SRC_SINC_MAX_WINDOW_WIDTH*2 * sizeof(float)); + + float _iWindowFUnaligned[MAL_SRC_SINC_MAX_WINDOW_WIDTH*2 + MAL_SIMD_ALIGNMENT]; + float* iWindowF = (float*)(((mal_uintptr)_iWindowFUnaligned + MAL_SIMD_ALIGNMENT-1) & ~(MAL_SIMD_ALIGNMENT-1)); + mal_zero_memory(iWindowF, MAL_SRC_SINC_MAX_WINDOW_WIDTH*2 * sizeof(float)); + for (mal_int32 i = 0; i < windowWidth2; ++i) { + iWindowF[i] = (float)(i - windowWidth); + } + + mal_uint64 totalOutputFramesRead = 0; + while (totalOutputFramesRead < frameCount) { + // The maximum number of frames we can read this iteration depends on how many input samples we have available to us. This is the number + // of input samples between the end of the window and the end of the cache. + mal_uint32 maxInputSamplesAvailableInCache = mal_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth*2) - pSRC->sinc.windowPosInSamples; + if (maxInputSamplesAvailableInCache > pSRC->sinc.inputFrameCount) { + maxInputSamplesAvailableInCache = pSRC->sinc.inputFrameCount; + } + + // Never consume the tail end of the input data if requested. + if (pSRC->config.neverConsumeEndOfInput) { + if (maxInputSamplesAvailableInCache >= pSRC->config.sinc.windowWidth) { + maxInputSamplesAvailableInCache -= pSRC->config.sinc.windowWidth; + } else { + maxInputSamplesAvailableInCache = 0; + } + } + + float timeInBeg = pSRC->sinc.timeIn; + float timeInEnd = (float)(pSRC->sinc.windowPosInSamples + maxInputSamplesAvailableInCache); + + mal_assert(timeInBeg >= 0); + mal_assert(timeInBeg <= timeInEnd); + + mal_uint64 maxOutputFramesToRead = (mal_uint64)(((timeInEnd - timeInBeg) * inverseFactor)); + + mal_uint64 outputFramesRemaining = frameCount - totalOutputFramesRead; + mal_uint64 outputFramesToRead = outputFramesRemaining; + if (outputFramesToRead > maxOutputFramesToRead) { + outputFramesToRead = maxOutputFramesToRead; + } + + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) { + // Do SRC. + float timeIn = timeInBeg; + for (mal_uint32 iSample = 0; iSample < outputFramesToRead; iSample += 1) { + float sampleOut = 0; + float iTimeInF = mal_floorf(timeIn); + mal_uint32 iTimeIn = (mal_uint32)iTimeInF; + + mal_int32 iWindow = 0; + + // Pre-load the window samples into an aligned buffer to begin with. Need to put these into an aligned buffer to make SIMD easier. + windowSamples[0] = 0; // <-- The first sample is always zero. + for (mal_int32 i = 1; i < windowWidth2; ++i) { + windowSamples[i] = pSRC->sinc.input[iChannel][iTimeIn + i]; + } + +#if defined(MAL_SUPPORT_AVX2) || defined(MAL_SUPPORT_AVX512) + if (pSRC->useAVX2 || pSRC->useAVX512) { + __m256i ixabs[MAL_SRC_SINC_MAX_WINDOW_WIDTH*2/8]; + __m256 a[MAL_SRC_SINC_MAX_WINDOW_WIDTH*2/8]; + __m256 resolution256 = _mm256_set1_ps(MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION); + + __m256 t = _mm256_set1_ps((timeIn - iTimeInF)); + __m256 r = _mm256_set1_ps(0); + + mal_int32 windowWidth8 = windowWidthSIMD2 >> 3; + for (mal_int32 iWindow8 = 0; iWindow8 < windowWidth8; iWindow8 += 1) { + __m256 w = *((__m256*)iWindowF + iWindow8); + + __m256 xabs = _mm256_sub_ps(t, w); + xabs = mal_fabsf_avx2(xabs); + xabs = _mm256_mul_ps(xabs, resolution256); + + ixabs[iWindow8] = _mm256_cvttps_epi32(xabs); + a[iWindow8] = _mm256_sub_ps(xabs, _mm256_cvtepi32_ps(ixabs[iWindow8])); + } + + for (mal_int32 iWindow8 = 0; iWindow8 < windowWidth8; iWindow8 += 1) { + int* ixabsv = (int*)&ixabs[iWindow8]; + + __m256 lo = _mm256_set_ps( + pSRC->sinc.table[ixabsv[7]], + pSRC->sinc.table[ixabsv[6]], + pSRC->sinc.table[ixabsv[5]], + pSRC->sinc.table[ixabsv[4]], + pSRC->sinc.table[ixabsv[3]], + pSRC->sinc.table[ixabsv[2]], + pSRC->sinc.table[ixabsv[1]], + pSRC->sinc.table[ixabsv[0]] + ); + + __m256 hi = _mm256_set_ps( + pSRC->sinc.table[ixabsv[7]+1], + pSRC->sinc.table[ixabsv[6]+1], + pSRC->sinc.table[ixabsv[5]+1], + pSRC->sinc.table[ixabsv[4]+1], + pSRC->sinc.table[ixabsv[3]+1], + pSRC->sinc.table[ixabsv[2]+1], + pSRC->sinc.table[ixabsv[1]+1], + pSRC->sinc.table[ixabsv[0]+1] + ); + + __m256 s = *((__m256*)windowSamples + iWindow8); + r = _mm256_add_ps(r, _mm256_mul_ps(s, mal_mix_f32_fast__avx2(lo, hi, a[iWindow8]))); + } + + // Horizontal add. + __m256 x = _mm256_hadd_ps(r, _mm256_permute2f128_ps(r, r, 1)); + x = _mm256_hadd_ps(x, x); + x = _mm256_hadd_ps(x, x); + sampleOut += _mm_cvtss_f32(_mm256_castps256_ps128(x)); + + iWindow += windowWidth8 * 8; + } + else +#endif +#if defined(MAL_SUPPORT_SSE2) + if (pSRC->useSSE2) { + __m128 t = _mm_set1_ps((timeIn - iTimeInF)); + __m128 r = _mm_set1_ps(0); + + mal_int32 windowWidth4 = windowWidthSIMD2 >> 2; + for (mal_int32 iWindow4 = 0; iWindow4 < windowWidth4; iWindow4 += 1) { + __m128* s = (__m128*)windowSamples + iWindow4; + __m128* w = (__m128*)iWindowF + iWindow4; + + __m128 a = mal_src_sinc__interpolation_factor__sse2(pSRC, _mm_sub_ps(t, *w)); + r = _mm_add_ps(r, _mm_mul_ps(*s, a)); + } + + sampleOut += ((float*)(&r))[0]; + sampleOut += ((float*)(&r))[1]; + sampleOut += ((float*)(&r))[2]; + sampleOut += ((float*)(&r))[3]; + + iWindow += windowWidth4 * 4; + } + else +#endif +#if defined(MAL_SUPPORT_NEON) + if (pSRC->useNEON) { + float32x4_t t = vmovq_n_f32((timeIn - iTimeInF)); + float32x4_t r = vmovq_n_f32(0); + + mal_int32 windowWidth4 = windowWidthSIMD2 >> 2; + for (mal_int32 iWindow4 = 0; iWindow4 < windowWidth4; iWindow4 += 1) { + float32x4_t* s = (float32x4_t*)windowSamples + iWindow4; + float32x4_t* w = (float32x4_t*)iWindowF + iWindow4; + + float32x4_t a = mal_src_sinc__interpolation_factor__neon(pSRC, vsubq_f32(t, *w)); + r = vaddq_f32(r, vmulq_f32(*s, a)); + } + + sampleOut += ((float*)(&r))[0]; + sampleOut += ((float*)(&r))[1]; + sampleOut += ((float*)(&r))[2]; + sampleOut += ((float*)(&r))[3]; + + iWindow += windowWidth4 * 4; + } + else +#endif + { + iWindow += 1; // The first one is a dummy for SIMD alignment purposes. Skip it. + } + + // Non-SIMD/Reference implementation. + float t = (timeIn - iTimeIn); + for (; iWindow < windowWidth2; iWindow += 1) { + float s = windowSamples[iWindow]; + float w = iWindowF[iWindow]; + + float a = mal_src_sinc__interpolation_factor(pSRC, (t - w)); + float r = s * a; + + sampleOut += r; + } + + ppNextSamplesOut[iChannel][iSample] = (float)sampleOut; + + timeIn += factor; + } + + ppNextSamplesOut[iChannel] += outputFramesToRead; + } + + totalOutputFramesRead += outputFramesToRead; + + mal_uint32 prevWindowPosInSamples = pSRC->sinc.windowPosInSamples; + + pSRC->sinc.timeIn += (outputFramesToRead * factor); + pSRC->sinc.windowPosInSamples = (mal_uint32)pSRC->sinc.timeIn; + pSRC->sinc.inputFrameCount -= pSRC->sinc.windowPosInSamples - prevWindowPosInSamples; + + // If the window has reached a point where we cannot read a whole output sample it needs to be moved back to the start. + mal_uint32 availableOutputFrames = (mal_uint32)((timeInEnd - pSRC->sinc.timeIn) * inverseFactor); + + if (availableOutputFrames == 0) { + size_t samplesToMove = mal_countof(pSRC->sinc.input[0]) - pSRC->sinc.windowPosInSamples; + + pSRC->sinc.timeIn -= mal_floorf(pSRC->sinc.timeIn); + pSRC->sinc.windowPosInSamples = 0; + + // Move everything from the end of the cache up to the front. + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) { + memmove(pSRC->sinc.input[iChannel], pSRC->sinc.input[iChannel] + mal_countof(pSRC->sinc.input[iChannel]) - samplesToMove, samplesToMove * sizeof(*pSRC->sinc.input[iChannel])); + } + } + + // Read more data from the client if required. + if (pSRC->isEndOfInputLoaded) { + pSRC->isEndOfInputLoaded = MAL_FALSE; + break; + } + + // Everything beyond this point is reloading. If we're at the end of the input data we do _not_ want to try reading any more in this function call. If the + // caller wants to keep trying, they can reload their internal data sources and call this function again. We should never be + mal_assert(pSRC->isEndOfInputLoaded == MAL_FALSE); + + if (pSRC->sinc.inputFrameCount <= pSRC->config.sinc.windowWidth || availableOutputFrames == 0) { + float* ppInputDst[MAL_MAX_CHANNELS] = {0}; + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) { + ppInputDst[iChannel] = pSRC->sinc.input[iChannel] + pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount; + } + + // Now read data from the client. + mal_uint32 framesToReadFromClient = mal_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount); + + mal_uint32 framesReadFromClient = 0; + if (framesToReadFromClient > 0) { + framesReadFromClient = pSRC->config.onReadDeinterleaved(pSRC, framesToReadFromClient, (void**)ppInputDst, pUserData); + } + + if (framesReadFromClient != framesToReadFromClient) { + pSRC->isEndOfInputLoaded = MAL_TRUE; + } else { + pSRC->isEndOfInputLoaded = MAL_FALSE; + } + + if (framesReadFromClient != 0) { + pSRC->sinc.inputFrameCount += framesReadFromClient; + } else { + // We couldn't get anything more from the client. If no more output samples can be computed from the available input samples + // we need to return. + if (pSRC->config.neverConsumeEndOfInput) { + if ((pSRC->sinc.inputFrameCount * inverseFactor) <= pSRC->config.sinc.windowWidth) { + break; + } + } else { + if ((pSRC->sinc.inputFrameCount * inverseFactor) < 1) { + break; + } + } + } + + // Anything left over in the cache must be set to zero. + mal_uint32 leftoverFrames = mal_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount); + if (leftoverFrames > 0) { + for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) { + mal_zero_memory(pSRC->sinc.input[iChannel] + pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount, leftoverFrames * sizeof(float)); + } + } + } + } + + return totalOutputFramesRead; +} + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -10467,31 +26065,10 @@ mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void mal_pcm_u8_to_s16(short* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_s24(void* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_s32(int* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_f32(float* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_s16_to_u8(unsigned char* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_s24(void* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_s32(int* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_f32(float* pOut, const short* pIn, unsigned int count); -void mal_pcm_s24_to_u8(unsigned char* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_s16(short* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_s32(int* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_f32(float* pOut, const void* pIn, unsigned int count); -void mal_pcm_s32_to_u8(unsigned char* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_s16(short* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_s24(void* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_f32(float* pOut, const int* pIn, unsigned int count); -void mal_pcm_f32_to_u8(unsigned char* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s16(short* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s24(void* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count); - -void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, unsigned int sampleCount) +void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode) { if (formatOut == formatIn) { - mal_copy_memory(pOut, pIn, sampleCount * mal_get_sample_size_in_bytes(formatOut)); + mal_copy_memory_64(pOut, pIn, sampleCount * mal_get_bytes_per_sample(formatOut)); return; } @@ -10501,10 +26078,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_s16: mal_pcm_u8_to_s16((short*)pOut, (const unsigned char*)pIn, sampleCount); return; - case mal_format_s24: mal_pcm_u8_to_s24( pOut, (const unsigned char*)pIn, sampleCount); return; - case mal_format_s32: mal_pcm_u8_to_s32( (int*)pOut, (const unsigned char*)pIn, sampleCount); return; - case mal_format_f32: mal_pcm_u8_to_f32((float*)pOut, (const unsigned char*)pIn, sampleCount); return; + case mal_format_s16: mal_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s24: mal_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s32: mal_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_f32: mal_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -10513,10 +26090,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_u8: mal_pcm_s16_to_u8( (unsigned char*)pOut, (const short*)pIn, sampleCount); return; - case mal_format_s24: mal_pcm_s16_to_s24( pOut, (const short*)pIn, sampleCount); return; - case mal_format_s32: mal_pcm_s16_to_s32( (int*)pOut, (const short*)pIn, sampleCount); return; - case mal_format_f32: mal_pcm_s16_to_f32( (float*)pOut, (const short*)pIn, sampleCount); return; + case mal_format_u8: mal_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s24: mal_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s32: mal_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_f32: mal_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -10525,10 +26102,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_u8: mal_pcm_s24_to_u8( (unsigned char*)pOut, pIn, sampleCount); return; - case mal_format_s16: mal_pcm_s24_to_s16( (short*)pOut, pIn, sampleCount); return; - case mal_format_s32: mal_pcm_s24_to_s32( (int*)pOut, pIn, sampleCount); return; - case mal_format_f32: mal_pcm_s24_to_f32( (float*)pOut, pIn, sampleCount); return; + case mal_format_u8: mal_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s16: mal_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s32: mal_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_f32: mal_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -10537,10 +26114,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_u8: mal_pcm_s32_to_u8( (unsigned char*)pOut, (const int*)pIn, sampleCount); return; - case mal_format_s16: mal_pcm_s32_to_s16( (short*)pOut, (const int*)pIn, sampleCount); return; - case mal_format_s24: mal_pcm_s32_to_s24( pOut, (const int*)pIn, sampleCount); return; - case mal_format_f32: mal_pcm_s32_to_f32( (float*)pOut, (const int*)pIn, sampleCount); return; + case mal_format_u8: mal_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s16: mal_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s24: mal_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_f32: mal_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -10549,10 +26126,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_u8: mal_pcm_f32_to_u8( (unsigned char*)pOut, (const float*)pIn, sampleCount); return; - case mal_format_s16: mal_pcm_f32_to_s16( (short*)pOut, (const float*)pIn, sampleCount); return; - case mal_format_s24: mal_pcm_f32_to_s24( pOut, (const float*)pIn, sampleCount); return; - case mal_format_s32: mal_pcm_f32_to_s32( (int*)pOut, (const float*)pIn, sampleCount); return; + case mal_format_u8: mal_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s16: mal_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s24: mal_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s32: mal_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -10562,585 +26139,418 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form } -static void mal_rearrange_channels_u8(mal_uint8* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) -{ - mal_uint8 temp[MAL_MAX_CHANNELS]; - mal_copy_memory(temp, pFrame, sizeof(temp[0]) * channels); - switch (channels) { - case 18: pFrame[17] = temp[channelMap[17]]; - case 17: pFrame[16] = temp[channelMap[16]]; - case 16: pFrame[15] = temp[channelMap[15]]; - case 15: pFrame[14] = temp[channelMap[14]]; - case 14: pFrame[13] = temp[channelMap[13]]; - case 13: pFrame[12] = temp[channelMap[12]]; - case 12: pFrame[11] = temp[channelMap[11]]; - case 11: pFrame[10] = temp[channelMap[10]]; - case 10: pFrame[ 9] = temp[channelMap[ 9]]; - case 9: pFrame[ 8] = temp[channelMap[ 8]]; - case 8: pFrame[ 7] = temp[channelMap[ 7]]; - case 7: pFrame[ 6] = temp[channelMap[ 6]]; - case 6: pFrame[ 5] = temp[channelMap[ 5]]; - case 5: pFrame[ 4] = temp[channelMap[ 4]]; - case 4: pFrame[ 3] = temp[channelMap[ 3]]; - case 3: pFrame[ 2] = temp[channelMap[ 2]]; - case 2: pFrame[ 1] = temp[channelMap[ 1]]; - case 1: pFrame[ 0] = temp[channelMap[ 0]]; - } +typedef struct +{ + mal_dsp* pDSP; + void* pUserDataForClient; +} mal_dsp_callback_data; + +mal_uint32 mal_dsp__pre_format_converter_on_read(mal_format_converter* pConverter, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + (void)pConverter; + + mal_dsp_callback_data* pData = (mal_dsp_callback_data*)pUserData; + mal_assert(pData != NULL); + + mal_dsp* pDSP = pData->pDSP; + mal_assert(pDSP != NULL); + + return pDSP->onRead(pDSP, frameCount, pFramesOut, pData->pUserDataForClient); } -static void mal_rearrange_channels_s16(mal_int16* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +mal_uint32 mal_dsp__post_format_converter_on_read(mal_format_converter* pConverter, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { - mal_int16 temp[MAL_MAX_CHANNELS]; - mal_copy_memory(temp, pFrame, sizeof(temp[0]) * channels); + (void)pConverter; - switch (channels) { - case 18: pFrame[17] = temp[channelMap[17]]; - case 17: pFrame[16] = temp[channelMap[16]]; - case 16: pFrame[15] = temp[channelMap[15]]; - case 15: pFrame[14] = temp[channelMap[14]]; - case 14: pFrame[13] = temp[channelMap[13]]; - case 13: pFrame[12] = temp[channelMap[12]]; - case 12: pFrame[11] = temp[channelMap[11]]; - case 11: pFrame[10] = temp[channelMap[10]]; - case 10: pFrame[ 9] = temp[channelMap[ 9]]; - case 9: pFrame[ 8] = temp[channelMap[ 8]]; - case 8: pFrame[ 7] = temp[channelMap[ 7]]; - case 7: pFrame[ 6] = temp[channelMap[ 6]]; - case 6: pFrame[ 5] = temp[channelMap[ 5]]; - case 5: pFrame[ 4] = temp[channelMap[ 4]]; - case 4: pFrame[ 3] = temp[channelMap[ 3]]; - case 3: pFrame[ 2] = temp[channelMap[ 2]]; - case 2: pFrame[ 1] = temp[channelMap[ 1]]; - case 1: pFrame[ 0] = temp[channelMap[ 0]]; - } + mal_dsp_callback_data* pData = (mal_dsp_callback_data*)pUserData; + mal_assert(pData != NULL); + + mal_dsp* pDSP = pData->pDSP; + mal_assert(pDSP != NULL); + + // When this version of this callback is used it means we're reading directly from the client. + mal_assert(pDSP->isPreFormatConversionRequired == MAL_FALSE); + mal_assert(pDSP->isChannelRoutingRequired == MAL_FALSE); + mal_assert(pDSP->isSRCRequired == MAL_FALSE); + + return pDSP->onRead(pDSP, frameCount, pFramesOut, pData->pUserDataForClient); } -static void mal_rearrange_channels_s32(mal_int32* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +mal_uint32 mal_dsp__post_format_converter_on_read_deinterleaved(mal_format_converter* pConverter, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData) { - mal_int32 temp[MAL_MAX_CHANNELS]; - mal_copy_memory(temp, pFrame, sizeof(temp[0]) * channels); + (void)pConverter; - switch (channels) { - case 18: pFrame[17] = temp[channelMap[17]]; - case 17: pFrame[16] = temp[channelMap[16]]; - case 16: pFrame[15] = temp[channelMap[15]]; - case 15: pFrame[14] = temp[channelMap[14]]; - case 14: pFrame[13] = temp[channelMap[13]]; - case 13: pFrame[12] = temp[channelMap[12]]; - case 12: pFrame[11] = temp[channelMap[11]]; - case 11: pFrame[10] = temp[channelMap[10]]; - case 10: pFrame[ 9] = temp[channelMap[ 9]]; - case 9: pFrame[ 8] = temp[channelMap[ 8]]; - case 8: pFrame[ 7] = temp[channelMap[ 7]]; - case 7: pFrame[ 6] = temp[channelMap[ 6]]; - case 6: pFrame[ 5] = temp[channelMap[ 5]]; - case 5: pFrame[ 4] = temp[channelMap[ 4]]; - case 4: pFrame[ 3] = temp[channelMap[ 3]]; - case 3: pFrame[ 2] = temp[channelMap[ 2]]; - case 2: pFrame[ 1] = temp[channelMap[ 1]]; - case 1: pFrame[ 0] = temp[channelMap[ 0]]; - } -} + mal_dsp_callback_data* pData = (mal_dsp_callback_data*)pUserData; + mal_assert(pData != NULL); -static void mal_rearrange_channels_f32(float* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) -{ - float temp[MAL_MAX_CHANNELS]; - mal_copy_memory(temp, pFrame, sizeof(temp[0]) * channels); + mal_dsp* pDSP = pData->pDSP; + mal_assert(pDSP != NULL); - switch (channels) { - case 18: pFrame[17] = temp[channelMap[17]]; - case 17: pFrame[16] = temp[channelMap[16]]; - case 16: pFrame[15] = temp[channelMap[15]]; - case 15: pFrame[14] = temp[channelMap[14]]; - case 14: pFrame[13] = temp[channelMap[13]]; - case 13: pFrame[12] = temp[channelMap[12]]; - case 12: pFrame[11] = temp[channelMap[11]]; - case 11: pFrame[10] = temp[channelMap[10]]; - case 10: pFrame[ 9] = temp[channelMap[ 9]]; - case 9: pFrame[ 8] = temp[channelMap[ 8]]; - case 8: pFrame[ 7] = temp[channelMap[ 7]]; - case 7: pFrame[ 6] = temp[channelMap[ 6]]; - case 6: pFrame[ 5] = temp[channelMap[ 5]]; - case 5: pFrame[ 4] = temp[channelMap[ 4]]; - case 4: pFrame[ 3] = temp[channelMap[ 3]]; - case 3: pFrame[ 2] = temp[channelMap[ 2]]; - case 2: pFrame[ 1] = temp[channelMap[ 1]]; - case 1: pFrame[ 0] = temp[channelMap[ 0]]; - } -} - -static void mal_rearrange_channels_generic(void* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS], mal_format format) -{ - mal_uint32 sampleSizeInBytes = mal_get_sample_size_in_bytes(format); - - mal_uint8 temp[MAL_MAX_CHANNELS * 8]; // x8 to ensure it's large enough for all formats. - mal_copy_memory(temp, pFrame, sampleSizeInBytes * channels); - - switch (channels) { - case 18: mal_copy_memory((mal_uint8*)pFrame + (17 * sampleSizeInBytes), &temp[channelMap[17] * sampleSizeInBytes], sampleSizeInBytes); - case 17: mal_copy_memory((mal_uint8*)pFrame + (16 * sampleSizeInBytes), &temp[channelMap[16] * sampleSizeInBytes], sampleSizeInBytes); - case 16: mal_copy_memory((mal_uint8*)pFrame + (15 * sampleSizeInBytes), &temp[channelMap[15] * sampleSizeInBytes], sampleSizeInBytes); - case 15: mal_copy_memory((mal_uint8*)pFrame + (14 * sampleSizeInBytes), &temp[channelMap[14] * sampleSizeInBytes], sampleSizeInBytes); - case 14: mal_copy_memory((mal_uint8*)pFrame + (13 * sampleSizeInBytes), &temp[channelMap[13] * sampleSizeInBytes], sampleSizeInBytes); - case 13: mal_copy_memory((mal_uint8*)pFrame + (12 * sampleSizeInBytes), &temp[channelMap[12] * sampleSizeInBytes], sampleSizeInBytes); - case 12: mal_copy_memory((mal_uint8*)pFrame + (11 * sampleSizeInBytes), &temp[channelMap[11] * sampleSizeInBytes], sampleSizeInBytes); - case 11: mal_copy_memory((mal_uint8*)pFrame + (10 * sampleSizeInBytes), &temp[channelMap[10] * sampleSizeInBytes], sampleSizeInBytes); - case 10: mal_copy_memory((mal_uint8*)pFrame + ( 9 * sampleSizeInBytes), &temp[channelMap[ 9] * sampleSizeInBytes], sampleSizeInBytes); - case 9: mal_copy_memory((mal_uint8*)pFrame + ( 8 * sampleSizeInBytes), &temp[channelMap[ 8] * sampleSizeInBytes], sampleSizeInBytes); - case 8: mal_copy_memory((mal_uint8*)pFrame + ( 7 * sampleSizeInBytes), &temp[channelMap[ 7] * sampleSizeInBytes], sampleSizeInBytes); - case 7: mal_copy_memory((mal_uint8*)pFrame + ( 6 * sampleSizeInBytes), &temp[channelMap[ 6] * sampleSizeInBytes], sampleSizeInBytes); - case 6: mal_copy_memory((mal_uint8*)pFrame + ( 5 * sampleSizeInBytes), &temp[channelMap[ 5] * sampleSizeInBytes], sampleSizeInBytes); - case 5: mal_copy_memory((mal_uint8*)pFrame + ( 4 * sampleSizeInBytes), &temp[channelMap[ 4] * sampleSizeInBytes], sampleSizeInBytes); - case 4: mal_copy_memory((mal_uint8*)pFrame + ( 3 * sampleSizeInBytes), &temp[channelMap[ 3] * sampleSizeInBytes], sampleSizeInBytes); - case 3: mal_copy_memory((mal_uint8*)pFrame + ( 2 * sampleSizeInBytes), &temp[channelMap[ 2] * sampleSizeInBytes], sampleSizeInBytes); - case 2: mal_copy_memory((mal_uint8*)pFrame + ( 1 * sampleSizeInBytes), &temp[channelMap[ 1] * sampleSizeInBytes], sampleSizeInBytes); - case 1: mal_copy_memory((mal_uint8*)pFrame + ( 0 * sampleSizeInBytes), &temp[channelMap[ 0] * sampleSizeInBytes], sampleSizeInBytes); - } -} - -static void mal_rearrange_channels(void* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS], mal_format format) -{ - switch (format) - { - case mal_format_u8: mal_rearrange_channels_u8( (mal_uint8*)pFrame, channels, channelMap); break; - case mal_format_s16: mal_rearrange_channels_s16((mal_int16*)pFrame, channels, channelMap); break; - case mal_format_s32: mal_rearrange_channels_s32((mal_int32*)pFrame, channels, channelMap); break; - case mal_format_f32: mal_rearrange_channels_f32( (float*)pFrame, channels, channelMap); break; - default: mal_rearrange_channels_generic(pFrame, channels, channelMap, format); break; - } -} - -static void mal_dsp_mix_channels__dec(float* pFramesOut, mal_uint32 channelsOut, const mal_uint8 channelMapOut[MAL_MAX_CHANNELS], const float* pFramesIn, mal_uint32 channelsIn, const mal_uint8 channelMapIn[MAL_MAX_CHANNELS], mal_uint32 frameCount, mal_channel_mix_mode mode) -{ - mal_assert(pFramesOut != NULL); - mal_assert(channelsOut > 0); - mal_assert(pFramesIn != NULL); - mal_assert(channelsIn > 0); - mal_assert(channelsOut < channelsIn); - - (void)channelMapOut; - (void)channelMapIn; - - if (mode == mal_channel_mix_mode_basic) { - // Basic mode is where we just drop excess channels. - for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { - switch (channelsOut) { - case 17: pFramesOut[iFrame*channelsOut+16] = pFramesIn[iFrame*channelsIn+16]; - case 16: pFramesOut[iFrame*channelsOut+15] = pFramesIn[iFrame*channelsIn+15]; - case 15: pFramesOut[iFrame*channelsOut+14] = pFramesIn[iFrame*channelsIn+14]; - case 14: pFramesOut[iFrame*channelsOut+13] = pFramesIn[iFrame*channelsIn+13]; - case 13: pFramesOut[iFrame*channelsOut+12] = pFramesIn[iFrame*channelsIn+12]; - case 12: pFramesOut[iFrame*channelsOut+11] = pFramesIn[iFrame*channelsIn+11]; - case 11: pFramesOut[iFrame*channelsOut+10] = pFramesIn[iFrame*channelsIn+10]; - case 10: pFramesOut[iFrame*channelsOut+ 9] = pFramesIn[iFrame*channelsIn+ 9]; - case 9: pFramesOut[iFrame*channelsOut+ 8] = pFramesIn[iFrame*channelsIn+ 8]; - case 8: pFramesOut[iFrame*channelsOut+ 7] = pFramesIn[iFrame*channelsIn+ 7]; - case 7: pFramesOut[iFrame*channelsOut+ 6] = pFramesIn[iFrame*channelsIn+ 6]; - case 6: pFramesOut[iFrame*channelsOut+ 5] = pFramesIn[iFrame*channelsIn+ 5]; - case 5: pFramesOut[iFrame*channelsOut+ 4] = pFramesIn[iFrame*channelsIn+ 4]; - case 4: pFramesOut[iFrame*channelsOut+ 3] = pFramesIn[iFrame*channelsIn+ 3]; - case 3: pFramesOut[iFrame*channelsOut+ 2] = pFramesIn[iFrame*channelsIn+ 2]; - case 2: pFramesOut[iFrame*channelsOut+ 1] = pFramesIn[iFrame*channelsIn+ 1]; - case 1: pFramesOut[iFrame*channelsOut+ 0] = pFramesIn[iFrame*channelsIn+ 0]; - } - } + if (!pDSP->isChannelRoutingAtStart) { + return (mal_uint32)mal_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData); } else { - // Blend mode is where we just use simple averaging to blend based on spacial locality. - if (channelsOut == 1) { - for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { - float total = 0; - switch (channelsIn) { - case 18: total += pFramesIn[iFrame*channelsIn+17]; - case 17: total += pFramesIn[iFrame*channelsIn+16]; - case 16: total += pFramesIn[iFrame*channelsIn+15]; - case 15: total += pFramesIn[iFrame*channelsIn+14]; - case 14: total += pFramesIn[iFrame*channelsIn+13]; - case 13: total += pFramesIn[iFrame*channelsIn+12]; - case 12: total += pFramesIn[iFrame*channelsIn+11]; - case 11: total += pFramesIn[iFrame*channelsIn+10]; - case 10: total += pFramesIn[iFrame*channelsIn+ 9]; - case 9: total += pFramesIn[iFrame*channelsIn+ 8]; - case 8: total += pFramesIn[iFrame*channelsIn+ 7]; - case 7: total += pFramesIn[iFrame*channelsIn+ 6]; - case 6: total += pFramesIn[iFrame*channelsIn+ 5]; - case 5: total += pFramesIn[iFrame*channelsIn+ 4]; - case 4: total += pFramesIn[iFrame*channelsIn+ 3]; - case 3: total += pFramesIn[iFrame*channelsIn+ 2]; - case 2: total += pFramesIn[iFrame*channelsIn+ 1]; - case 1: total += pFramesIn[iFrame*channelsIn+ 0]; - } - - pFramesOut[iFrame+0] = total / channelsIn; - } - } else if (channelsOut == 2) { - // TODO: Implement proper stereo blending. - mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); + if (pDSP->isSRCRequired) { + return (mal_uint32)mal_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData); } else { - // Fall back to basic mode. - mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); + return (mal_uint32)mal_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData); } } } -static void mal_dsp_mix_channels__inc(float* pFramesOut, mal_uint32 channelsOut, const mal_uint8 channelMapOut[MAL_MAX_CHANNELS], const float* pFramesIn, mal_uint32 channelsIn, const mal_uint8 channelMapIn[MAL_MAX_CHANNELS], mal_uint32 frameCount, mal_channel_mix_mode mode) -{ - mal_assert(pFramesOut != NULL); - mal_assert(channelsOut > 0); - mal_assert(pFramesIn != NULL); - mal_assert(channelsIn > 0); - mal_assert(channelsOut > channelsIn); - - (void)channelMapOut; - (void)channelMapIn; - - if (mode == mal_channel_mix_mode_basic) { - // Basic mode is where we just zero out extra channels. - for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { - switch (channelsIn) { - case 17: pFramesOut[iFrame*channelsOut+16] = pFramesIn[iFrame*channelsIn+16]; - case 16: pFramesOut[iFrame*channelsOut+15] = pFramesIn[iFrame*channelsIn+15]; - case 15: pFramesOut[iFrame*channelsOut+14] = pFramesIn[iFrame*channelsIn+14]; - case 14: pFramesOut[iFrame*channelsOut+13] = pFramesIn[iFrame*channelsIn+13]; - case 13: pFramesOut[iFrame*channelsOut+12] = pFramesIn[iFrame*channelsIn+12]; - case 12: pFramesOut[iFrame*channelsOut+11] = pFramesIn[iFrame*channelsIn+11]; - case 11: pFramesOut[iFrame*channelsOut+10] = pFramesIn[iFrame*channelsIn+10]; - case 10: pFramesOut[iFrame*channelsOut+ 9] = pFramesIn[iFrame*channelsIn+ 9]; - case 9: pFramesOut[iFrame*channelsOut+ 8] = pFramesIn[iFrame*channelsIn+ 8]; - case 8: pFramesOut[iFrame*channelsOut+ 7] = pFramesIn[iFrame*channelsIn+ 7]; - case 7: pFramesOut[iFrame*channelsOut+ 6] = pFramesIn[iFrame*channelsIn+ 6]; - case 6: pFramesOut[iFrame*channelsOut+ 5] = pFramesIn[iFrame*channelsIn+ 5]; - case 5: pFramesOut[iFrame*channelsOut+ 4] = pFramesIn[iFrame*channelsIn+ 4]; - case 4: pFramesOut[iFrame*channelsOut+ 3] = pFramesIn[iFrame*channelsIn+ 3]; - case 3: pFramesOut[iFrame*channelsOut+ 2] = pFramesIn[iFrame*channelsIn+ 2]; - case 2: pFramesOut[iFrame*channelsOut+ 1] = pFramesIn[iFrame*channelsIn+ 1]; - case 1: pFramesOut[iFrame*channelsOut+ 0] = pFramesIn[iFrame*channelsIn+ 0]; - } - - // Zero out extra channels. - switch (channelsOut - channelsIn) { - case 17: pFramesOut[iFrame*channelsOut+16 + channelsIn] = 0; - case 16: pFramesOut[iFrame*channelsOut+15 + channelsIn] = 0; - case 15: pFramesOut[iFrame*channelsOut+14 + channelsIn] = 0; - case 14: pFramesOut[iFrame*channelsOut+13 + channelsIn] = 0; - case 13: pFramesOut[iFrame*channelsOut+12 + channelsIn] = 0; - case 12: pFramesOut[iFrame*channelsOut+11 + channelsIn] = 0; - case 11: pFramesOut[iFrame*channelsOut+10 + channelsIn] = 0; - case 10: pFramesOut[iFrame*channelsOut+ 9 + channelsIn] = 0; - case 9: pFramesOut[iFrame*channelsOut+ 8 + channelsIn] = 0; - case 8: pFramesOut[iFrame*channelsOut+ 7 + channelsIn] = 0; - case 7: pFramesOut[iFrame*channelsOut+ 6 + channelsIn] = 0; - case 6: pFramesOut[iFrame*channelsOut+ 5 + channelsIn] = 0; - case 5: pFramesOut[iFrame*channelsOut+ 4 + channelsIn] = 0; - case 4: pFramesOut[iFrame*channelsOut+ 3 + channelsIn] = 0; - case 3: pFramesOut[iFrame*channelsOut+ 2 + channelsIn] = 0; - case 2: pFramesOut[iFrame*channelsOut+ 1 + channelsIn] = 0; - case 1: pFramesOut[iFrame*channelsOut+ 0 + channelsIn] = 0; - } - } - } else { - // Using blended mixing mode. Basically this is just the mode where audio is distributed across all channels - // based on spacial locality. - if (channelsIn == 1) { - for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { - switch (channelsOut) { - case 18: pFramesOut[iFrame*channelsOut+17] = pFramesIn[iFrame*channelsIn+0]; - case 17: pFramesOut[iFrame*channelsOut+16] = pFramesIn[iFrame*channelsIn+0]; - case 16: pFramesOut[iFrame*channelsOut+15] = pFramesIn[iFrame*channelsIn+0]; - case 15: pFramesOut[iFrame*channelsOut+14] = pFramesIn[iFrame*channelsIn+0]; - case 14: pFramesOut[iFrame*channelsOut+13] = pFramesIn[iFrame*channelsIn+0]; - case 13: pFramesOut[iFrame*channelsOut+12] = pFramesIn[iFrame*channelsIn+0]; - case 12: pFramesOut[iFrame*channelsOut+11] = pFramesIn[iFrame*channelsIn+0]; - case 11: pFramesOut[iFrame*channelsOut+10] = pFramesIn[iFrame*channelsIn+0]; - case 10: pFramesOut[iFrame*channelsOut+ 9] = pFramesIn[iFrame*channelsIn+0]; - case 9: pFramesOut[iFrame*channelsOut+ 8] = pFramesIn[iFrame*channelsIn+0]; - case 8: pFramesOut[iFrame*channelsOut+ 7] = pFramesIn[iFrame*channelsIn+0]; - case 7: pFramesOut[iFrame*channelsOut+ 6] = pFramesIn[iFrame*channelsIn+0]; - case 6: pFramesOut[iFrame*channelsOut+ 5] = pFramesIn[iFrame*channelsIn+0]; - case 5: pFramesOut[iFrame*channelsOut+ 4] = pFramesIn[iFrame*channelsIn+0]; - case 4: pFramesOut[iFrame*channelsOut+ 3] = pFramesIn[iFrame*channelsIn+0]; - case 3: pFramesOut[iFrame*channelsOut+ 2] = pFramesIn[iFrame*channelsIn+0]; - case 2: pFramesOut[iFrame*channelsOut+ 1] = pFramesIn[iFrame*channelsIn+0]; - case 1: pFramesOut[iFrame*channelsOut+ 0] = pFramesIn[iFrame*channelsIn+0]; - } - } - } else if (channelsIn == 2) { - // TODO: Implement an optimized stereo conversion. - mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); - } else { - // Fall back to basic mixing mode. - mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); - } - } -} - -static void mal_dsp_mix_channels(float* pFramesOut, mal_uint32 channelsOut, const mal_uint8 channelMapOut[MAL_MAX_CHANNELS], const float* pFramesIn, mal_uint32 channelsIn, const mal_uint8 channelMapIn[MAL_MAX_CHANNELS], mal_uint32 frameCount, mal_channel_mix_mode mode) -{ - if (channelsIn < channelsOut) { - // Increasing the channel count. - mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mode); - } else { - // Decreasing the channel count. - mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mode); - } -} - - -mal_uint32 mal_dsp__src_on_read(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +mal_uint32 mal_dsp__src_on_read_deinterleaved(mal_src* pSRC, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData) { (void)pSRC; - mal_dsp* pDSP = (mal_dsp*)pUserData; + mal_dsp_callback_data* pData = (mal_dsp_callback_data*)pUserData; + mal_assert(pData != NULL); + + mal_dsp* pDSP = pData->pDSP; mal_assert(pDSP != NULL); - return pDSP->onRead(pDSP, frameCount, pFramesOut, pDSP->pUserDataForOnRead); + // If the channel routing stage is at the front we need to read from that. Otherwise we read from the pre format converter. + if (pDSP->isChannelRoutingAtStart) { + return (mal_uint32)mal_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData); + } else { + return (mal_uint32)mal_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData); + } } -mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp_read_proc onRead, void* pUserData, mal_dsp* pDSP) +mal_uint32 mal_dsp__channel_router_on_read_deinterleaved(mal_channel_router* pRouter, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData) { - if (pDSP == NULL) return MAL_INVALID_ARGS; - mal_zero_object(pDSP); - pDSP->config = *pConfig; - pDSP->onRead = onRead; - pDSP->pUserDataForOnRead = pUserData; + (void)pRouter; - if (pDSP->config.cacheSizeInFrames > MAL_SRC_CACHE_SIZE_IN_FRAMES || pDSP->config.cacheSizeInFrames == 0) { - pDSP->config.cacheSizeInFrames = MAL_SRC_CACHE_SIZE_IN_FRAMES; + mal_dsp_callback_data* pData = (mal_dsp_callback_data*)pUserData; + mal_assert(pData != NULL); + + mal_dsp* pDSP = pData->pDSP; + mal_assert(pDSP != NULL); + + // If the channel routing stage is at the front of the pipeline we read from the pre format converter. Otherwise we read from the sample rate converter. + if (pDSP->isChannelRoutingAtStart) { + return (mal_uint32)mal_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData); + } else { + if (pDSP->isSRCRequired) { + return (mal_uint32)mal_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData); + } else { + return (mal_uint32)mal_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData); + } + } +} + +mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP) +{ + if (pDSP == NULL) { + return MAL_INVALID_ARGS; } - if (pConfig->sampleRateIn != pConfig->sampleRateOut) { - pDSP->isSRCRequired = MAL_TRUE; + mal_zero_object(pDSP); + pDSP->onRead = pConfig->onRead; + pDSP->pUserData = pConfig->pUserData; + pDSP->isDynamicSampleRateAllowed = pConfig->allowDynamicSampleRate; - mal_src_config srcConfig; - srcConfig.sampleRateIn = pConfig->sampleRateIn; - srcConfig.sampleRateOut = pConfig->sampleRateOut; - srcConfig.formatIn = pConfig->formatIn; - srcConfig.formatOut = mal_format_f32; - srcConfig.channels = pConfig->channelsIn; - srcConfig.algorithm = mal_src_algorithm_linear; - srcConfig.cacheSizeInFrames = pConfig->cacheSizeInFrames; - mal_result result = mal_src_init(&srcConfig, mal_dsp__src_on_read, pDSP, &pDSP->src); + + // In general, this is the pipeline used for data conversion. Note that this can actually change which is explained later. + // + // Pre Format Conversion -> Sample Rate Conversion -> Channel Routing -> Post Format Conversion + // + // Pre Format Conversion + // --------------------- + // This is where the sample data is converted to a format that's usable by the later stages in the pipeline. Input data + // is converted to deinterleaved floating-point. + // + // Channel Routing + // --------------- + // Channel routing is where stereo is converted to 5.1, mono is converted to stereo, etc. This stage depends on the + // pre format conversion stage. + // + // Sample Rate Conversion + // ---------------------- + // Sample rate conversion depends on the pre format conversion stage and as the name implies performs sample rate conversion. + // + // Post Format Conversion + // ---------------------- + // This stage is where our deinterleaved floating-point data from the previous stages are converted to the requested output + // format. + // + // + // Optimizations + // ------------- + // Sometimes the conversion pipeline is rearranged for efficiency. The first obvious optimization is to eliminate unnecessary + // stages in the pipeline. When no channel routing nor sample rate conversion is necessary, the entire pipeline is optimized + // down to just this: + // + // Post Format Conversion + // + // When sample rate conversion is not unnecessary: + // + // Pre Format Conversion -> Channel Routing -> Post Format Conversion + // + // When channel routing is unnecessary: + // + // Pre Format Conversion -> Sample Rate Conversion -> Post Format Conversion + // + // A slightly less obvious optimization is used depending on whether or not we are increasing or decreasing the number of + // channels. Because everything in the pipeline works on a per-channel basis, the efficiency of the pipeline is directly + // proportionate to the number of channels that need to be processed. Therefore, it's can be more efficient to move the + // channel conversion stage to an earlier or later stage. When the channel count is being reduced, we move the channel + // conversion stage to the start of the pipeline so that later stages can work on a smaller number of channels at a time. + // Otherwise, we move the channel conversion stage to the end of the pipeline. When reducing the channel count, the pipeline + // will look like this: + // + // Pre Format Conversion -> Channel Routing -> Sample Rate Conversion -> Post Format Conversion + // + // Notice how the Channel Routing and Sample Rate Conversion stages are swapped so that the SRC stage has less data to process. + + // First we need to determine what's required and what's not. + if (pConfig->sampleRateIn != pConfig->sampleRateOut || pConfig->allowDynamicSampleRate) { + pDSP->isSRCRequired = MAL_TRUE; + } + if (pConfig->channelsIn != pConfig->channelsOut || !mal_channel_map_equal(pConfig->channelsIn, pConfig->channelMapIn, pConfig->channelMapOut)) { + pDSP->isChannelRoutingRequired = MAL_TRUE; + } + + // If neither a sample rate conversion nor channel conversion is necessary we can skip the pre format conversion. + if (!pDSP->isSRCRequired && !pDSP->isChannelRoutingRequired) { + // We don't need a pre format conversion stage, but we may still need a post format conversion stage. + if (pConfig->formatIn != pConfig->formatOut) { + pDSP->isPostFormatConversionRequired = MAL_TRUE; + } + } else { + pDSP->isPreFormatConversionRequired = MAL_TRUE; + pDSP->isPostFormatConversionRequired = MAL_TRUE; + } + + // Use a passthrough if none of the stages are being used. + if (!pDSP->isPreFormatConversionRequired && !pDSP->isPostFormatConversionRequired && !pDSP->isChannelRoutingRequired && !pDSP->isSRCRequired) { + pDSP->isPassthrough = MAL_TRUE; + } + + // Move the channel conversion stage to the start of the pipeline if we are reducing the channel count. + if (pConfig->channelsOut < pConfig->channelsIn) { + pDSP->isChannelRoutingAtStart = MAL_TRUE; + } + + + // We always initialize every stage of the pipeline regardless of whether or not the stage is used because it simplifies + // a few things when it comes to dynamically changing properties post-initialization. + mal_result result = MAL_SUCCESS; + + // Pre format conversion. + { + mal_format_converter_config preFormatConverterConfig = mal_format_converter_config_init( + pConfig->formatIn, + mal_format_f32, + pConfig->channelsIn, + mal_dsp__pre_format_converter_on_read, + pDSP + ); + preFormatConverterConfig.ditherMode = pConfig->ditherMode; + preFormatConverterConfig.noSSE2 = pConfig->noSSE2; + preFormatConverterConfig.noAVX2 = pConfig->noAVX2; + preFormatConverterConfig.noAVX512 = pConfig->noAVX512; + preFormatConverterConfig.noNEON = pConfig->noNEON; + + result = mal_format_converter_init(&preFormatConverterConfig, &pDSP->formatConverterIn); if (result != MAL_SUCCESS) { return result; } } - - - pDSP->isChannelMappingRequired = MAL_FALSE; - if (pConfig->channelMapIn[0] != MAL_CHANNEL_NONE && pConfig->channelMapOut[0] != MAL_CHANNEL_NONE) { // <-- Channel mapping will be ignored if the first channel map is MAL_CHANNEL_NONE. - // When using channel mapping we need to figure out a shuffling table. The first thing to do is convert the input channel map - // so that it contains the same number of channels as the output channel count. - mal_uint32 iChannel; - mal_uint32 channelsMin = mal_min(pConfig->channelsIn, pConfig->channelsOut); - for (iChannel = 0; iChannel < channelsMin; ++iChannel) { - pDSP->channelMapInPostMix[iChannel] = pConfig->channelMapIn[iChannel]; + // Post format conversion. The exact configuration for this depends on whether or not we are reading data directly from the client + // or from an earlier stage in the pipeline. + { + mal_format_converter_config postFormatConverterConfig = mal_format_converter_config_init_new(); + postFormatConverterConfig.formatIn = pConfig->formatIn; + postFormatConverterConfig.formatOut = pConfig->formatOut; + postFormatConverterConfig.channels = pConfig->channelsOut; + postFormatConverterConfig.ditherMode = pConfig->ditherMode; + postFormatConverterConfig.noSSE2 = pConfig->noSSE2; + postFormatConverterConfig.noAVX2 = pConfig->noAVX2; + postFormatConverterConfig.noAVX512 = pConfig->noAVX512; + postFormatConverterConfig.noNEON = pConfig->noNEON; + if (pDSP->isPreFormatConversionRequired) { + postFormatConverterConfig.onReadDeinterleaved = mal_dsp__post_format_converter_on_read_deinterleaved; + postFormatConverterConfig.formatIn = mal_format_f32; + } else { + postFormatConverterConfig.onRead = mal_dsp__post_format_converter_on_read; } - // Any excess channels need to be filled with the relevant channels from the output channel map. Currently we're justing filling it with - // the first channels that are not present in the input channel map. - if (pConfig->channelsOut > pConfig->channelsIn) { - for (iChannel = pConfig->channelsIn; iChannel < pConfig->channelsOut; ++iChannel) { - mal_uint8 newChannel = MAL_CHANNEL_NONE; - for (mal_uint32 iChannelOut = 0; iChannelOut < pConfig->channelsOut; ++iChannelOut) { - mal_bool32 exists = MAL_FALSE; - for (mal_uint32 iChannelIn = 0; iChannelIn < pConfig->channelsIn; ++iChannelIn) { - if (pConfig->channelMapOut[iChannelOut] == pConfig->channelMapIn[iChannelIn]) { - exists = MAL_TRUE; - break; - } - } - - if (!exists) { - newChannel = pConfig->channelMapOut[iChannelOut]; - break; - } - } - - pDSP->channelMapInPostMix[iChannel] = newChannel; - } - } - - // We only need to do a channel mapping if the map after mixing is different to the final output map. - for (iChannel = 0; iChannel < pConfig->channelsOut; ++iChannel) { - if (pDSP->channelMapInPostMix[iChannel] != pConfig->channelMapOut[iChannel]) { - pDSP->isChannelMappingRequired = MAL_TRUE; - break; - } - } - - // Now we need to create the shuffling table. - if (pDSP->isChannelMappingRequired) { - for (mal_uint32 iChannelIn = 0; iChannelIn < pConfig->channelsOut; ++iChannelIn) { - for (mal_uint32 iChannelOut = 0; iChannelOut < pConfig->channelsOut; ++iChannelOut) { - if (pDSP->channelMapInPostMix[iChannelOut] == pConfig->channelMapOut[iChannelIn]) { - pDSP->channelShuffleTable[iChannelOut] = (mal_uint8)iChannelIn; - } - } - } + result = mal_format_converter_init(&postFormatConverterConfig, &pDSP->formatConverterOut); + if (result != MAL_SUCCESS) { + return result; } } - if (pConfig->formatIn == pConfig->formatOut && pConfig->channelsIn == pConfig->channelsOut && pConfig->sampleRateIn == pConfig->sampleRateOut && !pDSP->isChannelMappingRequired) { - pDSP->isPassthrough = MAL_TRUE; - } else { - pDSP->isPassthrough = MAL_FALSE; + // SRC + { + mal_src_config srcConfig = mal_src_config_init( + pConfig->sampleRateIn, + pConfig->sampleRateOut, + ((pConfig->channelsIn < pConfig->channelsOut) ? pConfig->channelsIn : pConfig->channelsOut), + mal_dsp__src_on_read_deinterleaved, + pDSP + ); + srcConfig.algorithm = pConfig->srcAlgorithm; + srcConfig.neverConsumeEndOfInput = pConfig->neverConsumeEndOfInput; + srcConfig.noSSE2 = pConfig->noSSE2; + srcConfig.noAVX2 = pConfig->noAVX2; + srcConfig.noAVX512 = pConfig->noAVX512; + srcConfig.noNEON = pConfig->noNEON; + mal_copy_memory(&srcConfig.sinc, &pConfig->sinc, sizeof(pConfig->sinc)); + + result = mal_src_init(&srcConfig, &pDSP->src); + if (result != MAL_SUCCESS) { + return result; + } + } + + // Channel conversion + { + mal_channel_router_config routerConfig = mal_channel_router_config_init( + pConfig->channelsIn, + pConfig->channelMapIn, + pConfig->channelsOut, + pConfig->channelMapOut, + pConfig->channelMixMode, + mal_dsp__channel_router_on_read_deinterleaved, + pDSP); + routerConfig.noSSE2 = pConfig->noSSE2; + routerConfig.noAVX2 = pConfig->noAVX2; + routerConfig.noAVX512 = pConfig->noAVX512; + routerConfig.noNEON = pConfig->noNEON; + + result = mal_channel_router_init(&routerConfig, &pDSP->channelRouter); + if (result != MAL_SUCCESS) { + return result; + } } return MAL_SUCCESS; } + +mal_result mal_dsp_refresh_sample_rate(mal_dsp* pDSP) +{ + // The SRC stage will already have been initialized so we can just set it there. + mal_src_set_input_sample_rate(&pDSP->src, pDSP->src.config.sampleRateIn); + mal_src_set_output_sample_rate(&pDSP->src, pDSP->src.config.sampleRateOut); + + return MAL_SUCCESS; +} + +mal_result mal_dsp_set_input_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateIn) +{ + if (pDSP == NULL) { + return MAL_INVALID_ARGS; + } + + // Must have a sample rate of > 0. + if (sampleRateIn == 0) { + return MAL_INVALID_ARGS; + } + + // Must have been initialized with allowDynamicSampleRate. + if (!pDSP->isDynamicSampleRateAllowed) { + return MAL_INVALID_OPERATION; + } + + mal_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn); + return mal_dsp_refresh_sample_rate(pDSP); +} + mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut) { - if (pDSP == NULL) return MAL_INVALID_ARGS; + if (pDSP == NULL) { + return MAL_INVALID_ARGS; + } // Must have a sample rate of > 0. if (sampleRateOut == 0) { return MAL_INVALID_ARGS; } - pDSP->config.sampleRateOut = sampleRateOut; - - // If we already have an SRC pipeline initialized we do _not_ want to re-create it. Instead we adjust it. If we didn't previously - // have an SRC pipeline in place we'll need to initialize it. - if (pDSP->isSRCRequired) { - if (pDSP->config.sampleRateIn != pDSP->config.sampleRateOut) { - mal_src_set_output_sample_rate(&pDSP->src, sampleRateOut); - } else { - pDSP->isSRCRequired = MAL_FALSE; - } - } else { - // We may need a new SRC pipeline. - if (pDSP->config.sampleRateIn != pDSP->config.sampleRateOut) { - pDSP->isSRCRequired = MAL_TRUE; - - mal_src_config srcConfig; - srcConfig.sampleRateIn = pDSP->config.sampleRateIn; - srcConfig.sampleRateOut = pDSP->config.sampleRateOut; - srcConfig.formatIn = pDSP->config.formatIn; - srcConfig.formatOut = mal_format_f32; - srcConfig.channels = pDSP->config.channelsIn; - srcConfig.algorithm = mal_src_algorithm_linear; - srcConfig.cacheSizeInFrames = pDSP->config.cacheSizeInFrames; - mal_result result = mal_src_init(&srcConfig, mal_dsp__src_on_read, pDSP, &pDSP->src); - if (result != MAL_SUCCESS) { - return result; - } - } else { - pDSP->isSRCRequired = MAL_FALSE; - } + // Must have been initialized with allowDynamicSampleRate. + if (!pDSP->isDynamicSampleRateAllowed) { + return MAL_INVALID_OPERATION; } - // Update whether or not the pipeline is a passthrough. - if (pDSP->config.formatIn == pDSP->config.formatOut && pDSP->config.channelsIn == pDSP->config.channelsOut && pDSP->config.sampleRateIn == pDSP->config.sampleRateOut && !pDSP->isChannelMappingRequired) { - pDSP->isPassthrough = MAL_TRUE; - } else { - pDSP->isPassthrough = MAL_FALSE; - } - - return MAL_SUCCESS; + mal_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut); + return mal_dsp_refresh_sample_rate(pDSP); } -mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut) +mal_result mal_dsp_set_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut) { - return mal_dsp_read_frames_ex(pDSP, frameCount, pFramesOut, MAL_FALSE); + if (pDSP == NULL) { + return MAL_INVALID_ARGS; + } + + // Must have a sample rate of > 0. + if (sampleRateIn == 0 || sampleRateOut == 0) { + return MAL_INVALID_ARGS; + } + + // Must have been initialized with allowDynamicSampleRate. + if (!pDSP->isDynamicSampleRateAllowed) { + return MAL_INVALID_OPERATION; + } + + mal_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn); + mal_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut); + + return mal_dsp_refresh_sample_rate(pDSP); } -mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) +mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData) { if (pDSP == NULL || pFramesOut == NULL) return 0; // Fast path. if (pDSP->isPassthrough) { - return pDSP->onRead(pDSP, frameCount, pFramesOut, pDSP->pUserDataForOnRead); - } - - - // Slower path - where the real work is done. - mal_uint8 pFrames[2][MAL_MAX_CHANNELS * 512 * MAL_MAX_SAMPLE_SIZE_IN_BYTES]; - mal_format pFramesFormat[2]; - mal_uint32 iFrames = 0; // <-- Used as an index into pFrames and cycles between 0 and 1. - - mal_uint32 totalFramesRead = 0; - while (frameCount > 0) { - iFrames = 0; - - mal_uint32 framesToRead = mal_countof(pFrames[0]) / (mal_max(pDSP->config.channelsIn, pDSP->config.channelsOut) * MAL_MAX_SAMPLE_SIZE_IN_BYTES); - if (framesToRead > frameCount) { - framesToRead = frameCount; - } - - // The initial filling of sample data depends on whether or not we are using SRC. - mal_uint32 framesRead = 0; - if (pDSP->isSRCRequired) { - framesRead = mal_src_read_frames_ex(&pDSP->src, framesToRead, pFrames[iFrames], flush); - pFramesFormat[iFrames] = pDSP->src.config.formatOut; // Should always be f32. + if (frameCount <= 0xFFFFFFFF) { + return (mal_uint32)pDSP->onRead(pDSP, (mal_uint32)frameCount, pFramesOut, pUserData); } else { - framesRead = pDSP->onRead(pDSP, framesToRead, pFrames[iFrames], pDSP->pUserDataForOnRead); - pFramesFormat[iFrames] = pDSP->config.formatIn; - } + mal_uint8* pNextFramesOut = (mal_uint8*)pFramesOut; - if (framesRead == 0) { - break; - } + mal_uint64 totalFramesRead = 0; + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > 0xFFFFFFFF) { + framesToReadRightNow = 0xFFFFFFFF; + } + mal_uint32 framesRead = pDSP->onRead(pDSP, (mal_uint32)framesToReadRightNow, pNextFramesOut, pUserData); + if (framesRead == 0) { + break; + } - // Channel mixing. The input format must be in f32 which may require a conversion. - if (pDSP->config.channelsIn != pDSP->config.channelsOut) { - if (pFramesFormat[iFrames] != mal_format_f32) { - mal_pcm_convert(pFrames[(iFrames + 1) % 2], mal_format_f32, pFrames[iFrames], pDSP->config.formatIn, framesRead * pDSP->config.channelsIn); - iFrames = (iFrames + 1) % 2; - pFramesFormat[iFrames] = mal_format_f32; + pNextFramesOut += framesRead * pDSP->channelRouter.config.channelsOut * mal_get_bytes_per_sample(pDSP->formatConverterOut.config.formatOut); + totalFramesRead += framesRead; } - mal_dsp_mix_channels((float*)(pFrames[(iFrames + 1) % 2]), pDSP->config.channelsOut, pDSP->config.channelMapOut, (const float*)(pFrames[iFrames]), pDSP->config.channelsIn, pDSP->config.channelMapIn, framesRead, mal_channel_mix_mode_blend); - iFrames = (iFrames + 1) % 2; - pFramesFormat[iFrames] = mal_format_f32; + return totalFramesRead; } - - - // Channel mapping. - if (pDSP->isChannelMappingRequired) { - for (mal_uint32 i = 0; i < framesRead; ++i) { - mal_rearrange_channels(pFrames[iFrames] + (i * pDSP->config.channelsOut * mal_get_sample_size_in_bytes(pFramesFormat[iFrames])), pDSP->config.channelsOut, pDSP->channelShuffleTable, pFramesFormat[iFrames]); - } - } - - - // Final conversion to output format. - mal_pcm_convert(pFramesOut, pDSP->config.formatOut, pFrames[iFrames], pFramesFormat[iFrames], framesRead * pDSP->config.channelsOut); - - pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pDSP->config.channelsOut * mal_get_sample_size_in_bytes(pDSP->config.formatOut)); - frameCount -= framesRead; - totalFramesRead += framesRead; } - return totalFramesRead; + // Slower path. The real is done here. To do this all we need to do is read from the last stage in the pipeline. + mal_assert(pDSP->isPostFormatConversionRequired == MAL_TRUE); + + mal_dsp_callback_data data; + data.pDSP = pDSP; + data.pUserDataForClient = pUserData; + return mal_format_converter_read(&pDSP->formatConverterOut, frameCount, pFramesOut, &data); } -mal_uint32 mal_calculate_frame_count_after_src(mal_uint32 sampleRateOut, mal_uint32 sampleRateIn, mal_uint32 frameCountIn) -{ - double srcRatio = (double)sampleRateOut / sampleRateIn; - double frameCountOutF = frameCountIn * srcRatio; - - mal_uint32 frameCountOut = (mal_uint32)frameCountOutF; - - // If the output frame count is fractional, make sure we add an extra frame to ensure there's enough room for that last sample. - if ((frameCountOutF - frameCountOut) > 0.0) { - frameCountOut += 1; - } - - return frameCountOut; -} - typedef struct { const void* pDataIn; mal_format formatIn; mal_uint32 channelsIn; - mal_uint32 totalFrameCount; - mal_uint32 iNextFrame; + mal_uint64 totalFrameCount; + mal_uint64 iNextFrame; + mal_bool32 isFeedingZeros; // When set to true, feeds the DSP zero samples. } mal_convert_frames__data; mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) @@ -11152,25 +26562,78 @@ mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, voi mal_assert(pData->totalFrameCount >= pData->iNextFrame); mal_uint32 framesToRead = frameCount; - mal_uint32 framesRemaining = (pData->totalFrameCount - pData->iNextFrame); + mal_uint64 framesRemaining = (pData->totalFrameCount - pData->iNextFrame); if (framesToRead > framesRemaining) { - framesToRead = framesRemaining; + framesToRead = (mal_uint32)framesRemaining; } - mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(pData->formatIn) * pData->channelsIn; - mal_copy_memory(pFramesOut, (const mal_uint8*)pData->pDataIn + (frameSizeInBytes * pData->iNextFrame), frameSizeInBytes * framesToRead); + mal_uint32 frameSizeInBytes = mal_get_bytes_per_frame(pData->formatIn, pData->channelsIn); + + if (!pData->isFeedingZeros) { + mal_copy_memory(pFramesOut, (const mal_uint8*)pData->pDataIn + (frameSizeInBytes * pData->iNextFrame), frameSizeInBytes * framesToRead); + } else { + mal_zero_memory(pFramesOut, frameSizeInBytes * framesToRead); + } pData->iNextFrame += framesToRead; return framesToRead; } -mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint32 frameCountIn) +mal_dsp_config mal_dsp_config_init_new() +{ + mal_dsp_config config; + mal_zero_object(&config); + + return config; +} + +mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_dsp_read_proc onRead, void* pUserData) +{ + return mal_dsp_config_init_ex(formatIn, channelsIn, sampleRateIn, NULL, formatOut, channelsOut, sampleRateOut, NULL, onRead, pUserData); +} + +mal_dsp_config mal_dsp_config_init_ex(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_dsp_read_proc onRead, void* pUserData) +{ + mal_dsp_config config; + mal_zero_object(&config); + config.formatIn = formatIn; + config.channelsIn = channelsIn; + config.sampleRateIn = sampleRateIn; + config.formatOut = formatOut; + config.channelsOut = channelsOut; + config.sampleRateOut = sampleRateOut; + if (channelMapIn != NULL) { + mal_copy_memory(config.channelMapIn, channelMapIn, sizeof(config.channelMapIn)); + } + if (channelMapOut != NULL) { + mal_copy_memory(config.channelMapOut, channelMapOut, sizeof(config.channelMapOut)); + } + config.onRead = onRead; + config.pUserData = pUserData; + + return config; +} + + + +mal_uint64 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint64 frameCountIn) +{ + mal_channel channelMapOut[MAL_MAX_CHANNELS]; + mal_get_standard_channel_map(mal_standard_channel_map_default, channelsOut, channelMapOut); + + mal_channel channelMapIn[MAL_MAX_CHANNELS]; + mal_get_standard_channel_map(mal_standard_channel_map_default, channelsIn, channelMapIn); + + return mal_convert_frames_ex(pOut, formatOut, channelsOut, sampleRateOut, channelMapOut, pIn, formatIn, channelsIn, sampleRateIn, channelMapIn, frameCountIn); +} + +mal_uint64 mal_convert_frames_ex(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint64 frameCountIn) { if (frameCountIn == 0) { return 0; } - mal_uint32 frameCountOut = mal_calculate_frame_count_after_src(sampleRateOut, sampleRateIn, frameCountIn); + mal_uint64 frameCountOut = mal_calculate_frame_count_after_src(sampleRateOut, sampleRateIn, frameCountIn); if (pOut == NULL) { return frameCountOut; } @@ -11181,36 +26644,69 @@ mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 chann data.channelsIn = channelsIn; data.totalFrameCount = frameCountIn; data.iNextFrame = 0; + data.isFeedingZeros = MAL_FALSE; mal_dsp_config config; mal_zero_object(&config); + config.formatIn = formatIn; config.channelsIn = channelsIn; config.sampleRateIn = sampleRateIn; + if (channelMapIn != NULL) { + mal_channel_map_copy(config.channelMapIn, channelMapIn, channelsIn); + } else { + mal_get_standard_channel_map(mal_standard_channel_map_default, config.channelsIn, config.channelMapIn); + } + config.formatOut = formatOut; config.channelsOut = channelsOut; config.sampleRateOut = sampleRateOut; + if (channelMapOut != NULL) { + mal_channel_map_copy(config.channelMapOut, channelMapOut, channelsOut); + } else { + mal_get_standard_channel_map(mal_standard_channel_map_default, config.channelsOut, config.channelMapOut); + } + + config.onRead = mal_convert_frames__on_read; + config.pUserData = &data; mal_dsp dsp; - if (mal_dsp_init(&config, mal_convert_frames__on_read, &data, &dsp) != MAL_SUCCESS) { + if (mal_dsp_init(&config, &dsp) != MAL_SUCCESS) { return 0; } - return mal_dsp_read_frames_ex(&dsp, frameCountOut, pOut, MAL_TRUE); -} + // Always output our computed frame count. There is a chance the sample rate conversion routine may not output the last sample + // due to precision issues with 32-bit floats, in which case we should feed the DSP zero samples so it can generate that last + // frame. + mal_uint64 totalFramesRead = mal_dsp_read(&dsp, frameCountOut, pOut, dsp.pUserData); + if (totalFramesRead < frameCountOut) { + mal_uint32 bpf = mal_get_bytes_per_frame(formatIn, channelsIn); -mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut) -{ - mal_dsp_config config; - mal_zero_object(&config); - config.formatIn = formatIn; - config.channelsIn = channelsIn; - config.sampleRateIn = sampleRateIn; - config.formatOut = formatOut; - config.channelsOut = channelsOut; - config.sampleRateOut = sampleRateOut; + data.isFeedingZeros = MAL_TRUE; + data.totalFrameCount = 0xFFFFFFFFFFFFFFFF; + data.pDataIn = NULL; - return config; + while (totalFramesRead < frameCountOut) { + mal_uint64 framesToRead = (frameCountOut - totalFramesRead); + mal_assert(framesToRead > 0); + + mal_uint64 framesJustRead = mal_dsp_read(&dsp, framesToRead, mal_offset_ptr(pOut, totalFramesRead * bpf), dsp.pUserData); + totalFramesRead += framesJustRead; + + if (framesJustRead < framesToRead) { + break; + } + } + + // At this point we should have output every sample, but just to be super duper sure, just fill the rest with zeros. + if (totalFramesRead < frameCountOut) { + mal_zero_memory_64(mal_offset_ptr(pOut, totalFramesRead * bpf), ((frameCountOut - totalFramesRead) * bpf)); + totalFramesRead = frameCountOut; + } + } + + mal_assert(totalFramesRead == frameCountOut); + return totalFramesRead; } @@ -11224,24 +26720,43 @@ mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, m ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -const char* mal_get_backend_name(mal_backend backend) +void* mal_malloc(size_t sz) { - switch (backend) - { - case mal_backend_null: return "Null"; - case mal_backend_wasapi: return "WASAPI"; - case mal_backend_dsound: return "DirectSound"; - case mal_backend_winmm: return "WinMM"; - case mal_backend_alsa: return "ALSA"; - //case mal_backend_pulse: return "PulseAudio"; - //case mal_backend_jack: return "JACK"; - //case mal_backend_coreaudio: return "Core Audio"; - case mal_backend_oss: return "OSS"; - case mal_backend_opensl: return "OpenSL|ES"; - case mal_backend_openal: return "OpenAL"; - case mal_backend_sdl: return "SDL"; - default: return "Unknown"; + return MAL_MALLOC(sz); +} + +void* mal_realloc(void* p, size_t sz) +{ + return MAL_REALLOC(p, sz); +} + +void mal_free(void* p) +{ + MAL_FREE(p); +} + +void* mal_aligned_malloc(size_t sz, size_t alignment) +{ + if (alignment == 0) { + return 0; } + + size_t extraBytes = alignment-1 + sizeof(void*); + + void* pUnaligned = mal_malloc(sz + extraBytes); + if (pUnaligned == NULL) { + return NULL; + } + + void* pAligned = (void*)(((mal_uintptr)pUnaligned + extraBytes) & ~((mal_uintptr)(alignment-1))); + ((void**)pAligned)[-1] = pUnaligned; + + return pAligned; +} + +void mal_aligned_free(void* p) +{ + mal_free(((void**)p)[-1]); } const char* mal_get_format_name(mal_format format) @@ -11266,268 +26781,1638 @@ void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint } +mal_uint32 mal_get_bytes_per_sample(mal_format format) +{ + mal_uint32 sizes[] = { + 0, // unknown + 1, // u8 + 2, // s16 + 3, // s24 + 4, // s32 + 4, // f32 + }; + return sizes[format]; +} -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// -// -// AUTO-GENERATED -// -// -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // -// FORMAT CONVERSION +// DECODING // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void mal_pcm_u8_to_s16(short* pOut, const unsigned char* pIn, unsigned int count) +#ifndef MAL_NO_DECODING + +mal_decoder_config mal_decoder_config_init(mal_format outputFormat, mal_uint32 outputChannels, mal_uint32 outputSampleRate) { - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x - 128; - r = r << 8; - pOut[i] = (short)r; + mal_decoder_config config; + mal_zero_object(&config); + config.format = outputFormat; + config.channels = outputChannels; + config.sampleRate = outputSampleRate; + mal_get_standard_channel_map(mal_standard_channel_map_default, config.channels, config.channelMap); + + return config; +} + +mal_decoder_config mal_decoder_config_init_copy(const mal_decoder_config* pConfig) +{ + mal_decoder_config config; + if (pConfig != NULL) { + config = *pConfig; + } else { + mal_zero_object(&config); + } + + return config; +} + +mal_result mal_decoder__init_dsp(mal_decoder* pDecoder, const mal_decoder_config* pConfig, mal_dsp_read_proc onRead) +{ + mal_assert(pDecoder != NULL); + + // Output format. + if (pConfig->format == mal_format_unknown) { + pDecoder->outputFormat = pDecoder->internalFormat; + } else { + pDecoder->outputFormat = pConfig->format; + } + + if (pConfig->channels == 0) { + pDecoder->outputChannels = pDecoder->internalChannels; + } else { + pDecoder->outputChannels = pConfig->channels; + } + + if (pConfig->sampleRate == 0) { + pDecoder->outputSampleRate = pDecoder->internalSampleRate; + } else { + pDecoder->outputSampleRate = pConfig->sampleRate; + } + + if (mal_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) { + mal_get_standard_channel_map(mal_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap); + } else { + mal_copy_memory(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap)); + } + + + // DSP. + mal_dsp_config dspConfig = mal_dsp_config_init_ex( + pDecoder->internalFormat, pDecoder->internalChannels, pDecoder->internalSampleRate, pDecoder->internalChannelMap, + pDecoder->outputFormat, pDecoder->outputChannels, pDecoder->outputSampleRate, pDecoder->outputChannelMap, + onRead, pDecoder); + dspConfig.channelMixMode = pConfig->channelMixMode; + dspConfig.ditherMode = pConfig->ditherMode; + dspConfig.srcAlgorithm = pConfig->srcAlgorithm; + dspConfig.sinc = pConfig->src.sinc; + + return mal_dsp_init(&dspConfig, &pDecoder->dsp); +} + +// WAV +#ifdef dr_wav_h +#define MAL_HAS_WAV + +size_t mal_decoder_internal_on_read__wav(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onRead != NULL); + + return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead); +} + +drwav_bool32 mal_decoder_internal_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin) +{ + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onSeek != NULL); + + return pDecoder->onSeek(pDecoder, offset, (origin == drwav_seek_origin_start) ? mal_seek_origin_start : mal_seek_origin_current); +} + +mal_result mal_decoder_internal_on_seek_to_frame__wav(mal_decoder* pDecoder, mal_uint64 frameIndex) +{ + drwav* pWav = (drwav*)pDecoder->pInternalDecoder; + mal_assert(pWav != NULL); + + drwav_bool32 result = drwav_seek_to_sample(pWav, frameIndex*pWav->channels); + if (result) { + return MAL_SUCCESS; + } else { + return MAL_ERROR; } } -void mal_pcm_u8_to_s24(void* pOut, const unsigned char* pIn, unsigned int count) +mal_result mal_decoder_internal_on_uninit__wav(mal_decoder* pDecoder) { - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x - 128; - r = r << 16; - ((unsigned char*)pOut)[(i*3)+0] = (unsigned char)(r & 0xFF); ((unsigned char*)pOut)[(i*3)+1] = (unsigned char)((r & 0xFF00) >> 8); ((unsigned char*)pOut)[(i*3)+2] = (unsigned char)((r & 0xFF0000) >> 16); - } + drwav_close((drwav*)pDecoder->pInternalDecoder); + return MAL_SUCCESS; } -void mal_pcm_u8_to_s32(int* pOut, const unsigned char* pIn, unsigned int count) +mal_uint32 mal_decoder_internal_on_read_frames__wav(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData) { - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x - 128; - r = r << 24; - pOut[i] = (int)r; + (void)pDSP; + + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + + drwav* pWav = (drwav*)pDecoder->pInternalDecoder; + mal_assert(pWav != NULL); + + switch (pDecoder->internalFormat) { + case mal_format_s16: return (mal_uint32)drwav_read_s16(pWav, frameCount*pDecoder->internalChannels, (drwav_int16*)pSamplesOut) / pDecoder->internalChannels; + case mal_format_s32: return (mal_uint32)drwav_read_s32(pWav, frameCount*pDecoder->internalChannels, (drwav_int32*)pSamplesOut) / pDecoder->internalChannels; + case mal_format_f32: return (mal_uint32)drwav_read_f32(pWav, frameCount*pDecoder->internalChannels, (float*)pSamplesOut) / pDecoder->internalChannels; + default: break; } + + // Should never get here. If we do, it means the internal format was not set correctly at initialization time. + mal_assert(MAL_FALSE); + return 0; } -void mal_pcm_u8_to_f32(float* pOut, const unsigned char* pIn, unsigned int count) +mal_result mal_decoder_init_wav__internal(const mal_decoder_config* pConfig, mal_decoder* pDecoder) { - float r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x * 0.00784313725490196078f; - r = r - 1; - pOut[i] = (float)r; - } -} + mal_assert(pConfig != NULL); + mal_assert(pDecoder != NULL); -void mal_pcm_s16_to_u8(unsigned char* pOut, const short* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x >> 8; - r = r + 128; - pOut[i] = (unsigned char)r; + // Try opening the decoder first. + drwav* pWav = drwav_open(mal_decoder_internal_on_read__wav, mal_decoder_internal_on_seek__wav, pDecoder); + if (pWav == NULL) { + return MAL_ERROR; } -} -void mal_pcm_s16_to_s24(void* pOut, const short* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x << 8; - ((unsigned char*)pOut)[(i*3)+0] = (unsigned char)(r & 0xFF); ((unsigned char*)pOut)[(i*3)+1] = (unsigned char)((r & 0xFF00) >> 8); ((unsigned char*)pOut)[(i*3)+2] = (unsigned char)((r & 0xFF0000) >> 16); + // If we get here it means we successfully initialized the WAV decoder. We can now initialize the rest of the mal_decoder. + pDecoder->onSeekToFrame = mal_decoder_internal_on_seek_to_frame__wav; + pDecoder->onUninit = mal_decoder_internal_on_uninit__wav; + pDecoder->pInternalDecoder = pWav; + + // Try to be as optimal as possible for the internal format. If mini_al does not support a format we will fall back to f32. + pDecoder->internalFormat = mal_format_unknown; + switch (pWav->translatedFormatTag) { + case DR_WAVE_FORMAT_PCM: + { + if (pWav->bitsPerSample == 8) { + pDecoder->internalFormat = mal_format_s16; + } else if (pWav->bitsPerSample == 16) { + pDecoder->internalFormat = mal_format_s16; + } else if (pWav->bitsPerSample == 32) { + pDecoder->internalFormat = mal_format_s32; + } + } break; + + case DR_WAVE_FORMAT_IEEE_FLOAT: + { + if (pWav->bitsPerSample == 32) { + pDecoder->internalFormat = mal_format_f32; + } + } break; + + case DR_WAVE_FORMAT_ALAW: + case DR_WAVE_FORMAT_MULAW: + case DR_WAVE_FORMAT_ADPCM: + case DR_WAVE_FORMAT_DVI_ADPCM: + { + pDecoder->internalFormat = mal_format_s16; + } break; } -} -void mal_pcm_s16_to_s32(int* pOut, const short* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x << 16; - pOut[i] = (int)r; + if (pDecoder->internalFormat == mal_format_unknown) { + pDecoder->internalFormat = mal_format_f32; } -} -void mal_pcm_s16_to_f32(float* pOut, const short* pIn, unsigned int count) -{ - float r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = (float)(x + 32768); - r = r * 0.00003051804379339284f; - r = r - 1; - pOut[i] = (float)r; + pDecoder->internalChannels = pWav->channels; + pDecoder->internalSampleRate = pWav->sampleRate; + mal_get_standard_channel_map(mal_standard_channel_map_microsoft, pDecoder->internalChannels, pDecoder->internalChannelMap); + + mal_result result = mal_decoder__init_dsp(pDecoder, pConfig, mal_decoder_internal_on_read_frames__wav); + if (result != MAL_SUCCESS) { + drwav_close(pWav); + return result; } -} -void mal_pcm_s24_to_u8(unsigned char* pOut, const void* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8; - r = x >> 16; - r = r + 128; - pOut[i] = (unsigned char)r; - } + return MAL_SUCCESS; } - -void mal_pcm_s24_to_s16(short* pOut, const void* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8; - r = x >> 8; - pOut[i] = (short)r; - } -} - -void mal_pcm_s24_to_s32(int* pOut, const void* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8; - r = x << 8; - pOut[i] = (int)r; - } -} - -void mal_pcm_s24_to_f32(float* pOut, const void* pIn, unsigned int count) -{ - float r; - for (unsigned int i = 0; i < count; ++i) { - int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8; - r = (float)(x + 8388608); - r = r * 0.00000011920929665621f; - r = r - 1; - pOut[i] = (float)r; - } -} - -void mal_pcm_s32_to_u8(unsigned char* pOut, const int* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x >> 24; - r = r + 128; - pOut[i] = (unsigned char)r; - } -} - -void mal_pcm_s32_to_s16(short* pOut, const int* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x >> 16; - pOut[i] = (short)r; - } -} - -void mal_pcm_s32_to_s24(void* pOut, const int* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - r = x >> 8; - ((unsigned char*)pOut)[(i*3)+0] = (unsigned char)(r & 0xFF); ((unsigned char*)pOut)[(i*3)+1] = (unsigned char)((r & 0xFF00) >> 8); ((unsigned char*)pOut)[(i*3)+2] = (unsigned char)((r & 0xFF0000) >> 16); - } -} - -void mal_pcm_s32_to_f32(float* pOut, const int* pIn, unsigned int count) -{ - float r; - for (unsigned int i = 0; i < count; ++i) { - int x = pIn[i]; - double t; - t = (double)(x + 2147483647); - t = t + 1; - t = t * 0.0000000004656612873077392578125; - r = (float)(t - 1); - pOut[i] = (float)r; - } -} - -void mal_pcm_f32_to_u8(unsigned char* pOut, const float* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - float x = pIn[i]; - float c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 127.5f); - pOut[i] = (unsigned char)r; - } -} - -void mal_pcm_f32_to_s16(short* pOut, const float* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - float x = pIn[i]; - float c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5f); - r = r - 32768; - pOut[i] = (short)r; - } -} - -void mal_pcm_f32_to_s24(void* pOut, const float* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - float x = pIn[i]; - float c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 8388607.5f); - r = r - 8388608; - ((unsigned char*)pOut)[(i*3)+0] = (unsigned char)(r & 0xFF); ((unsigned char*)pOut)[(i*3)+1] = (unsigned char)((r & 0xFF00) >> 8); ((unsigned char*)pOut)[(i*3)+2] = (unsigned char)((r & 0xFF0000) >> 16); - } -} - -void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count) -{ - int r; - for (unsigned int i = 0; i < count; ++i) { - float x = pIn[i]; - float c; - mal_int64 t; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - t = (mal_int64)(c * 2147483647.5); - t = t - 2147483647; - r = (int)(t - 1); - pOut[i] = (int)r; - } -} - #endif +// FLAC +#ifdef dr_flac_h +#define MAL_HAS_FLAC + +size_t mal_decoder_internal_on_read__flac(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onRead != NULL); + + return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead); +} + +drflac_bool32 mal_decoder_internal_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin) +{ + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onSeek != NULL); + + return pDecoder->onSeek(pDecoder, offset, (origin == drflac_seek_origin_start) ? mal_seek_origin_start : mal_seek_origin_current); +} + +mal_result mal_decoder_internal_on_seek_to_frame__flac(mal_decoder* pDecoder, mal_uint64 frameIndex) +{ + drflac* pFlac = (drflac*)pDecoder->pInternalDecoder; + mal_assert(pFlac != NULL); + + drflac_bool32 result = drflac_seek_to_sample(pFlac, frameIndex*pFlac->channels); + if (result) { + return MAL_SUCCESS; + } else { + return MAL_ERROR; + } +} + +mal_result mal_decoder_internal_on_uninit__flac(mal_decoder* pDecoder) +{ + drflac_close((drflac*)pDecoder->pInternalDecoder); + return MAL_SUCCESS; +} + +mal_uint32 mal_decoder_internal_on_read_frames__flac(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData) +{ + (void)pDSP; + + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->internalFormat == mal_format_s32); + + drflac* pFlac = (drflac*)pDecoder->pInternalDecoder; + mal_assert(pFlac != NULL); + + return (mal_uint32)drflac_read_s32(pFlac, frameCount*pDecoder->internalChannels, (drflac_int32*)pSamplesOut) / pDecoder->internalChannels; +} + +mal_result mal_decoder_init_flac__internal(const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_assert(pConfig != NULL); + mal_assert(pDecoder != NULL); + + // Try opening the decoder first. + drflac* pFlac = drflac_open(mal_decoder_internal_on_read__flac, mal_decoder_internal_on_seek__flac, pDecoder); + if (pFlac == NULL) { + return MAL_ERROR; + } + + // If we get here it means we successfully initialized the FLAC decoder. We can now initialize the rest of the mal_decoder. + pDecoder->onSeekToFrame = mal_decoder_internal_on_seek_to_frame__flac; + pDecoder->onUninit = mal_decoder_internal_on_uninit__flac; + pDecoder->pInternalDecoder = pFlac; + + // The internal format is always s32. + pDecoder->internalFormat = mal_format_s32; + pDecoder->internalChannels = pFlac->channels; + pDecoder->internalSampleRate = pFlac->sampleRate; + mal_get_standard_channel_map(mal_standard_channel_map_flac, pDecoder->internalChannels, pDecoder->internalChannelMap); + + mal_result result = mal_decoder__init_dsp(pDecoder, pConfig, mal_decoder_internal_on_read_frames__flac); + if (result != MAL_SUCCESS) { + drflac_close(pFlac); + return result; + } + + return MAL_SUCCESS; +} +#endif + +// Vorbis +#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define MAL_HAS_VORBIS + +// The size in bytes of each chunk of data to read from the Vorbis stream. +#define MAL_VORBIS_DATA_CHUNK_SIZE 4096 + +typedef struct +{ + stb_vorbis* pInternalVorbis; + mal_uint8* pData; + size_t dataSize; + size_t dataCapacity; + mal_uint32 framesConsumed; // The number of frames consumed in ppPacketData. + mal_uint32 framesRemaining; // The number of frames remaining in ppPacketData. + float** ppPacketData; +} mal_vorbis_decoder; + +mal_uint32 mal_vorbis_decoder_read(mal_vorbis_decoder* pVorbis, mal_decoder* pDecoder, mal_uint32 frameCount, void* pSamplesOut) +{ + mal_assert(pVorbis != NULL); + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onRead != NULL); + mal_assert(pDecoder->onSeek != NULL); + + float* pSamplesOutF = (float*)pSamplesOut; + + mal_uint32 totalFramesRead = 0; + while (frameCount > 0) { + // Read from the in-memory buffer first. + while (pVorbis->framesRemaining > 0 && frameCount > 0) { + for (mal_uint32 iChannel = 0; iChannel < pDecoder->internalChannels; ++iChannel) { + pSamplesOutF[0] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed]; + pSamplesOutF += 1; + } + + pVorbis->framesConsumed += 1; + pVorbis->framesRemaining -= 1; + frameCount -= 1; + totalFramesRead += 1; + } + + if (frameCount == 0) { + break; + } + + mal_assert(pVorbis->framesRemaining == 0); + + // We've run out of cached frames, so decode the next packet and continue iteration. + do + { + if (pVorbis->dataSize > INT_MAX) { + break; // Too big. + } + + int samplesRead = 0; + int consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->pInternalVorbis, pVorbis->pData, (int)pVorbis->dataSize, NULL, (float***)&pVorbis->ppPacketData, &samplesRead); + if (consumedDataSize != 0) { + size_t leftoverDataSize = (pVorbis->dataSize - (size_t)consumedDataSize); + for (size_t i = 0; i < leftoverDataSize; ++i) { + pVorbis->pData[i] = pVorbis->pData[i + consumedDataSize]; + } + + pVorbis->dataSize = leftoverDataSize; + pVorbis->framesConsumed = 0; + pVorbis->framesRemaining = samplesRead; + break; + } else { + // Need more data. If there's any room in the existing buffer allocation fill that first. Otherwise expand. + if (pVorbis->dataCapacity == pVorbis->dataSize) { + // No room. Expand. + pVorbis->dataCapacity += MAL_VORBIS_DATA_CHUNK_SIZE; + mal_uint8* pNewData = (mal_uint8*)mal_realloc(pVorbis->pData, pVorbis->dataCapacity); + if (pNewData == NULL) { + return totalFramesRead; // Out of memory. + } + + pVorbis->pData = pNewData; + } + + // Fill in a chunk. + size_t bytesRead = pDecoder->onRead(pDecoder, pVorbis->pData + pVorbis->dataSize, (pVorbis->dataCapacity - pVorbis->dataSize)); + if (bytesRead == 0) { + return totalFramesRead; // Error reading more data. + } + + pVorbis->dataSize += bytesRead; + } + } while (MAL_TRUE); + } + + return totalFramesRead; +} + +mal_result mal_vorbis_decoder_seek_to_frame(mal_vorbis_decoder* pVorbis, mal_decoder* pDecoder, mal_uint64 frameIndex) +{ + mal_assert(pVorbis != NULL); + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onRead != NULL); + mal_assert(pDecoder->onSeek != NULL); + + // This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs + // a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we + // find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis. + if (!pDecoder->onSeek(pDecoder, 0, mal_seek_origin_start)) { + return MAL_ERROR; + } + + stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis); + pVorbis->framesConsumed = 0; + pVorbis->framesRemaining = 0; + pVorbis->dataSize = 0; + + float buffer[4096]; + while (frameIndex > 0) { + mal_uint32 framesToRead = mal_countof(buffer)/pDecoder->internalChannels; + if (framesToRead > frameIndex) { + framesToRead = (mal_uint32)frameIndex; + } + + mal_uint32 framesRead = mal_vorbis_decoder_read(pVorbis, pDecoder, framesToRead, buffer); + if (framesRead == 0) { + return MAL_ERROR; + } + + frameIndex -= framesRead; + } + + return MAL_SUCCESS; +} + + +mal_result mal_decoder_internal_on_seek_to_frame__vorbis(mal_decoder* pDecoder, mal_uint64 frameIndex) +{ + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onRead != NULL); + mal_assert(pDecoder->onSeek != NULL); + + mal_vorbis_decoder* pVorbis = (mal_vorbis_decoder*)pDecoder->pInternalDecoder; + mal_assert(pVorbis != NULL); + + return mal_vorbis_decoder_seek_to_frame(pVorbis, pDecoder, frameIndex); +} + +mal_result mal_decoder_internal_on_uninit__vorbis(mal_decoder* pDecoder) +{ + mal_vorbis_decoder* pVorbis = (mal_vorbis_decoder*)pDecoder->pInternalDecoder; + mal_assert(pVorbis != NULL); + + stb_vorbis_close(pVorbis->pInternalVorbis); + mal_free(pVorbis->pData); + mal_free(pVorbis); + + return MAL_SUCCESS; +} + +mal_uint32 mal_decoder_internal_on_read_frames__vorbis(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData) +{ + (void)pDSP; + + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->internalFormat == mal_format_f32); + mal_assert(pDecoder->onRead != NULL); + mal_assert(pDecoder->onSeek != NULL); + + mal_vorbis_decoder* pVorbis = (mal_vorbis_decoder*)pDecoder->pInternalDecoder; + mal_assert(pVorbis != NULL); + + return mal_vorbis_decoder_read(pVorbis, pDecoder, frameCount, pSamplesOut); +} + +mal_result mal_decoder_init_vorbis__internal(const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_assert(pConfig != NULL); + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onRead != NULL); + mal_assert(pDecoder->onSeek != NULL); + + stb_vorbis* pInternalVorbis = NULL; + + // We grow the buffer in chunks. + size_t dataSize = 0; + size_t dataCapacity = 0; + mal_uint8* pData = NULL; + do + { + // Allocate memory for a new chunk. + dataCapacity += MAL_VORBIS_DATA_CHUNK_SIZE; + mal_uint8* pNewData = (mal_uint8*)mal_realloc(pData, dataCapacity); + if (pNewData == NULL) { + mal_free(pData); + return MAL_OUT_OF_MEMORY; + } + + pData = pNewData; + + // Fill in a chunk. + size_t bytesRead = pDecoder->onRead(pDecoder, pData + dataSize, (dataCapacity - dataSize)); + if (bytesRead == 0) { + return MAL_ERROR; + } + + dataSize += bytesRead; + if (dataSize > INT_MAX) { + return MAL_ERROR; // Too big. + } + + int vorbisError = 0; + int consumedDataSize = 0; + pInternalVorbis = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); + if (pInternalVorbis != NULL) { + // If we get here it means we were able to open the stb_vorbis decoder. There may be some leftover bytes in our buffer, so + // we need to move those bytes down to the front of the buffer since they'll be needed for future decoding. + size_t leftoverDataSize = (dataSize - (size_t)consumedDataSize); + for (size_t i = 0; i < leftoverDataSize; ++i) { + pData[i] = pData[i + consumedDataSize]; + } + + dataSize = leftoverDataSize; + break; // Success. + } else { + if (vorbisError == VORBIS_need_more_data) { + continue; + } else { + return MAL_ERROR; // Failed to open the stb_vorbis decoder. + } + } + } while (MAL_TRUE); + + + // If we get here it means we successfully opened the Vorbis decoder. + stb_vorbis_info vorbisInfo = stb_vorbis_get_info(pInternalVorbis); + + // Don't allow more than MAL_MAX_CHANNELS channels. + if (vorbisInfo.channels > MAL_MAX_CHANNELS) { + stb_vorbis_close(pInternalVorbis); + mal_free(pData); + return MAL_ERROR; // Too many channels. + } + + size_t vorbisDataSize = sizeof(mal_vorbis_decoder) + sizeof(float)*vorbisInfo.max_frame_size; + mal_vorbis_decoder* pVorbis = (mal_vorbis_decoder*)mal_malloc(vorbisDataSize); + if (pVorbis == NULL) { + stb_vorbis_close(pInternalVorbis); + mal_free(pData); + return MAL_OUT_OF_MEMORY; + } + + mal_zero_memory(pVorbis, vorbisDataSize); + pVorbis->pInternalVorbis = pInternalVorbis; + pVorbis->pData = pData; + pVorbis->dataSize = dataSize; + pVorbis->dataCapacity = dataCapacity; + + pDecoder->onSeekToFrame = mal_decoder_internal_on_seek_to_frame__vorbis; + pDecoder->onUninit = mal_decoder_internal_on_uninit__vorbis; + pDecoder->pInternalDecoder = pVorbis; + + // The internal format is always f32. + pDecoder->internalFormat = mal_format_f32; + pDecoder->internalChannels = vorbisInfo.channels; + pDecoder->internalSampleRate = vorbisInfo.sample_rate; + mal_get_standard_channel_map(mal_standard_channel_map_vorbis, pDecoder->internalChannels, pDecoder->internalChannelMap); + + mal_result result = mal_decoder__init_dsp(pDecoder, pConfig, mal_decoder_internal_on_read_frames__vorbis); + if (result != MAL_SUCCESS) { + stb_vorbis_close(pVorbis->pInternalVorbis); + mal_free(pVorbis->pData); + mal_free(pVorbis); + return result; + } + + return MAL_SUCCESS; +} +#endif + +// MP3 +#ifdef dr_mp3_h +#define MAL_HAS_MP3 + +size_t mal_decoder_internal_on_read__mp3(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onRead != NULL); + + return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead); +} + +drmp3_bool32 mal_decoder_internal_on_seek__mp3(void* pUserData, int offset, drmp3_seek_origin origin) +{ + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->onSeek != NULL); + + return pDecoder->onSeek(pDecoder, offset, (origin == drmp3_seek_origin_start) ? mal_seek_origin_start : mal_seek_origin_current); +} + +mal_result mal_decoder_internal_on_seek_to_frame__mp3(mal_decoder* pDecoder, mal_uint64 frameIndex) +{ + drmp3* pMP3 = (drmp3*)pDecoder->pInternalDecoder; + mal_assert(pMP3 != NULL); + + drmp3_bool32 result = drmp3_seek_to_frame(pMP3, frameIndex); + if (result) { + return MAL_SUCCESS; + } else { + return MAL_ERROR; + } +} + +mal_result mal_decoder_internal_on_uninit__mp3(mal_decoder* pDecoder) +{ + drmp3_uninit((drmp3*)pDecoder->pInternalDecoder); + mal_free(pDecoder->pInternalDecoder); + return MAL_SUCCESS; +} + +mal_uint32 mal_decoder_internal_on_read_frames__mp3(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData) +{ + (void)pDSP; + + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + mal_assert(pDecoder->internalFormat == mal_format_f32); + + drmp3* pMP3 = (drmp3*)pDecoder->pInternalDecoder; + mal_assert(pMP3 != NULL); + + return (mal_uint32)drmp3_read_f32(pMP3, frameCount, (float*)pSamplesOut); +} + +mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_assert(pConfig != NULL); + mal_assert(pDecoder != NULL); + + drmp3* pMP3 = (drmp3*)mal_malloc(sizeof(*pMP3)); + if (pMP3 == NULL) { + return MAL_OUT_OF_MEMORY; + } + + // Try opening the decoder first. MP3 can have variable sample rates (it's per frame/packet). We therefore need + // to use some smarts to determine the most appropriate internal sample rate. These are the rules we're going + // to use: + // + // Sample Rates + // 1) If an output sample rate is specified in pConfig we just use that. Otherwise; + // 2) Fall back to 44100. + // + // The internal channel count is always stereo, and the internal format is always f32. + drmp3_config mp3Config; + mal_zero_object(&mp3Config); + mp3Config.outputChannels = 2; + mp3Config.outputSampleRate = (pConfig->sampleRate != 0) ? pConfig->sampleRate : 44100; + if (!drmp3_init(pMP3, mal_decoder_internal_on_read__mp3, mal_decoder_internal_on_seek__mp3, pDecoder, &mp3Config)) { + return MAL_ERROR; + } + + // If we get here it means we successfully initialized the MP3 decoder. We can now initialize the rest of the mal_decoder. + pDecoder->onSeekToFrame = mal_decoder_internal_on_seek_to_frame__mp3; + pDecoder->onUninit = mal_decoder_internal_on_uninit__mp3; + pDecoder->pInternalDecoder = pMP3; + + // Internal format. + pDecoder->internalFormat = mal_format_f32; + pDecoder->internalChannels = pMP3->channels; + pDecoder->internalSampleRate = pMP3->sampleRate; + mal_get_standard_channel_map(mal_standard_channel_map_default, pDecoder->internalChannels, pDecoder->internalChannelMap); + + mal_result result = mal_decoder__init_dsp(pDecoder, pConfig, mal_decoder_internal_on_read_frames__mp3); + if (result != MAL_SUCCESS) { + mal_free(pMP3); + return result; + } + + return MAL_SUCCESS; +} +#endif + +// Raw +mal_result mal_decoder_internal_on_seek_to_frame__raw(mal_decoder* pDecoder, mal_uint64 frameIndex) +{ + mal_assert(pDecoder != NULL); + + if (pDecoder->onSeek == NULL) { + return MAL_ERROR; + } + + mal_bool32 result = MAL_FALSE; + + // The callback uses a 32 bit integer whereas we use a 64 bit unsigned integer. We just need to continuously seek until we're at the correct position. + mal_uint64 totalBytesToSeek = frameIndex * mal_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels); + if (totalBytesToSeek < 0x7FFFFFFF) { + // Simple case. + result = pDecoder->onSeek(pDecoder, (int)(frameIndex * mal_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels)), mal_seek_origin_start); + } else { + // Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking. + result = pDecoder->onSeek(pDecoder, 0x7FFFFFFF, mal_seek_origin_start); + if (result == MAL_TRUE) { + totalBytesToSeek -= 0x7FFFFFFF; + + while (totalBytesToSeek > 0) { + mal_uint64 bytesToSeekThisIteration = totalBytesToSeek; + if (bytesToSeekThisIteration > 0x7FFFFFFF) { + bytesToSeekThisIteration = 0x7FFFFFFF; + } + + result = pDecoder->onSeek(pDecoder, (int)bytesToSeekThisIteration, mal_seek_origin_current); + if (result != MAL_TRUE) { + break; + } + + totalBytesToSeek -= bytesToSeekThisIteration; + } + } + } + + if (result) { + return MAL_SUCCESS; + } else { + return MAL_ERROR; + } +} + +mal_result mal_decoder_internal_on_uninit__raw(mal_decoder* pDecoder) +{ + (void)pDecoder; + return MAL_SUCCESS; +} + +mal_uint32 mal_decoder_internal_on_read_frames__raw(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData) +{ + (void)pDSP; + + mal_decoder* pDecoder = (mal_decoder*)pUserData; + mal_assert(pDecoder != NULL); + + // For raw decoding we just read directly from the decoder's callbacks. + mal_uint32 bpf = mal_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels); + return (mal_uint32)pDecoder->onRead(pDecoder, pSamplesOut, frameCount * bpf) / bpf; +} + +mal_result mal_decoder_init_raw__internal(const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder) +{ + mal_assert(pConfigIn != NULL); + mal_assert(pConfigOut != NULL); + mal_assert(pDecoder != NULL); + + pDecoder->onSeekToFrame = mal_decoder_internal_on_seek_to_frame__raw; + pDecoder->onUninit = mal_decoder_internal_on_uninit__raw; + + // Internal format. + pDecoder->internalFormat = pConfigIn->format; + pDecoder->internalChannels = pConfigIn->channels; + pDecoder->internalSampleRate = pConfigIn->sampleRate; + mal_channel_map_copy(pDecoder->internalChannelMap, pConfigIn->channelMap, pConfigIn->channels); + + mal_result result = mal_decoder__init_dsp(pDecoder, pConfigOut, mal_decoder_internal_on_read_frames__raw); + if (result != MAL_SUCCESS) { + return result; + } + + return MAL_SUCCESS; +} + +mal_result mal_decoder__preinit(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_assert(pConfig != NULL); + + if (pDecoder == NULL) { + return MAL_INVALID_ARGS; + } + + mal_zero_object(pDecoder); + + if (onRead == NULL || onSeek == NULL) { + return MAL_INVALID_ARGS; + } + + pDecoder->onRead = onRead; + pDecoder->onSeek = onSeek; + pDecoder->pUserData = pUserData; + + (void)pConfig; + return MAL_SUCCESS; +} + +mal_result mal_decoder_init_wav(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); + + mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + +#ifdef MAL_HAS_WAV + return mal_decoder_init_wav__internal(&config, pDecoder); +#else + return MAL_NO_BACKEND; +#endif +} + +mal_result mal_decoder_init_flac(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); + + mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + +#ifdef MAL_HAS_FLAC + return mal_decoder_init_flac__internal(&config, pDecoder); +#else + return MAL_NO_BACKEND; +#endif +} + +mal_result mal_decoder_init_vorbis(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); + + mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + +#ifdef MAL_HAS_VORBIS + return mal_decoder_init_vorbis__internal(&config, pDecoder); +#else + return MAL_NO_BACKEND; +#endif +} + +mal_result mal_decoder_init_mp3(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); + + mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + +#ifdef MAL_HAS_MP3 + return mal_decoder_init_mp3__internal(&config, pDecoder); +#else + return MAL_NO_BACKEND; +#endif +} + +mal_result mal_decoder_init_raw(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfigOut); + + mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder_init_raw__internal(pConfigIn, &config, pDecoder); +} + +mal_result mal_decoder_init__internal(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_assert(pConfig != NULL); + mal_assert(pDecoder != NULL); + + // Silence some warnings in the case that we don't have any decoder backends enabled. + (void)onRead; + (void)onSeek; + (void)pUserData; + (void)pConfig; + (void)pDecoder; + + // We use trial and error to open a decoder. + mal_result result = MAL_NO_BACKEND; + +#ifdef MAL_HAS_WAV + if (result != MAL_SUCCESS) { + result = mal_decoder_init_wav__internal(pConfig, pDecoder); + if (result != MAL_SUCCESS) { + onSeek(pDecoder, 0, mal_seek_origin_start); + } + } +#endif +#ifdef MAL_HAS_FLAC + if (result != MAL_SUCCESS) { + result = mal_decoder_init_flac__internal(pConfig, pDecoder); + if (result != MAL_SUCCESS) { + onSeek(pDecoder, 0, mal_seek_origin_start); + } + } +#endif +#ifdef MAL_HAS_VORBIS + if (result != MAL_SUCCESS) { + result = mal_decoder_init_vorbis__internal(pConfig, pDecoder); + if (result != MAL_SUCCESS) { + onSeek(pDecoder, 0, mal_seek_origin_start); + } + } +#endif +#ifdef MAL_HAS_MP3 + if (result != MAL_SUCCESS) { + result = mal_decoder_init_mp3__internal(pConfig, pDecoder); + if (result != MAL_SUCCESS) { + onSeek(pDecoder, 0, mal_seek_origin_start); + } + } +#endif + + if (result != MAL_SUCCESS) { + return result; + } + + return result; +} + +mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); + + mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); +} + + +size_t mal_decoder__on_read_memory(mal_decoder* pDecoder, void* pBufferOut, size_t bytesToRead) +{ + mal_assert(pDecoder->memory.dataSize >= pDecoder->memory.currentReadPos); + + size_t bytesRemaining = pDecoder->memory.dataSize - pDecoder->memory.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + mal_copy_memory(pBufferOut, pDecoder->memory.pData + pDecoder->memory.currentReadPos, bytesToRead); + pDecoder->memory.currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +mal_bool32 mal_decoder__on_seek_memory(mal_decoder* pDecoder, int byteOffset, mal_seek_origin origin) +{ + if (origin == mal_seek_origin_current) { + if (byteOffset > 0) { + if (pDecoder->memory.currentReadPos + byteOffset > pDecoder->memory.dataSize) { + byteOffset = (int)(pDecoder->memory.dataSize - pDecoder->memory.currentReadPos); // Trying to seek too far forward. + } + } else { + if (pDecoder->memory.currentReadPos < (size_t)-byteOffset) { + byteOffset = -(int)pDecoder->memory.currentReadPos; // Trying to seek too far backwards. + } + } + + // This will never underflow thanks to the clamps above. + pDecoder->memory.currentReadPos += byteOffset; + } else { + if ((mal_uint32)byteOffset <= pDecoder->memory.dataSize) { + pDecoder->memory.currentReadPos = byteOffset; + } else { + pDecoder->memory.currentReadPos = pDecoder->memory.dataSize; // Trying to seek too far forward. + } + } + + return MAL_TRUE; +} + +mal_result mal_decoder__preinit_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_result result = mal_decoder__preinit(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + + if (pData == NULL || dataSize == 0) { + return MAL_INVALID_ARGS; + } + + pDecoder->memory.pData = (const mal_uint8*)pData; + pDecoder->memory.dataSize = dataSize; + pDecoder->memory.currentReadPos = 0; + + (void)pConfig; + return MAL_SUCCESS; +} + +mal_result mal_decoder_init_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL. + + mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder_init__internal(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, &config, pDecoder); +} + +mal_result mal_decoder_init_memory_wav(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL. + + mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + +#ifdef MAL_HAS_WAV + return mal_decoder_init_wav__internal(&config, pDecoder); +#else + return MAL_NO_BACKEND; +#endif +} + +mal_result mal_decoder_init_memory_flac(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL. + + mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + +#ifdef MAL_HAS_FLAC + return mal_decoder_init_flac__internal(&config, pDecoder); +#else + return MAL_NO_BACKEND; +#endif +} + +mal_result mal_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL. + + mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + +#ifdef MAL_HAS_VORBIS + return mal_decoder_init_vorbis__internal(&config, pDecoder); +#else + return MAL_NO_BACKEND; +#endif +} + +mal_result mal_decoder_init_memory_mp3(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL. + + mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + +#ifdef MAL_HAS_MP3 + return mal_decoder_init_mp3__internal(&config, pDecoder); +#else + return MAL_NO_BACKEND; +#endif +} + +mal_result mal_decoder_init_memory_raw(const void* pData, size_t dataSize, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder) +{ + mal_decoder_config config = mal_decoder_config_init_copy(pConfigOut); // Make sure the config is not NULL. + + mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder_init_raw__internal(pConfigIn, &config, pDecoder); +} + +#ifndef MAL_NO_STDIO +#include +#if !defined(_MSC_VER) && !defined(__DMC__) +#include // For strcasecmp(). +#endif + +const char* mal_path_file_name(const char* path) +{ + if (path == NULL) { + return NULL; + } + + const char* fileName = path; + + // We just loop through the path until we find the last slash. + while (path[0] != '\0') { + if (path[0] == '/' || path[0] == '\\') { + fileName = path; + } + + path += 1; + } + + // At this point the file name is sitting on a slash, so just move forward. + while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { + fileName += 1; + } + + return fileName; +} + +const char* mal_path_extension(const char* path) +{ + if (path == NULL) { + path = ""; + } + + const char* extension = mal_path_file_name(path); + const char* lastOccurance = NULL; + + // Just find the last '.' and return. + while (extension[0] != '\0') { + if (extension[0] == '.') { + extension += 1; + lastOccurance = extension; + } + + extension += 1; + } + + return (lastOccurance != NULL) ? lastOccurance : extension; +} + +mal_bool32 mal_path_extension_equal(const char* path, const char* extension) +{ + if (path == NULL || extension == NULL) { + return MAL_FALSE; + } + + const char* ext1 = extension; + const char* ext2 = mal_path_extension(path); + +#if defined(_MSC_VER) || defined(__DMC__) + return _stricmp(ext1, ext2) == 0; +#else + return strcasecmp(ext1, ext2) == 0; +#endif +} + +size_t mal_decoder__on_read_stdio(mal_decoder* pDecoder, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pDecoder->pUserData); +} + +mal_bool32 mal_decoder__on_seek_stdio(mal_decoder* pDecoder, int byteOffset, mal_seek_origin origin) +{ + return fseek((FILE*)pDecoder->pUserData, byteOffset, (origin == mal_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +mal_result mal_decoder__preinit_file(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + if (pDecoder == NULL) { + return MAL_INVALID_ARGS; + } + + mal_zero_object(pDecoder); + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MAL_INVALID_ARGS; + } + + FILE* pFile; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (fopen_s(&pFile, pFilePath, "rb") != 0) { + return MAL_ERROR; + } +#else + pFile = fopen(pFilePath, "rb"); + if (pFile == NULL) { + return MAL_ERROR; + } +#endif + + // We need to manually set the user data so the calls to mal_decoder__on_seek_stdio() succeed. + pDecoder->pUserData = pFile; + + (void)pConfig; + return MAL_SUCCESS; +} + +mal_result mal_decoder_init_file(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_result result = mal_decoder__preinit_file(pFilePath, pConfig, pDecoder); // This sets pDecoder->pUserData to a FILE*. + if (result != MAL_SUCCESS) { + return result; + } + + // WAV + if (mal_path_extension_equal(pFilePath, "wav")) { + result = mal_decoder_init_wav(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); + if (result == MAL_SUCCESS) { + return MAL_SUCCESS; + } + + mal_decoder__on_seek_stdio(pDecoder, 0, mal_seek_origin_start); + } + + // FLAC + if (mal_path_extension_equal(pFilePath, "flac")) { + result = mal_decoder_init_flac(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); + if (result == MAL_SUCCESS) { + return MAL_SUCCESS; + } + + mal_decoder__on_seek_stdio(pDecoder, 0, mal_seek_origin_start); + } + + // MP3 + if (mal_path_extension_equal(pFilePath, "mp3")) { + result = mal_decoder_init_mp3(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); + if (result == MAL_SUCCESS) { + return MAL_SUCCESS; + } + + mal_decoder__on_seek_stdio(pDecoder, 0, mal_seek_origin_start); + } + + // Trial and error. + return mal_decoder_init(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} + +mal_result mal_decoder_init_file_wav(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_result result = mal_decoder__preinit_file(pFilePath, pConfig, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder_init_wav(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} + +mal_result mal_decoder_init_file_flac(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_result result = mal_decoder__preinit_file(pFilePath, pConfig, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder_init_flac(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} + +mal_result mal_decoder_init_file_vorbis(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_result result = mal_decoder__preinit_file(pFilePath, pConfig, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder_init_vorbis(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} + +mal_result mal_decoder_init_file_mp3(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder) +{ + mal_result result = mal_decoder__preinit_file(pFilePath, pConfig, pDecoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder_init_mp3(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder); +} +#endif + +mal_result mal_decoder_uninit(mal_decoder* pDecoder) +{ + if (pDecoder == NULL) { + return MAL_INVALID_ARGS; + } + + if (pDecoder->onUninit) { + pDecoder->onUninit(pDecoder); + } + +#ifndef MAL_NO_STDIO + // If we have a file handle, close it. + if (pDecoder->onRead == mal_decoder__on_read_stdio) { + fclose((FILE*)pDecoder->pUserData); + } +#endif + + return MAL_SUCCESS; +} + +mal_uint64 mal_decoder_read(mal_decoder* pDecoder, mal_uint64 frameCount, void* pFramesOut) +{ + if (pDecoder == NULL) return 0; + + return mal_dsp_read(&pDecoder->dsp, frameCount, pFramesOut, pDecoder->dsp.pUserData); +} + +mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameIndex) +{ + if (pDecoder == NULL) return 0; + + if (pDecoder->onSeekToFrame) { + return pDecoder->onSeekToFrame(pDecoder, frameIndex); + } + + // Should never get here, but if we do it means onSeekToFrame was not set by the backend. + return MAL_INVALID_ARGS; +} + + +mal_result mal_decoder__full_decode_and_uninit(mal_decoder* pDecoder, mal_decoder_config* pConfigOut, mal_uint64* pFrameCountOut, void** ppDataOut) +{ + mal_assert(pDecoder != NULL); + + mal_uint64 totalFrameCount = 0; + mal_uint64 bpf = mal_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); + + // The frame count is unknown until we try reading. Thus, we just run in a loop. + mal_uint64 dataCapInFrames = 0; + void* pDataOut = NULL; + for (;;) { + // Make room if there's not enough. + if (totalFrameCount == dataCapInFrames) { + mal_uint64 newDataCapInFrames = dataCapInFrames*2; + if (newDataCapInFrames == 0) { + newDataCapInFrames = 4096; + } + + if ((newDataCapInFrames * bpf) > MAL_SIZE_MAX) { + mal_free(pDataOut); + return MAL_TOO_LARGE; + } + + + void* pNewDataOut = (void*)mal_realloc(pDataOut, (size_t)(newDataCapInFrames * bpf)); + if (pNewDataOut == NULL) { + mal_free(pDataOut); + return MAL_OUT_OF_MEMORY; + } + + dataCapInFrames = newDataCapInFrames; + pDataOut = pNewDataOut; + } + + mal_uint64 frameCountToTryReading = dataCapInFrames - totalFrameCount; + mal_assert(frameCountToTryReading > 0); + + mal_uint64 framesJustRead = mal_decoder_read(pDecoder, frameCountToTryReading, (mal_uint8*)pDataOut + (totalFrameCount * bpf)); + totalFrameCount += framesJustRead; + + if (framesJustRead < frameCountToTryReading) { + break; + } + } + + + if (pConfigOut != NULL) { + pConfigOut->format = pDecoder->outputFormat; + pConfigOut->channels = pDecoder->outputChannels; + pConfigOut->sampleRate = pDecoder->outputSampleRate; + mal_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels); + } + + if (ppDataOut != NULL) { + *ppDataOut = pDataOut; + } else { + mal_free(pDataOut); + } + + if (pFrameCountOut != NULL) { + *pFrameCountOut = totalFrameCount; + } + + mal_decoder_uninit(pDecoder); + return MAL_SUCCESS; +} + +#ifndef MAL_NO_STDIO +mal_result mal_decode_file(const char* pFilePath, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut) +{ + if (pFrameCountOut != NULL) { + *pFrameCountOut = 0; + } + if (ppDataOut != NULL) { + *ppDataOut = NULL; + } + + if (pFilePath == NULL) { + return MAL_INVALID_ARGS; + } + + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); + + mal_decoder decoder; + mal_result result = mal_decoder_init_file(pFilePath, &config, &decoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppDataOut); +} +#endif + +mal_result mal_decode_memory(const void* pData, size_t dataSize, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut) +{ + if (pFrameCountOut != NULL) { + *pFrameCountOut = 0; + } + if (ppDataOut != NULL) { + *ppDataOut = NULL; + } + + if (pData == NULL || dataSize == 0) { + return MAL_INVALID_ARGS; + } + + mal_decoder_config config = mal_decoder_config_init_copy(pConfig); + + mal_decoder decoder; + mal_result result = mal_decoder_init_memory(pData, dataSize, &config, &decoder); + if (result != MAL_SUCCESS) { + return result; + } + + return mal_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppDataOut); +} + +#endif // MAL_NO_DECODING + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GENERATION +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +mal_result mal_sine_wave_init(double amplitude, double periodsPerSecond, mal_uint32 sampleRate, mal_sine_wave* pSineWave) +{ + if (pSineWave == NULL) { + return MAL_INVALID_ARGS; + } + mal_zero_object(pSineWave); + + if (amplitude == 0 || periodsPerSecond == 0) { + return MAL_INVALID_ARGS; + } + + if (amplitude > 1) { + amplitude = 1; + } + if (amplitude < -1) { + amplitude = -1; + } + + pSineWave->amplitude = amplitude; + pSineWave->periodsPerSecond = periodsPerSecond; + pSineWave->delta = MAL_TAU_D / sampleRate; + pSineWave->time = 0; + + return MAL_SUCCESS; +} + +mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float* pSamples) +{ + return mal_sine_wave_read_ex(pSineWave, count, 1, mal_stream_layout_interleaved, &pSamples); +} + +mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount, mal_uint32 channels, mal_stream_layout layout, float** ppFrames) +{ + if (pSineWave == NULL) { + return 0; + } + + if (ppFrames != NULL) { + for (mal_uint64 iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = (float)(sin(pSineWave->time * pSineWave->periodsPerSecond) * pSineWave->amplitude); + pSineWave->time += pSineWave->delta; + + if (layout == mal_stream_layout_interleaved) { + for (mal_uint32 iChannel = 0; iChannel < channels; iChannel += 1) { + ppFrames[0][iFrame*channels + iChannel] = s; + } + } else { + for (mal_uint32 iChannel = 0; iChannel < channels; iChannel += 1) { + ppFrames[iChannel][iFrame] = s; + } + } + } + } else { + pSineWave->time += pSineWave->delta * frameCount; + } + + return frameCount; +} + + +#endif // MINI_AL_IMPLEMENTATION + // REVISION HISTORY // ================ // +// v0.8.8 - 2018-09-14 +// - Fix Linux build with the ALSA backend. +// - Minor documentation fix. +// +// v0.8.7 - 2018-09-12 +// - Fix a bug with UWP detection. +// +// v0.8.6 - 2018-08-26 +// - Automatically switch the internal device when the default device is unplugged. Note that this is still in the +// early stages and not all backends handle this the same way. As of this version, this will not detect a default +// device switch when changed from the operating system's audio preferences (unless the backend itself handles +// this automatically). This is not supported in exclusive mode. +// - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the +// user switches the default device via the operating system's audio preferences, mini_al will automatically switch +// the internal device to the new default. This is not supported in exclusive mode. +// - WASAPI: Add support for hardware offloading via IAudioClient2. Only supported on Windows 8 and newer. +// - WASAPI: Add support for low-latency shared mode via IAudioClient3. Only supported on Windows 10 and newer. +// - Add support for compiling the UWP build as C. +// - mal_device_set_recv_callback() and mal_device_set_send_callback() have been deprecated. You must now set this +// when the device is initialized with mal_device_init*(). These will be removed in version 0.9.0. +// +// v0.8.5 - 2018-08-12 +// - Add support for specifying the size of a device's buffer in milliseconds. You can still set the buffer size in +// frames if that suits you. When bufferSizeInFrames is 0, bufferSizeInMilliseconds will be used. If both are non-0 +// then bufferSizeInFrames will take priority. If both are set to 0 the default buffer size is used. +// - Add support for the audio(4) backend to OpenBSD. +// - Fix a bug with the ALSA backend that was causing problems on Raspberry Pi. This significantly improves the +// Raspberry Pi experience. +// - Fix a bug where an incorrect number of samples is returned from sinc resampling. +// - Add support for setting the value to be passed to internal calls to CoInitializeEx(). +// - WASAPI and WinMM: Stop the device when it is unplugged. +// +// v0.8.4 - 2018-08-06 +// - Add sndio backend for OpenBSD. +// - Add audio(4) backend for NetBSD. +// - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD. +// - Formats are now native-endian (were previously little-endian). +// - Mark some APIs as deprecated: +// - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate(). +// - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate(). +// - Fix a bug when capturing using the WASAPI backend. +// - Fix some aliasing issues with resampling, specifically when increasing the sample rate. +// - Fix warnings. +// +// v0.8.3 - 2018-07-15 +// - Fix a crackling bug when resampling in capture mode. +// - Core Audio: Fix a bug where capture does not work. +// - ALSA: Fix a bug where the worker thread can get stuck in an infinite loop. +// - PulseAudio: Fix a bug where mal_context_init() succeeds when PulseAudio is unusable. +// - JACK: Fix a bug where mal_context_init() succeeds when JACK is unusable. +// +// v0.8.2 - 2018-07-07 +// - Fix a bug on macOS with Core Audio where the internal callback is not called. +// +// v0.8.1 - 2018-07-06 +// - Fix compilation errors and warnings. +// +// v0.8 - 2018-07-05 +// - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old +// way is still supported for now, but you should update as it may be removed in the future. +// - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with +// mal_context_get_devices(). An additional low-level device enumration API has been introduced called +// mal_context_enumerate_devices() which uses a callback to report devices. +// - API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add +// mal_get_bytes_per_frame(). +// - API CHANGE: Replace mal_device_config.preferExclusiveMode with mal_device_config.shareMode. +// - This new config can be set to mal_share_mode_shared (default) or mal_share_mode_exclusive. +// - API CHANGE: Remove excludeNullDevice from mal_context_config.alsa. +// - API CHANGE: Rename MAL_MAX_SAMPLE_SIZE_IN_BYTES to MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES. +// - API CHANGE: Change the default channel mapping to the standard Microsoft mapping. +// - API CHANGE: Remove backend-specific result codes. +// - API CHANGE: Changes to the format conversion APIs (mal_pcm_f32_to_s16(), etc.) +// - Add support for Core Audio (Apple). +// - Add support for PulseAudio. +// - This is the highest priority backend on Linux (higher priority than ALSA) since it is commonly +// installed by default on many of the popular distros and offer's more seamless integration on +// platforms where PulseAudio is used. In addition, if PulseAudio is installed and running (which +// is extremely common), it's better to just use PulseAudio directly rather than going through the +// "pulse" ALSA plugin (which is what the "default" ALSA device is likely set to). +// - Add support for JACK. +// - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no +// longer required to build mini_al. +// - Remove dependency on dsound.h for the DirectSound backend. This fixes build issues with some +// distributions of MinGW. +// - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some +// distributions of MinGW. +// - Add support for dithering to format conversion. +// - Add support for configuring the priority of the worker thread. +// - Add a sine wave generator. +// - Improve efficiency of sample rate conversion. +// - Introduce the notion of standard channel maps. Use mal_get_standard_channel_map(). +// - Introduce the notion of default device configurations. A default config uses the same configuration +// as the backend's internal device, and as such results in a pass-through data transmission pipeline. +// - Add support for passing in NULL for the device config in mal_device_init(), which uses a default +// config. This requires manually calling mal_device_set_send/recv_callback(). +// - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.) +// - Make mal_device_init_ex() more robust. +// - Make some APIs more const-correct. +// - Fix errors with SDL detection on Apple platforms. +// - Fix errors with OpenAL detection. +// - Fix some memory leaks. +// - Fix a bug with opening decoders from memory. +// - Early work on SSE2, AVX2 and NEON optimizations. +// - Miscellaneous bug fixes. +// - Documentation updates. +// +// v0.7 - 2018-02-25 +// - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts. +// - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files. +// - Allow opening of devices without a context. +// - In this case the context is created and managed internally by the device. +// - Change the default channel mapping to the same as that used by FLAC. +// - Fix build errors with macOS. +// +// v0.6c - 2018-02-12 +// - Fix build errors with BSD/OSS. +// // v0.6b - 2018-02-03 // - Fix some warnings when compiling with Visual C++. // diff --git a/raylib/external/stb_vorbis.c b/raylib/external/stb_vorbis.c index c847242..4b72504 100644 --- a/raylib/external/stb_vorbis.c +++ b/raylib/external/stb_vorbis.c @@ -193,7 +193,7 @@ #undef __forceinline #endif #define __forceinline - //#define alloca __builtin_alloca + #define alloca __builtin_alloca #elif !defined(_MSC_VER) #if __GNUC__ #define __forceinline inline diff --git a/raylib/gestures.go b/raylib/gestures.go index efe34ba..84349a9 100644 --- a/raylib/gestures.go +++ b/raylib/gestures.go @@ -1,4 +1,4 @@ -package raylib +package rl /* #include "raylib.h" diff --git a/raylib/models.c b/raylib/models.c index 3879174..8a49813 100644 --- a/raylib/models.c +++ b/raylib/models.c @@ -5,10 +5,10 @@ * CONFIGURATION: * * #define SUPPORT_FILEFORMAT_OBJ -* Selected desired fileformats to be supported for loading. -* * #define SUPPORT_FILEFORMAT_MTL -* Selected desired fileformats to be supported for loading. +* #define SUPPORT_FILEFORMAT_IQM +* #define SUPPORT_FILEFORMAT_GLTF +* Selected desired fileformats to be supported for model data loading. * * #define SUPPORT_MESH_GENERATION * Support procedural mesh generation functions, uses external par_shapes.h library @@ -36,9 +36,8 @@ * **********************************************************************************************/ -#include "config.h" - -#include "raylib.h" +#include "config.h" // Defines module configuration flags +#include "raylib.h" // Declares module functions #include "utils.h" // Required for: fopen() Android mapping @@ -49,8 +48,20 @@ #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -#define PAR_SHAPES_IMPLEMENTATION -#include "external/par_shapes.h" // Shapes 3d parametric generation +#if defined(SUPPORT_FILEFORMAT_IQM) + #define RIQM_IMPLEMENTATION + #include "external/riqm.h" // IQM file format loading +#endif + +#if defined(SUPPORT_FILEFORMAT_GLTF) + #define CGLTF_IMPLEMENTATION + #include "external/cgltf.h" // glTF file format loading +#endif + +#if defined(SUPPORT_MESH_GENERATION) + #define PAR_SHAPES_IMPLEMENTATION + #include "external/par_shapes.h" // Shapes 3d parametric generation +#endif //---------------------------------------------------------------------------------- // Defines and Macros @@ -76,6 +87,12 @@ static Mesh LoadOBJ(const char *fileName); // Load OBJ mesh data #if defined(SUPPORT_FILEFORMAT_MTL) static Material LoadMTL(const char *fileName); // Load MTL material data #endif +#if defined(SUPPORT_FILEFORMAT_GLTF) +static Mesh LoadIQM(const char *fileName); // Load IQM mesh data +#endif +#if defined(SUPPORT_FILEFORMAT_GLTF) +static Mesh LoadGLTF(const char *fileName); // Load GLTF mesh data +#endif //---------------------------------------------------------------------------------- // Module Functions Definition @@ -647,45 +664,54 @@ void UnloadMesh(Mesh *mesh) rlUnloadMesh(mesh); } -// Export mesh as an OBJ file -void ExportMesh(const char *fileName, Mesh mesh) +// Export mesh data to file +void ExportMesh(Mesh mesh, const char *fileName) { - FILE *objFile = fopen(fileName, "wt"); + bool success = false; - fprintf(objFile, "# raylib Mesh OBJ exporter v1.0\n\n"); - fprintf(objFile, "# Mesh exported as triangle faces and not optimized.\n"); - fprintf(objFile, "# Vertex Count: %i\n", mesh.vertexCount); - fprintf(objFile, "# Triangle Count: %i\n\n", mesh.triangleCount); - fprintf(objFile, "# LICENSE: zlib/libpng\n"); - fprintf(objFile, "# Copyright (c) 2018 Ramon Santamaria (@raysan5)\n\n"); - - fprintf(objFile, "g mesh\n"); - - for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) + if (IsFileExtension(fileName, ".obj")) { - fprintf(objFile, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]); + FILE *objFile = fopen(fileName, "wt"); + + fprintf(objFile, "# raylib Mesh OBJ exporter v1.0\n\n"); + fprintf(objFile, "# Mesh exported as triangle faces and not optimized.\n"); + fprintf(objFile, "# Vertex Count: %i\n", mesh.vertexCount); + fprintf(objFile, "# Triangle Count: %i\n\n", mesh.triangleCount); + fprintf(objFile, "# LICENSE: zlib/libpng\n"); + fprintf(objFile, "# Copyright (c) 2018 Ramon Santamaria (@raysan5)\n\n"); + + fprintf(objFile, "g mesh\n"); + + for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) + { + fprintf(objFile, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]); + } + + for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2) + { + fprintf(objFile, "vt %.2f %.2f\n", mesh.texcoords[v], mesh.texcoords[v + 1]); + } + + for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) + { + fprintf(objFile, "vn %.2f %.2f %.2f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]); + } + + for (int i = 0; i < mesh.triangleCount; i += 3) + { + fprintf(objFile, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i, i, i, i + 1, i + 1, i + 1, i + 2, i + 2, i + 2); + } + + fprintf(objFile, "\n"); + + fclose(objFile); + + success = true; } - - for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2) - { - fprintf(objFile, "vt %.2f %.2f\n", mesh.texcoords[v], mesh.texcoords[v + 1]); - } - - for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) - { - fprintf(objFile, "vn %.2f %.2f %.2f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]); - } - - for (int i = 0; i < mesh.triangleCount; i += 3) - { - fprintf(objFile, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i, i, i, i + 1, i + 1, i + 1, i + 2, i + 2, i + 2); - } - - fprintf(objFile, "\n"); - - fclose(objFile); + else if (IsFileExtension(fileName, ".raw")) { } // TODO: Support additional file formats to export mesh vertex data - TraceLog(LOG_INFO, "Mesh saved: %s", fileName); + if (success) TraceLog(LOG_INFO, "Mesh exported successfully: %s", fileName); + else TraceLog(LOG_WARNING, "Mesh could not be exported."); } #if defined(SUPPORT_MESH_GENERATION) @@ -700,7 +726,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) resZ++; // Vertices definition - int vertexCount = resX*resZ*6; // 6 vertex by quad + int vertexCount = resX*resZ; // vertices get reused for the faces Vector3 *vertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); for (int z = 0; z < resZ; z++) @@ -719,7 +745,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) Vector3 *normals = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up; - // TexCoords definition + // TexCoords definition Vector2 *texcoords = (Vector2 *)malloc(vertexCount*sizeof(Vector2)); for (int v = 0; v < resZ; v++) { @@ -742,7 +768,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) triangles[t++] = i + 1; triangles[t++] = i; - triangles[t++] = i + resX; + triangles[t++] = i + resX; triangles[t++] = i + resX + 1; triangles[t++] = i + 1; } @@ -1772,7 +1798,7 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float // Draw a billboard void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint) { - Rectangle sourceRec = { 0, 0, texture.width, texture.height }; + Rectangle sourceRec = { 0.0f, 0.0f, (float)texture.width, (float)texture.height }; DrawBillboardRec(camera, texture, sourceRec, center, size, tint); } @@ -1838,9 +1864,9 @@ void DrawBoundingBox(BoundingBox box, Color color) { Vector3 size; - size.x = fabsf(box.max.x - box.min.x); - size.y = fabsf(box.max.y - box.min.y); - size.z = fabsf(box.max.z - box.min.z); + size.x = (float)fabs(box.max.x - box.min.x); + size.y = (float)fabs(box.max.y - box.min.y); + size.z = (float)fabs(box.max.z - box.min.z); Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f }; @@ -2075,7 +2101,7 @@ RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) RayHitInfo result = { 0 }; - if (fabsf(ray.direction.y) > EPSILON) + if (fabs(ray.direction.y) > EPSILON) { float distance = (ray.position.y - groundHeight)/-ray.direction.y; @@ -2207,9 +2233,8 @@ void MeshBinormals(Mesh *mesh) Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] }; float tangentW = mesh->tangents[i*4 + 3]; - Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW); - // TODO: Register computed binormal in mesh->binormal ? + // Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW); } } @@ -2223,7 +2248,7 @@ static Mesh LoadOBJ(const char *fileName) { Mesh mesh = { 0 }; - char dataType; + char dataType = 0; char comments[200]; int vertexCount = 0; @@ -2246,7 +2271,7 @@ static Mesh LoadOBJ(const char *fileName) // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face) while (!feof(objFile)) { - dataType = '\0'; + dataType = 0; fscanf(objFile, "%c", &dataType); switch (dataType) @@ -2632,3 +2657,59 @@ static Material LoadMTL(const char *fileName) return material; } #endif + +#if defined(SUPPORT_FILEFORMAT_GLTF) +// Load IQM mesh data +static Mesh LoadIQM(const char *fileName) +{ + Mesh mesh = { 0 }; + + // TODO: Load IQM file + + return mesh; +} +#endif + +#if defined(SUPPORT_FILEFORMAT_GLTF) +// Load GLTF mesh data +static Mesh LoadGLTF(const char *fileName) +{ + Mesh mesh = { 0 }; + + // GLTF file loading + FILE *gltfFile = fopen(fileName, "rb"); + + if (gltfFile == NULL) + { + TraceLog(LOG_WARNING, "[%s] GLTF file could not be opened", fileName); + return mesh; + } + + fseek(gltfFile, 0, SEEK_END); + int size = ftell(gltfFile); + fseek(gltfFile, 0, SEEK_SET); + + void *buffer = malloc(size); + fread(buffer, size, 1, gltfFile); + + fclose(gltfFile); + + // GLTF data loading + cgltf_options options = {0}; + cgltf_data data; + cgltf_result result = cgltf_parse(&options, buffer, size, &data); + + if (result == cgltf_result_success) + { + printf("Type: %u\n", data.file_type); + printf("Version: %d\n", data.version); + printf("Meshes: %lu\n", data.meshes_count); + } + else TraceLog(LOG_WARNING, "[%s] GLTF data could not be loaded", fileName); + + free(buffer); + cgltf_free(&data); + + return mesh; +} +#endif diff --git a/raylib/models.go b/raylib/models.go index 7958e22..0a74f27 100644 --- a/raylib/models.go +++ b/raylib/models.go @@ -1,4 +1,4 @@ -package raylib +package rl /* #include "raylib.h" @@ -201,11 +201,11 @@ func UnloadMesh(mesh *Mesh) { } // ExportMesh - Export mesh as an OBJ file -func ExportMesh(fileName string, mesh Mesh) { +func ExportMesh(mesh Mesh, fileName string) { cfileName := C.CString(fileName) defer C.free(unsafe.Pointer(cfileName)) cmesh := mesh.cptr() - C.ExportMesh(cfileName, *cmesh) + C.ExportMesh(*cmesh, cfileName) } // GenMeshPlane - Generate plane mesh (with subdivisions) diff --git a/raylib/platform_android.go b/raylib/platform_android.go index c8a5e54..f976096 100644 --- a/raylib/platform_android.go +++ b/raylib/platform_android.go @@ -1,6 +1,6 @@ // +build android -package raylib +package rl /* #include "raylib.h" diff --git a/raylib/platform_arm.go b/raylib/platform_arm.go index b335fdf..c867bfb 100644 --- a/raylib/platform_arm.go +++ b/raylib/platform_arm.go @@ -1,6 +1,6 @@ // +build !android,arm -package raylib +package rl /* #include "raylib.h" diff --git a/raylib/platform_desktop.go b/raylib/platform_desktop.go index 819ea51..5f334c4 100644 --- a/raylib/platform_desktop.go +++ b/raylib/platform_desktop.go @@ -1,6 +1,6 @@ // +build !android,!arm -package raylib +package rl /* #include "raylib.h" diff --git a/raylib/raylib.go b/raylib/raylib.go index b75c77b..7475517 100644 --- a/raylib/raylib.go +++ b/raylib/raylib.go @@ -33,7 +33,7 @@ Example: */ -package raylib +package rl import ( "image" @@ -174,8 +174,8 @@ const ( FlagFullscreenMode = 2 // Set to allow resizable window FlagWindowResizable = 4 - // Set to show window decoration (frame and buttons) - FlagWindowDecorated = 8 + // Set to disable window decoration (frame and buttons) + FlagWindowUndecorated = 8 // Set to allow transparent window FlagWindowTransparent = 16 // Set to try enabling MSAA 4X @@ -219,35 +219,35 @@ const ( KeyLeftShift = 340 KeyLeftControl = 341 KeyLeftAlt = 342 - KeyLeftSuper = 343 + KeyLeftSuper = 347 KeyRightShift = 344 KeyRightControl = 345 KeyRightAlt = 346 KeyRightSuper = 347 - KeyKBMenu = 348 + KeyKbMenu = 348 KeyLeftBracket = 91 KeyBackSlash = 92 KeyRightBracket = 93 KeyGrave = 96 // Keyboard Number Pad Keys - KeyKPZero = 320 - KeyKPOne = 321 - KeyKPTwo = 322 - KeyKPThree = 323 - KeyKPFour = 324 - KeyKPFive = 325 - KeyKPSix = 326 - KeyKPSeven = 327 - KeyKPEight = 328 - KeyKPNine = 329 - KeyKPDecimal = 330 - KeyKPDivide = 331 - KeyKPMultiply = 332 - KeyKPSubtract = 333 - KeyKPAdd = 334 - KeyKPEnter = 335 - KeyKPEqual = 336 + KeyKp0 = 320 + KeyKp1 = 321 + KeyKp2 = 322 + KeyKp3 = 323 + KeyKp4 = 324 + KeyKp5 = 325 + KeyKp6 = 326 + KeyKp7 = 327 + KeyKp8 = 328 + KeyKp9 = 329 + KeyKpDecimal = 330 + KeyKpDivide = 331 + KeyKpMultiply = 332 + KeyKpSubtract = 333 + KeyKpAdd = 334 + KeyKpEnter = 335 + KeyKpEqual = 336 // Keyboard Alpha Numeric Keys KeyApostrophe = 39 @@ -358,6 +358,24 @@ const ( GamepadXboxButtonLeft = 13 GamepadXboxButtonHome = 8 + // Android Gamepad Controller (SNES CLASSIC) + GamepadAndroidDpadUp = 19 + GamepadAndroidDpadDown = 20 + GamepadAndroidDpadLeft = 21 + GamepadAndroidDpadRight = 22 + GamepadAndroidDpadCenter = 23 + + GamepadAndroidButtonA = 96 + GamepadAndroidButtonB = 97 + GamepadAndroidButtonC = 98 + GamepadAndroidButtonX = 99 + GamepadAndroidButtonY = 100 + GamepadAndroidButtonZ = 101 + GamepadAndroidButtonL1 = 102 + GamepadAndroidButtonR1 = 103 + GamepadAndroidButtonL2 = 104 + GamepadAndroidButtonR2 = 105 + // Xbox360 USB Controller Axis // [-1..1] (left->right) GamepadXboxAxisLeftX = 0 @@ -871,25 +889,25 @@ const ( // VrDeviceInfo - Head-Mounted-Display device parameters type VrDeviceInfo struct { // HMD horizontal resolution in pixels - HResolution int + hResolution int // HMD vertical resolution in pixels - VResolution int + vResolution int // HMD horizontal size in meters - HScreenSize float32 + hScreenSize float32 // HMD vertical size in meters - VScreenSize float32 + vScreenSize float32 // HMD screen center in meters - VScreenCenter float32 + vScreenCenter float32 // HMD distance between eye and display in meters - EyeToScreenDistance float32 + eyeToScreenDistance float32 // HMD lens separation distance in meters - LensSeparationDistance float32 + lensSeparationDistance float32 // HMD IPD (distance between pupils) in meters - InterpupillaryDistance float32 + interpupillaryDistance float32 // HMD lens distortion constant parameters - LensDistortionValues [4]float32 + lensDistortionValues [4]float32 // HMD chromatic aberration correction parameters - ChromaAbCorrection [4]float32 + chromaAbCorrection [4]float32 } // NewVrDeviceInfo - Returns new VrDeviceInfo diff --git a/raylib/raylib.h b/raylib/raylib.h index f1caef4..ef53408 100644 --- a/raylib/raylib.h +++ b/raylib/raylib.h @@ -1,8 +1,10 @@ /********************************************************************************************** * -* raylib - A simple and easy-to-use library to learn videogames programming (www.raylib.com) +* raylib - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * * FEATURES: +* - NO external dependencies, all required libraries included with raylib +* - Multiple platforms support: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, MacOS, UWP, Android, Raspberry Pi, HTML5. * - Written in plain C code (C99) in PascalCase/camelCase notation * - Hardware accelerated with OpenGL (1.1, 2.1, 3.3 or ES2 - choose at compile) * - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] @@ -12,10 +14,8 @@ * - Flexible Materials system, supporting classic maps and PBR maps * - Shaders support, including Model shaders and Postprocessing shaders * - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] -* - Audio loading and playing with streaming support (WAV, OGG, FLAC, XM, MOD) -* - Multiple platforms support: Windows, Linux, FreeBSD, MacOS, UWP, Android, Raspberry Pi, HTML5. +* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) * - VR stereo rendering with configurable HMD device parameters -* - NO external dependencies, all required libraries included with raylib * - Complete bindings to LUA (raylib-lua) and Go (raylib-go) * * NOTES: @@ -33,14 +33,15 @@ * stb_image_resize (Sean Barret) for image resizing algorythms [textures] * stb_image_write (Sean Barret) for image writting (PNG) [utils] * stb_truetype (Sean Barret) for ttf fonts loading [text] +* stb_rect_pack (Sean Barret) for rectangles packing [text] * stb_vorbis (Sean Barret) for OGG audio loading [audio] * stb_perlin (Sean Barret) for Perlin noise image generation [textures] * par_shapes (Philip Rideout) for parametric 3d shapes generation [models] * jar_xm (Joshua Reisenauer) for XM audio module loading [audio] * jar_mod (Joshua Reisenauer) for MOD audio module loading [audio] * dr_flac (David Reid) for FLAC audio file loading [audio] +* dr_mp3 (David Reid) for MP3 audio file loading [audio] * rgif (Charlie Tangora, Ramon Santamaria) for GIF recording [core] -* tinfl for data decompression (DEFLATE algorithm) [rres] * * * LICENSE: zlib/libpng @@ -70,6 +71,8 @@ #ifndef RAYLIB_H #define RAYLIB_H +#include // Required for: va_list - Only used by TraceLogCallback + #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) #define RLAPI __declspec(dllexport) // We are building raylib as a Win32 shared library (.dll) #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) @@ -92,7 +95,7 @@ #define FLAG_SHOW_LOGO 1 // Set to show raylib logo at startup #define FLAG_FULLSCREEN_MODE 2 // Set to run program in fullscreen #define FLAG_WINDOW_RESIZABLE 4 // Set to allow resizable window -#define FLAG_WINDOW_DECORATED 8 // Set to show window decoration (frame and buttons) +#define FLAG_WINDOW_UNDECORATED 8 // Set to disable window decoration (frame and buttons) #define FLAG_WINDOW_TRANSPARENT 16 // Set to allow transparent window #define FLAG_MSAA_4X_HINT 32 // Set to try enabling MSAA 4X #define FLAG_VSYNC_HINT 64 // Set to try enabling V-Sync on GPU @@ -270,6 +273,24 @@ #define GAMEPAD_XBOX_BUTTON_LEFT 13 #define GAMEPAD_XBOX_BUTTON_HOME 8 +// Android Gamepad Controller (SNES CLASSIC) +#define GAMEPAD_ANDROID_DPAD_UP 19 +#define GAMEPAD_ANDROID_DPAD_DOWN 20 +#define GAMEPAD_ANDROID_DPAD_LEFT 21 +#define GAMEPAD_ANDROID_DPAD_RIGHT 22 +#define GAMEPAD_ANDROID_DPAD_CENTER 23 + +#define GAMEPAD_ANDROID_BUTTON_A 96 +#define GAMEPAD_ANDROID_BUTTON_B 97 +#define GAMEPAD_ANDROID_BUTTON_C 98 +#define GAMEPAD_ANDROID_BUTTON_X 99 +#define GAMEPAD_ANDROID_BUTTON_Y 100 +#define GAMEPAD_ANDROID_BUTTON_Z 101 +#define GAMEPAD_ANDROID_BUTTON_L1 102 +#define GAMEPAD_ANDROID_BUTTON_R1 103 +#define GAMEPAD_ANDROID_BUTTON_L2 104 +#define GAMEPAD_ANDROID_BUTTON_R2 105 + // Xbox360 USB Controller Axis // NOTE: For Raspberry Pi, axis must be reconfigured #if defined(PLATFORM_RPI) @@ -333,11 +354,9 @@ //---------------------------------------------------------------------------------- // Structures Definition //---------------------------------------------------------------------------------- -#ifndef __cplusplus // Boolean type - #ifndef bool - typedef enum { false, true } bool; - #endif +#if !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; #endif // Vector2 type @@ -361,6 +380,9 @@ typedef struct Vector4 { float w; } Vector4; +// Quaternion type, same as Vector4 +typedef Vector4 Quaternion; + // Matrix type (OpenGL style 4x4 - right handed, column major) typedef struct Matrix { float m0, m4, m8, m12; @@ -405,6 +427,9 @@ typedef struct Texture2D { int format; // Data format (PixelFormat type) } Texture2D; +// Texture type, same as Texture2D +typedef Texture2D Texture; + // RenderTexture2D type, for texture rendering typedef struct RenderTexture2D { unsigned int id; // OpenGL Framebuffer Object (FBO) id @@ -412,6 +437,19 @@ typedef struct RenderTexture2D { Texture2D depth; // Depth buffer attachment texture } RenderTexture2D; +// RenderTexture type, same as RenderTexture2D +typedef RenderTexture2D RenderTexture; + +// N-Patch layout info +typedef struct NPatchInfo { + Rectangle sourceRec; // Region in the texture + int left; // left border offset + int top; // top border offset + int right; // right border offset + int bottom; // bottom border offset + int type; // layout of the n-patch: 3x3, 1x3 or 3x1 +} NPatchInfo; + // Font character info typedef struct CharInfo { int value; // Character value (Unicode) @@ -419,6 +457,7 @@ typedef struct CharInfo { int offsetX; // Character offset X when drawing int offsetY; // Character offset Y when drawing int advanceX; // Character advance position X + unsigned char *data; // Character pixel data (grayscale) } CharInfo; // Font type, includes texture and charSet array data @@ -429,7 +468,7 @@ typedef struct Font { CharInfo *chars; // Characters info data } Font; -#define SpriteFont Font // SpriteFont type fallback, defaults to Font +#define SpriteFont Font // SpriteFont type fallback, defaults to Font // Camera type, defines a camera position/orientation in 3d space typedef struct Camera3D { @@ -440,7 +479,7 @@ typedef struct Camera3D { int type; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; -#define Camera Camera3D // Camera type fallback, defaults to Camera3D +#define Camera Camera3D // Camera type fallback, defaults to Camera3D // Camera2D type, defines a 2d camera typedef struct Camera2D { @@ -462,6 +501,7 @@ typedef struct Mesh { int vertexCount; // Number of vertices stored in arrays int triangleCount; // Number of triangles stored (indexed or not) + // Default vertex data float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) float *texcoords2; // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) @@ -469,9 +509,16 @@ typedef struct Mesh { float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) unsigned short *indices;// Vertex indices (in case vertex data comes indexed) + + // Animation vertex data + float *baseVertices; // Vertex base position (required to apply bones transformations) + float *baseNormals; // Vertex base normals (required to apply bones transformations) + float *weightBias; // Vertex weight bias + int *weightId; // Vertex weight id + // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id - unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (7 types of vertex data) + unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (default vertex data) } Mesh; // Shader type (generic) @@ -569,12 +616,12 @@ typedef struct VrDeviceInfo { // Enumerators Definition //---------------------------------------------------------------------------------- // Trace log type -typedef enum { +typedef enum { LOG_INFO = 1, - LOG_WARNING = 2, - LOG_ERROR = 4, - LOG_DEBUG = 8, - LOG_OTHER = 16 + LOG_WARNING = 2, + LOG_ERROR = 4, + LOG_DEBUG = 8, + LOG_OTHER = 16 } LogType; // Shader location point type @@ -666,16 +713,23 @@ typedef enum { } TextureFilterMode; // Texture parameters: wrap mode -typedef enum { - WRAP_REPEAT = 0, - WRAP_CLAMP, - WRAP_MIRROR +typedef enum { + WRAP_REPEAT = 0, + WRAP_CLAMP, + WRAP_MIRROR } TextureWrapMode; +// Font type, defines generation method +typedef enum { + FONT_DEFAULT = 0, // Default font generation, anti-aliased + FONT_BITMAP, // Bitmap font generation, no anti-aliasing + FONT_SDF // SDF font generation, requires external shader +} FontType; + // Color blending modes (pre-defined) -typedef enum { - BLEND_ALPHA = 0, - BLEND_ADDITIVE, +typedef enum { + BLEND_ALPHA = 0, + BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode; @@ -720,6 +774,16 @@ typedef enum { HMD_SONY_PSVR } VrDeviceType; +// Type of n-patch +typedef enum { + NPT_9PATCH = 0, // 3x3 + NPT_3PATCH_VERTICAL, // 1x3 + NPT_3PATCH_HORIZONTAL // 3x1 +} NPatchType; + +// Callbacks to be implemented by users +typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args); + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -748,6 +812,13 @@ RLAPI void SetWindowMinSize(int width, int height); // Set window RLAPI void SetWindowSize(int width, int height); // Set window dimensions RLAPI int GetScreenWidth(void); // Get current screen width RLAPI int GetScreenHeight(void); // Get current screen height +RLAPI void *GetWindowHandle(void); // Get native window handle +RLAPI int GetMonitorCount(void); // Get number of connected monitors +RLAPI int GetMonitorWidth(int monitor); // Get primary monitor width +RLAPI int GetMonitorHeight(int monitor); // Get primary monitor height +RLAPI int GetMonitorPhysicalWidth(int monitor); // Get primary monitor physical width in millimetres +RLAPI int GetMonitorPhysicalHeight(int monitor); // Get primary monitor physical height in millimetres +RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor // Cursor-related functions RLAPI void ShowCursor(void); // Shows cursor @@ -772,7 +843,7 @@ RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Returns a r RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position for a 3d world space position RLAPI Matrix GetCameraMatrix(Camera camera); // Returns camera transform matrix (view matrix) -// Timming-related functions +// timing-related functions RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) RLAPI int GetFPS(void); // Returns current FPS RLAPI float GetFrameTime(void); // Returns time in seconds for last frame drawn @@ -789,6 +860,7 @@ RLAPI Color Fade(Color color, float alpha); // Color fade- RLAPI void ShowLogo(void); // Activate raylib logo at startup (can be done with flags) RLAPI void SetConfigFlags(unsigned char flags); // Setup window configuration flags (view FLAGS) RLAPI void SetTraceLog(unsigned char types); // Enable trace log message types (bit flags based) +RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set a trace log callback to enable custom logging bypassing raylib's one RLAPI void TraceLog(int logType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (saved a .png) RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) @@ -799,10 +871,12 @@ RLAPI const char *GetExtension(const char *fileName); // Get pointer RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string RLAPI const char *GetDirectoryPath(const char *fileName); // Get full path for a given fileName (uses static string) RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) +RLAPI char **GetDirectoryFiles(const char *dirPath, int *count); // Get filenames in a directory path (memory should be freed) +RLAPI void ClearDirectoryFiles(void); // Clear directory files paths buffers (free memory) RLAPI bool ChangeDirectory(const char *dir); // Change working directory, returns true if success RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window -RLAPI char **GetDroppedFiles(int *count); // Get dropped files names -RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer +RLAPI char **GetDroppedFiles(int *count); // Get dropped files names (memory should be freed) +RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory) // Persistent storage management RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position) @@ -921,7 +995,7 @@ RLAPI Image LoadImage(const char *fileName); RLAPI Image LoadImageEx(Color *pixels, int width, int height); // Load image from Color array data (RGBA - 32bit) RLAPI Image LoadImagePro(void *data, int width, int height, int format); // Load image from raw data with parameters RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data -RLAPI void ExportImage(const char *fileName, Image image); // Export image as a PNG file +RLAPI void ExportImage(Image image, const char *fileName); // Export image data to file RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) @@ -929,6 +1003,7 @@ RLAPI void UnloadImage(Image image); RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) RLAPI Color *GetImageData(Image image); // Get pixel data from image as a Color struct array +RLAPI Vector4 *GetImageDataNormalized(Image image); // Get pixel data from image as Vector4 array (float normalized) RLAPI int GetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) RLAPI Image GetTextureData(Texture2D texture); // Get pixel data from GPU texture and return an Image RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data @@ -942,23 +1017,27 @@ RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle -RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize and image (bilinear filtering) -RLAPI void ImageResizeNN(Image *image,int newWidth,int newHeight); // Resize and image (Nearest-Neighbor scaling algorithm) +RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (bilinear filtering) +RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) +RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color color); // Resize canvas and fill with color RLAPI void ImageMipmaps(Image *image); // Generate all mipmap levels for a provided image RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) -RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) +RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image RLAPI void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color); // Draw rectangle within an image RLAPI void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color); // Draw text (default font) within an image (destination) RLAPI void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text, float fontSize, float spacing, Color color); // Draw text (custom sprite font) within an image (destination) RLAPI void ImageFlipVertical(Image *image); // Flip image vertically RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally +RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg +RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint RLAPI void ImageColorInvert(Image *image); // Modify image color: invert RLAPI void ImageColorGrayscale(Image *image); // Modify image color: grayscale RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100) RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255) +RLAPI void ImageColorReplace(Image *image, Color color, Color replace); // Modify image color: replace color // Image generation functions RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color @@ -981,29 +1060,31 @@ RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters RLAPI void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters - +RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely. //------------------------------------------------------------------------------------ // Font Loading and Text Drawing Functions (Module: text) //------------------------------------------------------------------------------------ // Font loading/unloading functions -RLAPI Font GetDefaultFont(void); // Get the default Font -RLAPI Font LoadFont(const char *fileName); // Load Font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load Font from file with extended parameters -RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM) +RLAPI Font GetFontDefault(void); // Get the default Font +RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) +RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load font from file with extended parameters +RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use +RLAPI Image GenImageFontAtlas(CharInfo *chars, int fontSize, int charsCount, int padding, int packMethod); // Generate image font atlas using chars info +RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM) // Text drawing functions -RLAPI void DrawFPS(int posX, int posY); // Shows current FPS -RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) -RLAPI void DrawTextEx(Font font, const char* text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using Font and additional parameters +RLAPI void DrawFPS(int posX, int posY); // Shows current FPS +RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) +RLAPI void DrawTextEx(Font font, const char* text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters // Text misc. functions -RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font -RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font -RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' -RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string -RLAPI int GetGlyphIndex(Font font, int character); // Returns index position for a unicode character on sprite font +RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font +RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font +RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' +RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string +RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) @@ -1039,11 +1120,11 @@ RLAPI void UnloadModel(Model model); // Mesh loading/unloading functions RLAPI Mesh LoadMesh(const char *fileName); // Load mesh from file RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) -RLAPI void ExportMesh(const char *fileName, Mesh mesh); // Export mesh as an OBJ file +RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file // Mesh manipulation functions RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits -RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents +RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals // Mesh generation functions @@ -1147,6 +1228,7 @@ RLAPI Sound LoadSoundFromWave(Wave wave); // Load so RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadSound(Sound sound); // Unload sound +RLAPI void ExportWave(Wave wave, const char *fileName); // Export wave data to file // Wave/Sound management functions RLAPI void PlaySound(Sound sound); // Play a sound @@ -1189,10 +1271,6 @@ RLAPI void StopAudioStream(AudioStream stream); // Stop au RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) -#if defined(PLATFORM_ANDROID) -RLAPI struct android_app *GetAndroidApp(void); -#endif - #ifdef __cplusplus } #endif diff --git a/raylib/raymath.h b/raylib/raymath.h index b004652..3311653 100644 --- a/raylib/raymath.h +++ b/raylib/raymath.h @@ -12,9 +12,9 @@ * #define RAYMATH_HEADER_ONLY * Define static inline functions code, so #include header suffices for use. * This may use up lots of memory. -* +* * #define RAYMATH_STANDALONE -* Avoid raylib.h header inclusion in this file. +* Avoid raylib.h header inclusion in this file. * Vector3 and Matrix data types are defined internally in raymath module. * * @@ -60,7 +60,13 @@ #endif #ifdef RAYMATH_IMPLEMENTATION - #define RMDEF extern inline // Provide external definition + #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) + #define RMDEF __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll). + #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) + #define RLAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) + #else + #define RMDEF extern inline // Provide external definition + #endif #elif defined RAYMATH_HEADER_ONLY #define RMDEF static inline // Functions may be inlined, no external out-of-line definition #else @@ -94,7 +100,7 @@ // Return float vector for Vector3 #ifndef Vector3ToFloat - #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) + #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) #endif //---------------------------------------------------------------------------------- @@ -114,6 +120,14 @@ float y; float z; } Vector3; + + // Quaternion type + typedef struct Quaternion { + float x; + float y; + float z; + float w; + } Quaternion; // Matrix type (OpenGL style 4x4 - right handed, column major) typedef struct Matrix { @@ -128,14 +142,6 @@ typedef struct float3 { float v[3]; } float3; typedef struct float16 { float v[16]; } float16; -// Quaternion type -typedef struct Quaternion { - float x; - float y; - float z; - float w; -} Quaternion; - #include // Required for: sinf(), cosf(), tan(), fabs() //---------------------------------------------------------------------------------- @@ -143,26 +149,32 @@ typedef struct Quaternion { //---------------------------------------------------------------------------------- // Clamp float value -RMDEF float Clamp(float value, float min, float max) +RMDEF float Clamp(float value, float min, float max) { const float res = value < min ? min : value; return res > max ? max : res; } +// Calculate linear interpolation between two vectors +RMDEF float Lerp(float start, float end, float amount) +{ + return start + amount*(end - start); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Vector2 math //---------------------------------------------------------------------------------- // Vector with components value 0.0f -RMDEF Vector2 Vector2Zero(void) -{ +RMDEF Vector2 Vector2Zero(void) +{ Vector2 result = { 0.0f, 0.0f }; return result; } // Vector with components value 1.0f -RMDEF Vector2 Vector2One(void) -{ +RMDEF Vector2 Vector2One(void) +{ Vector2 result = { 1.0f, 1.0f }; return result; } @@ -217,6 +229,13 @@ RMDEF Vector2 Vector2Scale(Vector2 v, float scale) return result; } +// Multiply vector by vector +RMDEF Vector2 Vector2MultiplyV(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x*v2.x, v1.y*v2.y }; + return result; +} + // Negate vector RMDEF Vector2 Vector2Negate(Vector2 v) { @@ -231,6 +250,13 @@ RMDEF Vector2 Vector2Divide(Vector2 v, float div) return result; } +// Divide vector by vector +RMDEF Vector2 Vector2DivideV(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x/v2.x, v1.y/v2.y }; + return result; +} + // Normalize provided vector RMDEF Vector2 Vector2Normalize(Vector2 v) { @@ -238,36 +264,47 @@ RMDEF Vector2 Vector2Normalize(Vector2 v) return result; } +// Calculate linear interpolation between two vectors +RMDEF Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount) +{ + Vector2 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + + return result; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Vector3 math //---------------------------------------------------------------------------------- // Vector with components value 0.0f -RMDEF Vector3 Vector3Zero(void) -{ +RMDEF Vector3 Vector3Zero(void) +{ Vector3 result = { 0.0f, 0.0f, 0.0f }; - return result; + return result; } // Vector with components value 1.0f -RMDEF Vector3 Vector3One(void) -{ +RMDEF Vector3 Vector3One(void) +{ Vector3 result = { 1.0f, 1.0f, 1.0f }; - return result; + return result; } // Add two vectors RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2) { Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; - return result; + return result; } // Substract two vectors RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) { Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; - return result; + return result; } // Multiply vector by scalar @@ -279,7 +316,7 @@ RMDEF Vector3 Vector3Multiply(Vector3 v, float scalar) // Multiply vector by vector RMDEF Vector3 Vector3MultiplyV(Vector3 v1, Vector3 v2) -{ +{ Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; return result; } @@ -296,17 +333,17 @@ RMDEF Vector3 Vector3Perpendicular(Vector3 v) { Vector3 result = { 0 }; - float min = fabsf(v.x); + float min = (float) fabs(v.x); Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; - if (fabsf(v.y) < min) + if (fabs(v.y) < min) { - min = fabsf(v.y); + min = (float) fabs(v.y); Vector3 tmp = {0.0f, 1.0f, 0.0f}; cardinalAxis = tmp; } - if (fabsf(v.z) < min) + if (fabs(v.z) < min) { Vector3 tmp = {0.0f, 0.0f, 1.0f}; cardinalAxis = tmp; @@ -355,11 +392,25 @@ RMDEF Vector3 Vector3Negate(Vector3 v) return result; } +// Divide vector by a float value +RMDEF Vector3 Vector3Divide(Vector3 v, float div) +{ + Vector3 result = { v.x / div, v.y / div, v.z / div }; + return result; +} + +// Divide vector by vector +RMDEF Vector3 Vector3DivideV(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; + return result; +} + // Normalize provided vector RMDEF Vector3 Vector3Normalize(Vector3 v) { Vector3 result = v; - + float length, ilength; length = Vector3Length(v); if (length == 0.0f) length = 1.0f; @@ -394,10 +445,22 @@ RMDEF Vector3 Vector3Transform(Vector3 v, Matrix mat) result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; - + return result; }; +// Transform a vector by quaternion rotation +RMDEF Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) +{ + Vector3 result = { 0 }; + + result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); + result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); + result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); + + return result; +} + // Calculate linear interpolation between two vectors RMDEF Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) { @@ -432,11 +495,11 @@ RMDEF Vector3 Vector3Reflect(Vector3 v, Vector3 normal) RMDEF Vector3 Vector3Min(Vector3 v1, Vector3 v2) { Vector3 result = { 0 }; - + result.x = fminf(v1.x, v2.x); result.y = fminf(v1.y, v2.y); result.z = fminf(v1.z, v2.z); - + return result; } @@ -444,11 +507,11 @@ RMDEF Vector3 Vector3Min(Vector3 v1, Vector3 v2) RMDEF Vector3 Vector3Max(Vector3 v1, Vector3 v2) { Vector3 result = { 0 }; - + result.x = fmaxf(v1.x, v2.x); result.y = fmaxf(v1.y, v2.y); result.z = fmaxf(v1.z, v2.z); - + return result; } @@ -457,7 +520,7 @@ RMDEF Vector3 Vector3Max(Vector3 v1, Vector3 v2) RMDEF Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) { //Vector v0 = b - a, v1 = c - a, v2 = p - a; - + Vector3 v0 = Vector3Subtract(b, a); Vector3 v1 = Vector3Subtract(c, a); Vector3 v2 = Vector3Subtract(p, a); @@ -466,15 +529,15 @@ RMDEF Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) float d11 = Vector3DotProduct(v1, v1); float d20 = Vector3DotProduct(v2, v0); float d21 = Vector3DotProduct(v2, v1); - + float denom = d00*d11 - d01*d01; - + Vector3 result = { 0 }; - + result.y = (d11*d20 - d01*d21)/denom; result.z = (d00*d21 - d01*d20)/denom; result.x = 1.0f - (result.z + result.y); - + return result; } @@ -598,7 +661,7 @@ RMDEF Matrix MatrixInvert(Matrix mat) RMDEF Matrix MatrixNormalize(Matrix mat) { Matrix result = { 0 }; - + float det = MatrixDeterminant(mat); result.m0 = mat.m0/det; @@ -617,15 +680,15 @@ RMDEF Matrix MatrixNormalize(Matrix mat) result.m13 = mat.m13/det; result.m14 = mat.m14/det; result.m15 = mat.m15/det; - + return result; } // Returns identity matrix RMDEF Matrix MatrixIdentity(void) { - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; @@ -685,9 +748,9 @@ RMDEF Matrix MatrixSubstract(Matrix left, Matrix right) // Returns translation matrix RMDEF Matrix MatrixTranslate(float x, float y, float z) { - Matrix result = { 1.0f, 0.0f, 0.0f, x, - 0.0f, 1.0f, 0.0f, y, - 0.0f, 0.0f, 1.0f, z, + Matrix result = { 1.0f, 0.0f, 0.0f, x, + 0.0f, 1.0f, 0.0f, y, + 0.0f, 0.0f, 1.0f, z, 0.0f, 0.0f, 0.0f, 1.0f }; return result; @@ -724,12 +787,12 @@ RMDEF Matrix MatrixRotate(Vector3 axis, float angle) result.m5 = y*y*t + cosres; result.m6 = z*y*t + x*sinres; result.m7 = 0.0f; - + result.m8 = x*z*t + y*sinres; result.m9 = y*z*t - x*sinres; result.m10 = z*z*t + cosres; result.m11 = 0.0f; - + result.m12 = 0.0f; result.m13 = 0.0f; result.m14 = 0.0f; @@ -789,9 +852,9 @@ RMDEF Matrix MatrixRotateZ(float angle) // Returns scaling matrix RMDEF Matrix MatrixScale(float x, float y, float z) { - Matrix result = { x, 0.0f, 0.0f, 0.0f, - 0.0f, y, 0.0f, 0.0f, - 0.0f, 0.0f, z, 0.0f, + Matrix result = { x, 0.0f, 0.0f, 0.0f, + 0.0f, y, 0.0f, 0.0f, + 0.0f, 0.0f, z, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; return result; @@ -828,28 +891,28 @@ RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top, { Matrix result = { 0 }; - float rl = (right - left); - float tb = (top - bottom); - float fn = (far - near); + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(far - near); - result.m0 = (near*2.0f)/rl; + result.m0 = ((float) near*2.0f)/rl; result.m1 = 0.0f; result.m2 = 0.0f; result.m3 = 0.0f; result.m4 = 0.0f; - result.m5 = (near*2.0f)/tb; + result.m5 = ((float) near*2.0f)/tb; result.m6 = 0.0f; result.m7 = 0.0f; - result.m8 = (right + left)/rl; - result.m9 = (top + bottom)/tb; - result.m10 = -(far + near)/fn; + result.m8 = ((float)right + (float)left)/rl; + result.m9 = ((float)top + (float)bottom)/tb; + result.m10 = -((float)far + (float)near)/fn; result.m11 = -1.0f; result.m12 = 0.0f; result.m13 = 0.0f; - result.m14 = -(far*near*2.0f)/fn; + result.m14 = -((float)far*(float)near*2.0f)/fn; result.m15 = 0.0f; return result; @@ -859,11 +922,11 @@ RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top, // NOTE: Angle should be provided in radians RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far) { - double top = near*tan(fovy*0.5); + double top = near*tan(fovy*0.5); double right = top*aspect; Matrix result = MatrixFrustum(-right, right, -top, top, near, far); - return result; + return result; } // Returns orthographic projection matrix @@ -871,9 +934,9 @@ RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, d { Matrix result = { 0 }; - float rl = (right - left); - float tb = (top - bottom); - float fn = (far - near); + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(far - near); result.m0 = 2.0f/rl; result.m1 = 0.0f; @@ -887,9 +950,9 @@ RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, d result.m9 = 0.0f; result.m10 = -2.0f/fn; result.m11 = 0.0f; - result.m12 = -(left + right)/rl; - result.m13 = -(top + bottom)/tb; - result.m14 = -(far + near)/fn; + result.m12 = -((float)left + (float)right)/rl; + result.m13 = -((float)top + (float)bottom)/tb; + result.m14 = -((float)far + (float)near)/fn; result.m15 = 1.0f; return result; @@ -906,7 +969,7 @@ RMDEF Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) x = Vector3Normalize(x); Vector3 y = Vector3CrossProduct(z, x); y = Vector3Normalize(y); - + result.m0 = x.x; result.m1 = x.y; result.m2 = x.z; @@ -968,7 +1031,7 @@ RMDEF Quaternion QuaternionIdentity(void) // Computes the length of a quaternion RMDEF float QuaternionLength(Quaternion q) { - float result = sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + float result = (float)sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); return result; } @@ -976,7 +1039,7 @@ RMDEF float QuaternionLength(Quaternion q) RMDEF Quaternion QuaternionNormalize(Quaternion q) { Quaternion result = { 0 }; - + float length, ilength; length = QuaternionLength(q); if (length == 0.0f) length = 1.0f; @@ -986,7 +1049,7 @@ RMDEF Quaternion QuaternionNormalize(Quaternion q) result.y = q.y*ilength; result.z = q.z*ilength; result.w = q.w*ilength; - + return result; } @@ -996,17 +1059,17 @@ RMDEF Quaternion QuaternionInvert(Quaternion q) Quaternion result = q; float length = QuaternionLength(q); float lengthSq = length*length; - + if (lengthSq != 0.0) { float i = 1.0f/lengthSq; - + result.x *= -i; result.y *= -i; result.z *= -i; result.w *= i; } - + return result; } @@ -1044,7 +1107,7 @@ RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) { Quaternion result = QuaternionLerp(q1, q2, amount); result = QuaternionNormalize(result); - + return result; } @@ -1059,8 +1122,8 @@ RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); else { - float halfTheta = acos(cosHalfTheta); - float sinHalfTheta = sqrt(1.0f - cosHalfTheta*cosHalfTheta); + float halfTheta = (float) acos(cosHalfTheta); + float sinHalfTheta = (float) sqrt(1.0f - cosHalfTheta*cosHalfTheta); if (fabs(sinHalfTheta) < 0.001f) { @@ -1096,13 +1159,13 @@ RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) result.y = cross.y; result.z = cross.y; result.w = 1.0f + cos2Theta; // NOTE: Added QuaternioIdentity() - + // Normalize to essentially nlerp the original and identity to 0.5 - result = QuaternionNormalize(result); - + result = QuaternionNormalize(result); + // Above lines are equivalent to: //Quaternion result = QuaternionNlerp(q, QuaternionIdentity(), 0.5f); - + return result; } @@ -1172,7 +1235,7 @@ RMDEF Matrix QuaternionToMatrix(Quaternion q) float x2 = x + x; float y2 = y + y; float z2 = z + z; - + float length = QuaternionLength(q); float lengthSquared = length*length; @@ -1204,7 +1267,7 @@ RMDEF Matrix QuaternionToMatrix(Quaternion q) result.m13 = 0.0f; result.m14 = 0.0f; result.m15 = 1.0f; - + return result; } @@ -1219,7 +1282,7 @@ RMDEF Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) angle *= 0.5f; axis = Vector3Normalize(axis); - + float sinres = sinf(angle); float cosres = cosf(angle); @@ -1277,7 +1340,7 @@ RMDEF Quaternion QuaternionFromEuler(float roll, float pitch, float yaw) q.y = x0*y1*z0 + x1*y0*z1; q.z = x0*y0*z1 - x1*y1*z0; q.w = x0*y0*z0 + x1*y1*z1; - + return q; } @@ -1300,9 +1363,9 @@ RMDEF Vector3 QuaternionToEuler(Quaternion q) // yaw (z-axis rotation) float z0 = 2.0f*(q.w*q.z + q.x*q.y); - float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); + float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); result.z = atan2f(z0, z1)*RAD2DEG; - + return result; } @@ -1315,7 +1378,7 @@ RMDEF Quaternion QuaternionTransform(Quaternion q, Matrix mat) result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; - + return result; } diff --git a/raylib/rlgl.h b/raylib/rlgl.h index c071aca..3f5c67a 100644 --- a/raylib/rlgl.h +++ b/raylib/rlgl.h @@ -2,10 +2,10 @@ * * rlgl - raylib OpenGL abstraction layer * -* rlgl is a wrapper for multiple OpenGL versions (1.1, 2.1, 3.3 Core, ES 2.0) to -* pseudo-OpenGL 1.1 style functions (rlVertex, rlTranslate, rlRotate...). +* rlgl is a wrapper for multiple OpenGL versions (1.1, 2.1, 3.3 Core, ES 2.0) to +* pseudo-OpenGL 1.1 style functions (rlVertex, rlTranslate, rlRotate...). * -* When chosing an OpenGL version greater than OpenGL 1.1, rlgl stores vertex data on internal +* When chosing an OpenGL version greater than OpenGL 1.1, rlgl stores vertex data on internal * VBO buffers (and VAOs if available). It requires calling 3 functions: * rlglInit() - Initialize internal buffers and auxiliar resources * rlglDraw() - Process internal buffers and send required draw calls @@ -18,9 +18,14 @@ * #define GRAPHICS_API_OPENGL_33 * #define GRAPHICS_API_OPENGL_ES2 * Use selected OpenGL graphics backend, should be supported by platform -* Those preprocessor defines are only used on rlgl module, if OpenGL version is +* Those preprocessor defines are only used on rlgl module, if OpenGL version is * required by any other module, use rlGetVersion() tocheck it * +* #define RLGL_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* * #define RLGL_STANDALONE * Use rlgl as standalone library (no raylib dependency) * @@ -28,7 +33,7 @@ * Support VR simulation functionality (stereo rendering) * * #define SUPPORT_DISTORTION_SHADER -* Include stereo rendering distortion shader (shader_distortion.h) +* Include stereo rendering distortion shader (embedded) * * DEPENDENCIES: * raymath - 3D math functionality (Vector3, Matrix, Quaternion) @@ -37,7 +42,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2017 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -61,6 +66,7 @@ #if defined(RLGL_STANDALONE) #define RAYMATH_STANDALONE + #define RAYMATH_HEADER_ONLY #else #include "raylib.h" // Required for: Model, Shader, Texture2D, TraceLog() #endif @@ -68,8 +74,11 @@ #include "raymath.h" // Required for: Vector3, Matrix // Security check in case no GRAPHICS_API_OPENGL_* defined -#if !defined(GRAPHICS_API_OPENGL_11) && !defined(GRAPHICS_API_OPENGL_21) && !defined(GRAPHICS_API_OPENGL_33) && !defined(GRAPHICS_API_OPENGL_ES2) - #define GRAPHICS_API_OPENGL_11 +#if !defined(GRAPHICS_API_OPENGL_11) && \ + !defined(GRAPHICS_API_OPENGL_21) && \ + !defined(GRAPHICS_API_OPENGL_33) && \ + !defined(GRAPHICS_API_OPENGL_ES2) + #define GRAPHICS_API_OPENGL_33 #endif // Security check in case multiple GRAPHICS_API_OPENGL_* defined @@ -124,7 +133,7 @@ #define RL_WRAP_CLAMP_MIRROR 0x8742 // GL_MIRROR_CLAMP_EXT // Matrix modes (equivalent to OpenGL) -#define RL_MODELVIEW 0x1700 // GL_MODELVIEW +#define RL_MODELVIEW 0x1700 // GL_MODELVIEW #define RL_PROJECTION 0x1701 // GL_PROJECTION #define RL_TEXTURE 0x1702 // GL_TEXTURE @@ -153,7 +162,7 @@ typedef unsigned char byte; unsigned char b; unsigned char a; } Color; - + // Rectangle type typedef struct Rectangle { int x; @@ -161,7 +170,7 @@ typedef unsigned char byte; int width; int height; } Rectangle; - + // Texture2D type // NOTE: Data stored in GPU memory typedef struct Texture2D { @@ -190,15 +199,22 @@ typedef unsigned char byte; float *tangents; // vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3) unsigned short *indices;// vertex indices (in case vertex data comes indexed) + + // Animation vertex data + float *baseVertices; // Vertex base position (required to apply bones transformations) + float *baseNormals; // Vertex base normals (required to apply bones transformations) + float *weightBias; // Vertex weight bias + int *weightId; // Vertex weight id + // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (7 types of vertex data) } Mesh; - + // Shader and material limits #define MAX_SHADER_LOCATIONS 32 #define MAX_MATERIAL_MAPS 12 - + // Shader type (generic) typedef struct Shader { unsigned int id; // Shader program id @@ -226,7 +242,7 @@ typedef unsigned char byte; Vector3 up; // Camera up vector (rotation over its axis) float fovy; // Camera field-of-view apperture in Y (degrees) } Camera; - + // Head-Mounted-Display device parameters typedef struct VrDeviceInfo { int hResolution; // HMD horizontal resolution in pixels @@ -240,16 +256,16 @@ typedef unsigned char byte; float lensDistortionValues[4]; // HMD lens distortion constant parameters float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters } VrDeviceInfo; - + // TraceLog message types - typedef enum { - LOG_INFO = 0, - LOG_ERROR, - LOG_WARNING, - LOG_DEBUG, - LOG_OTHER + typedef enum { + LOG_INFO = 0, + LOG_ERROR, + LOG_WARNING, + LOG_DEBUG, + LOG_OTHER } TraceLogType; - + // Texture formats (support depends on OpenGL version) typedef enum { UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) @@ -278,7 +294,7 @@ typedef unsigned char byte; // Texture parameters: filter mode // NOTE 1: Filtering considers mipmaps if available in the texture // NOTE 2: Filter is accordingly set for minification and magnification - typedef enum { + typedef enum { FILTER_POINT = 0, // No filter, just pixel aproximation FILTER_BILINEAR, // Linear filtering FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) @@ -286,19 +302,19 @@ typedef unsigned char byte; FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x } TextureFilterMode; - + // Texture parameters: wrap mode - typedef enum { - WRAP_REPEAT = 0, - WRAP_CLAMP, - WRAP_MIRROR + typedef enum { + WRAP_REPEAT = 0, + WRAP_CLAMP, + WRAP_MIRROR } TextureWrapMode; // Color blending modes (pre-defined) - typedef enum { - BLEND_ALPHA = 0, - BLEND_ADDITIVE, - BLEND_MULTIPLIED + typedef enum { + BLEND_ALPHA = 0, + BLEND_ADDITIVE, + BLEND_MULTIPLIED } BlendMode; // Shader location point type @@ -424,14 +440,17 @@ void rlglClose(void); // De-inititialize rlgl (buffers void rlglDraw(void); // Update and Draw default buffers (lines, triangles, quads) int rlGetVersion(void); // Returns current OpenGL version +bool rlCheckBufferLimit(int type, int vCount); // Check internal buffer overflow for a given number of vertex void rlSetDebugMarker(const char *text); // Set debug marker for analysis void rlLoadExtensions(void *loader); // Load OpenGL extensions Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates // Textures data management -unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU -void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data -void rlUnloadTexture(unsigned int id); +unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU +void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats +void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory + void rlGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture void *rlReadTexturePixels(Texture2D texture); // Read texture pixel data unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) @@ -451,45 +470,50 @@ void rlUnloadMesh(Mesh *mesh); // Unload me // Shaders System Functions (Module: rlgl) // NOTE: This functions are useless when using OpenGL 1.1 //------------------------------------------------------------------------------------ -Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader and bind default locations -void UnloadShader(Shader shader); // Unload a custom shader from memory +// Shader loading/unloading functions +char *LoadText(const char *fileName); // Load chars array from text file +Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations +Shader LoadShaderCode(char *vsCode, char *fsCode); // Load shader from code strings and bind default locations +void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) -Shader GetShaderDefault(void); // Get default shader -Texture2D GetTextureDefault(void); // Get default texture +Shader GetShaderDefault(void); // Get default shader +Texture2D GetTextureDefault(void); // Get default texture // Shader configuration functions int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location void SetShaderValue(Shader shader, int uniformLoc, const float *value, int size); // Set shader uniform value (float) void SetShaderValuei(Shader shader, int uniformLoc, const int *value, int size); // Set shader uniform value (int) void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) -void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) -void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) -Matrix GetMatrixModelview(); // Get internal modelview matrix - +void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) +void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) +Matrix GetMatrixModelview(); // Get internal modelview matrix // Texture maps generation (PBR) // NOTE: Required shaders should be provided -Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture -Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data -Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data -Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size); // Generate BRDF texture using cubemap data +Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture +Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data +Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data +Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size); // Generate BRDF texture using cubemap data -// Shading and blending -void BeginShaderMode(Shader shader); // Begin custom shader drawing -void EndShaderMode(void); // End custom shader drawing (use default shader) -void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied) -void EndBlendMode(void); // End blending mode (reset to default: alpha blending) +// Shading begin/end functions +void BeginShaderMode(Shader shader); // Begin custom shader drawing +void EndShaderMode(void); // End custom shader drawing (use default shader) +void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied) +void EndBlendMode(void); // End blending mode (reset to default: alpha blending) -// VR simulator functionality -VrDeviceInfo GetVrDeviceInfo(int vrDeviceType); // Get VR device information for some standard devices -void InitVrSimulator(VrDeviceInfo info); // Init VR simulator for selected device parameters -void CloseVrSimulator(void); // Close VR simulator for current device -void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera -void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator) -void BeginVrDrawing(void); // Begin VR stereo rendering -void EndVrDrawing(void); // End VR stereo rendering +// VR control functions +VrDeviceInfo GetVrDeviceInfo(int vrDeviceType); // Get VR device information for some standard devices +void InitVrSimulator(VrDeviceInfo info); // Init VR simulator for selected device parameters +void CloseVrSimulator(void); // Close VR simulator for current device +bool IsVrSimulatorReady(void); // Detect if VR simulator is ready +void SetVrDistortionShader(Shader shader); // Set VR distortion shader for stereoscopic rendering +void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +void ToggleVrMode(void); // Enable/Disable VR experience +void BeginVrDrawing(void); // Begin VR simulator stereo rendering +void EndVrDrawing(void); // End VR simulator stereo rendering void TraceLog(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) +int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture) #endif #ifdef __cplusplus @@ -497,3 +521,4315 @@ void TraceLog(int msgType, const char *text, ...); // Show trace log messag #endif #endif // RLGL_H + +/*********************************************************************************** +* +* RLGL IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RLGL_IMPLEMENTATION) + +#if defined(RLGL_STANDALONE) + #define SUPPORT_VR_SIMULATOR + #define SUPPORT_DISTORTION_SHADER +#else + #include "config.h" // rlgl module configuration +#endif + +#include // Required for: fopen(), fclose(), fread()... [Used only on LoadText()] +#include // Required for: malloc(), free(), rand() +#include // Required for: strcmp(), strlen(), strtok() [Used only in extensions loading] +#include // Required for: atan2() + +#if !defined(RLGL_STANDALONE) + #include "raymath.h" // Required for: Vector3 and Matrix functions +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + #if defined(__APPLE__) + #include // OpenGL 1.1 library for OSX + #include + #else + // APIENTRY for OpenGL function pointer declarations is required + #ifndef APIENTRY + #ifdef _WIN32 + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif + #endif + // WINGDIAPI definition. Some Windows OpenGL headers need it + #if !defined(WINGDIAPI) && defined(_WIN32) + #define WINGDIAPI __declspec(dllimport) + #endif + + #include // OpenGL 1.1 library + #endif +#endif + +#if defined(GRAPHICS_API_OPENGL_21) + #define GRAPHICS_API_OPENGL_33 // OpenGL 2.1 uses mostly OpenGL 3.3 Core functionality +#endif + +#if defined(GRAPHICS_API_OPENGL_33) + #if defined(__APPLE__) + #include // OpenGL 3 library for OSX + #include // OpenGL 3 extensions library for OSX + #else + #define GLAD_IMPLEMENTATION + #if defined(RLGL_STANDALONE) + #include "glad.h" // GLAD extensions loading library, includes OpenGL headers + #else + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers + #endif + #endif +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + #include // EGL library + #include // OpenGL ES 2.0 library + #include // OpenGL ES 2.0 extensions library +#endif + +#if defined(RLGL_STANDALONE) + #include // Required for: va_list, va_start(), vfprintf(), va_end() [Used only on TraceLog()] +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define MATRIX_STACK_SIZE 16 // Matrix stack max size +#define MAX_DRAWS_BY_TEXTURE 256 // Draws are organized by texture changes +#define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) + // NOTE: Every vertex are 3 floats (12 bytes) + +#ifndef GL_SHADING_LANGUAGE_VERSION + #define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#endif + +#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif +#ifndef GL_ETC1_RGB8_OES + #define GL_ETC1_RGB8_OES 0x8D64 +#endif +#ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 +#endif +#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif +#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#endif +#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR + #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_8x8_KHR + #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 +#endif + +#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + #define GL_UNSIGNED_SHORT_5_6_5 0x8363 + #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 + #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#endif + +#if defined(GRAPHICS_API_OPENGL_21) + #define GL_LUMINANCE 0x1909 + #define GL_LUMINANCE_ALPHA 0x190A +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + #define glClearDepth glClearDepthf + #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER + #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER +#endif + +// Default vertex attribute names on shader to set location points +#define DEFAULT_ATTRIB_POSITION_NAME "vertexPosition" // shader-location = 0 +#define DEFAULT_ATTRIB_TEXCOORD_NAME "vertexTexCoord" // shader-location = 1 +#define DEFAULT_ATTRIB_NORMAL_NAME "vertexNormal" // shader-location = 2 +#define DEFAULT_ATTRIB_COLOR_NAME "vertexColor" // shader-location = 3 +#define DEFAULT_ATTRIB_TANGENT_NAME "vertexTangent" // shader-location = 4 +#define DEFAULT_ATTRIB_TEXCOORD2_NAME "vertexTexCoord2" // shader-location = 5 + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// Dynamic vertex buffers (position + texcoords + colors + indices arrays) +typedef struct DynamicBuffer { + int vCounter; // vertex position counter to process (and draw) from full buffer + int tcCounter; // vertex texcoord counter to process (and draw) from full buffer + int cCounter; // vertex color counter to process (and draw) from full buffer + float *vertices; // vertex position (XYZ - 3 components per vertex) (shader-location = 0) + float *texcoords; // vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3) +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + unsigned int *indices; // vertex indices (in case vertex data comes indexed) (6 indices per quad) +#elif defined(GRAPHICS_API_OPENGL_ES2) + unsigned short *indices; // vertex indices (in case vertex data comes indexed) (6 indices per quad) + // NOTE: 6*2 byte = 12 byte, not alignment problem! +#endif + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int vboId[4]; // OpenGL Vertex Buffer Objects id (4 types of vertex data) +} DynamicBuffer; + +// Draw call type +// NOTE: Used to track required draw-calls, organized by texture +typedef struct DrawCall { + int vertexCount; + GLuint vaoId; + GLuint textureId; + GLuint shaderId; + + Matrix projection; + Matrix modelview; + + // TODO: Store additional draw state data + //int blendMode; + //Guint fboId; +} DrawCall; + +#if defined(SUPPORT_VR_SIMULATOR) +// VR Stereo rendering configuration for simulator +typedef struct VrStereoConfig { + RenderTexture2D stereoFbo; // VR stereo rendering framebuffer + Shader distortionShader; // VR stereo rendering distortion shader + Rectangle eyesViewport[2]; // VR stereo rendering eyes viewports + Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices + Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices +} VrStereoConfig; +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +#if !defined(GRAPHICS_API_OPENGL_11) && defined(SUPPORT_DISTORTION_SHADER) + // Distortion shader embedded + static char distortionFShaderStr[] = + #if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" + #elif defined(GRAPHICS_API_OPENGL_ES2) + "#version 100 \n" + "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) + #endif + #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" + #elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec2 fragTexCoord; \n" + "in vec4 fragColor; \n" + "out vec4 finalColor; \n" + #endif + "uniform sampler2D texture0; \n" + #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + "uniform vec2 leftLensCenter; \n" + "uniform vec2 rightLensCenter; \n" + "uniform vec2 leftScreenCenter; \n" + "uniform vec2 rightScreenCenter; \n" + "uniform vec2 scale; \n" + "uniform vec2 scaleIn; \n" + "uniform vec4 hmdWarpParam; \n" + "uniform vec4 chromaAbParam; \n" + #elif defined(GRAPHICS_API_OPENGL_33) + "uniform vec2 leftLensCenter = vec2(0.288, 0.5); \n" + "uniform vec2 rightLensCenter = vec2(0.712, 0.5); \n" + "uniform vec2 leftScreenCenter = vec2(0.25, 0.5); \n" + "uniform vec2 rightScreenCenter = vec2(0.75, 0.5); \n" + "uniform vec2 scale = vec2(0.25, 0.45); \n" + "uniform vec2 scaleIn = vec2(4, 2.2222); \n" + "uniform vec4 hmdWarpParam = vec4(1, 0.22, 0.24, 0); \n" + "uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); \n" + #endif + "void main() \n" + "{ \n" + " vec2 lensCenter = fragTexCoord.x < 0.5 ? leftLensCenter : rightLensCenter; \n" + " vec2 screenCenter = fragTexCoord.x < 0.5 ? leftScreenCenter : rightScreenCenter; \n" + " vec2 theta = (fragTexCoord - lensCenter)*scaleIn; \n" + " float rSq = theta.x*theta.x + theta.y*theta.y; \n" + " vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); \n" + " vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); \n" + " vec2 tcBlue = lensCenter + scale*thetaBlue; \n" + " if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) \n" + " { \n" + #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); \n" + #elif defined(GRAPHICS_API_OPENGL_33) + " finalColor = vec4(0.0, 0.0, 0.0, 1.0); \n" + #endif + " } \n" + " else \n" + " { \n" + #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + " float blue = texture2D(texture0, tcBlue).b; \n" + " vec2 tcGreen = lensCenter + scale*theta1; \n" + " float green = texture2D(texture0, tcGreen).g; \n" + #elif defined(GRAPHICS_API_OPENGL_33) + " float blue = texture(texture0, tcBlue).b; \n" + " vec2 tcGreen = lensCenter + scale*theta1; \n" + " float green = texture(texture0, tcGreen).g; \n" + #endif + " vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); \n" + " vec2 tcRed = lensCenter + scale*thetaRed; \n" + #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + " float red = texture2D(texture0, tcRed).r; \n" + " gl_FragColor = vec4(red, green, blue, 1.0); \n" + #elif defined(GRAPHICS_API_OPENGL_33) + " float red = texture(texture0, tcRed).r; \n" + " finalColor = vec4(red, green, blue, 1.0); \n" + #endif + " } \n" + "} \n"; +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +static Matrix stack[MATRIX_STACK_SIZE]; +static int stackCounter = 0; + +static Matrix modelview = { 0 }; +static Matrix projection = { 0 }; +static Matrix *currentMatrix = NULL; +static int currentMatrixMode = -1; + +static int currentDrawMode = -1; + +static float currentDepth = -1.0f; + +static DynamicBuffer lines = { 0 }; // Default dynamic buffer for lines data +static DynamicBuffer triangles = { 0 }; // Default dynamic buffer for triangles data +static DynamicBuffer quads = { 0 }; // Default dynamic buffer for quads data (used to draw textures) + +// Default buffers draw calls +static DrawCall *draws = NULL; +static int drawsCounter = 0; + +// Temp vertex buffer to be used with rlTranslate, rlRotate, rlScale +static Vector3 *tempBuffer = NULL; +static int tempBufferCount = 0; +static bool useTempBuffer = false; + +// Shaders +static unsigned int defaultVShaderId; // Default vertex shader id (used by default shader program) +static unsigned int defaultFShaderId; // Default fragment shader Id (used by default shader program) + +static Shader defaultShader; // Basic shader, support vertex color and diffuse texture +static Shader currentShader; // Shader to be used on rendering (by default, defaultShader) + +// Extension supported flag: VAO +static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support VAO extension) + +// Extension supported flag: Compressed textures +static bool texCompETC1Supported = false; // ETC1 texture compression support +static bool texCompETC2Supported = false; // ETC2/EAC texture compression support +static bool texCompPVRTSupported = false; // PVR texture compression support +static bool texCompASTCSupported = false; // ASTC texture compression support + +#if defined(SUPPORT_VR_SIMULATOR) +// VR global variables +static VrStereoConfig vrConfig; // VR stereo configuration for simulator +static bool vrSimulatorReady = false; // VR simulator ready flag +static bool vrStereoRender = false; // VR stereo rendering enabled/disabled flag + // NOTE: This flag is useful to render data over stereo image (i.e. FPS) +#endif // defined(SUPPORT_VR_SIMULATOR) + +#endif // defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + +// Extension supported flag: Anisotropic filtering +static bool texAnisotropicFilterSupported = false; // Anisotropic texture filtering support +static float maxAnisotropicLevel = 0.0f; // Maximum anisotropy level supported (minimum is 2.0f) + +// Extension supported flag: Clamp mirror wrap mode +static bool texClampMirrorSupported = false; // Clamp mirror wrap mode supported + +#if defined(GRAPHICS_API_OPENGL_ES2) +// NOTE: VAO functionality is exposed through extensions (OES) +static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays; +static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray; +static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; +//static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; // NOTE: Fails in WebGL, omitted +#endif + +static bool debugMarkerSupported = false; + +// Compressed textures support flags +static bool texCompDXTSupported = false; // DDS texture compression support +static bool texNPOTSupported = false; // NPOT textures full support +static bool texFloatSupported = false; // float textures support (32 bit per channel) + +static int blendMode = 0; // Track current blending mode + +// White texture useful for plain color polys (required by shader) +static unsigned int whiteTexture; + +// Default framebuffer size +static int screenWidth; // Default framebuffer width +static int screenHeight; // Default framebuffer height + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +static unsigned int CompileShader(const char *shaderStr, int type); // Compile custom shader and return shader id +static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program + +static Shader LoadShaderDefault(void); // Load default shader (just vertex positioning and texture coloring) +static void SetShaderDefaultLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) +static void UnloadShaderDefault(void); // Unload default shader + +static void LoadBuffersDefault(void); // Load default internal buffers (lines, triangles, quads) +static void UpdateBuffersDefault(void); // Update default internal buffers (VAOs/VBOs) with vertex data +static void DrawBuffersDefault(void); // Draw default internal buffers vertex data +static void UnloadBuffersDefault(void); // Unload default internal buffers vertex data from CPU and GPU + +static void GenDrawCube(void); // Generate and draw cube +static void GenDrawQuad(void); // Generate and draw quad + +#if defined(SUPPORT_VR_SIMULATOR) +static void SetStereoConfig(VrDeviceInfo info); // Configure stereo rendering (including distortion shader) with HMD device parameters +static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); // Set internal projection and modelview matrix depending on eye +#endif + +#endif // defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + +#if defined(GRAPHICS_API_OPENGL_11) +static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); +static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight); +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Matrix operations +//---------------------------------------------------------------------------------- + +#if defined(GRAPHICS_API_OPENGL_11) + +// Fallback to OpenGL 1.1 function calls +//--------------------------------------- +void rlMatrixMode(int mode) +{ + switch (mode) + { + case RL_PROJECTION: glMatrixMode(GL_PROJECTION); break; + case RL_MODELVIEW: glMatrixMode(GL_MODELVIEW); break; + case RL_TEXTURE: glMatrixMode(GL_TEXTURE); break; + default: break; + } +} + +void rlFrustum(double left, double right, double bottom, double top, double zNear, double zFar) +{ + glFrustum(left, right, bottom, top, zNear, zFar); +} + +void rlOrtho(double left, double right, double bottom, double top, double zNear, double zFar) +{ + glOrtho(left, right, bottom, top, zNear, zFar); +} + +void rlPushMatrix(void) { glPushMatrix(); } +void rlPopMatrix(void) { glPopMatrix(); } +void rlLoadIdentity(void) { glLoadIdentity(); } +void rlTranslatef(float x, float y, float z) { glTranslatef(x, y, z); } +void rlRotatef(float angleDeg, float x, float y, float z) { glRotatef(angleDeg, x, y, z); } +void rlScalef(float x, float y, float z) { glScalef(x, y, z); } +void rlMultMatrixf(float *matf) { glMultMatrixf(matf); } + +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + +// Choose the current matrix to be transformed +void rlMatrixMode(int mode) +{ + if (mode == RL_PROJECTION) currentMatrix = &projection; + else if (mode == RL_MODELVIEW) currentMatrix = &modelview; + //else if (mode == RL_TEXTURE) // Not supported + + currentMatrixMode = mode; +} + +// Push the current matrix to stack +void rlPushMatrix(void) +{ + if (stackCounter == MATRIX_STACK_SIZE - 1) + { + TraceLog(LOG_ERROR, "Stack Buffer Overflow (MAX %i Matrix)", MATRIX_STACK_SIZE); + } + + stack[stackCounter] = *currentMatrix; + rlLoadIdentity(); // TODO: Review matrix stack logic! + stackCounter++; + + if (currentMatrixMode == RL_MODELVIEW) useTempBuffer = true; +} + +// Pop lattest inserted matrix from stack +void rlPopMatrix(void) +{ + if (stackCounter > 0) + { + Matrix mat = stack[stackCounter - 1]; + *currentMatrix = mat; + stackCounter--; + } +} + +// Reset current matrix to identity matrix +void rlLoadIdentity(void) +{ + *currentMatrix = MatrixIdentity(); +} + +// Multiply the current matrix by a translation matrix +void rlTranslatef(float x, float y, float z) +{ + Matrix matTranslation = MatrixTranslate(x, y, z); + + // NOTE: We transpose matrix with multiplication order + *currentMatrix = MatrixMultiply(matTranslation, *currentMatrix); +} + +// Multiply the current matrix by a rotation matrix +void rlRotatef(float angleDeg, float x, float y, float z) +{ + Matrix matRotation = MatrixIdentity(); + + Vector3 axis = (Vector3){ x, y, z }; + matRotation = MatrixRotate(Vector3Normalize(axis), angleDeg*DEG2RAD); + + // NOTE: We transpose matrix with multiplication order + *currentMatrix = MatrixMultiply(matRotation, *currentMatrix); +} + +// Multiply the current matrix by a scaling matrix +void rlScalef(float x, float y, float z) +{ + Matrix matScale = MatrixScale(x, y, z); + + // NOTE: We transpose matrix with multiplication order + *currentMatrix = MatrixMultiply(matScale, *currentMatrix); +} + +// Multiply the current matrix by another matrix +void rlMultMatrixf(float *matf) +{ + // Matrix creation from array + Matrix mat = { matf[0], matf[4], matf[8], matf[12], + matf[1], matf[5], matf[9], matf[13], + matf[2], matf[6], matf[10], matf[14], + matf[3], matf[7], matf[11], matf[15] }; + + *currentMatrix = MatrixMultiply(*currentMatrix, mat); +} + +// Multiply the current matrix by a perspective matrix generated by parameters +void rlFrustum(double left, double right, double bottom, double top, double near, double far) +{ + Matrix matPerps = MatrixFrustum(left, right, bottom, top, near, far); + + *currentMatrix = MatrixMultiply(*currentMatrix, matPerps); +} + +// Multiply the current matrix by an orthographic matrix generated by parameters +void rlOrtho(double left, double right, double bottom, double top, double near, double far) +{ + Matrix matOrtho = MatrixOrtho(left, right, bottom, top, near, far); + + *currentMatrix = MatrixMultiply(*currentMatrix, matOrtho); +} + +#endif + +// Set the viewport area (transformation from normalized device coordinates to window coordinates) +// NOTE: Updates global variables: screenWidth, screenHeight +void rlViewport(int x, int y, int width, int height) +{ + glViewport(x, y, width, height); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vertex level operations +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_11) + +// Fallback to OpenGL 1.1 function calls +//--------------------------------------- +void rlBegin(int mode) +{ + switch (mode) + { + case RL_LINES: glBegin(GL_LINES); break; + case RL_TRIANGLES: glBegin(GL_TRIANGLES); break; + case RL_QUADS: glBegin(GL_QUADS); break; + default: break; + } +} + +void rlEnd() { glEnd(); } +void rlVertex2i(int x, int y) { glVertex2i(x, y); } +void rlVertex2f(float x, float y) { glVertex2f(x, y); } +void rlVertex3f(float x, float y, float z) { glVertex3f(x, y, z); } +void rlTexCoord2f(float x, float y) { glTexCoord2f(x, y); } +void rlNormal3f(float x, float y, float z) { glNormal3f(x, y, z); } +void rlColor4ub(byte r, byte g, byte b, byte a) { glColor4ub(r, g, b, a); } +void rlColor3f(float x, float y, float z) { glColor3f(x, y, z); } +void rlColor4f(float x, float y, float z, float w) { glColor4f(x, y, z, w); } + +#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + +// Initialize drawing mode (how to organize vertex) +void rlBegin(int mode) +{ + // Draw mode can only be RL_LINES, RL_TRIANGLES and RL_QUADS + currentDrawMode = mode; +} + +// Finish vertex providing +void rlEnd(void) +{ + if (useTempBuffer) + { + // NOTE: In this case, *currentMatrix is already transposed because transposing has been applied + // independently to translation-scale-rotation matrices -> t(M1 x M2) = t(M2) x t(M1) + // This way, rlTranslatef(), rlRotatef()... behaviour is the same than OpenGL 1.1 + + // Apply transformation matrix to all temp vertices + for (int i = 0; i < tempBufferCount; i++) tempBuffer[i] = Vector3Transform(tempBuffer[i], *currentMatrix); + + // Deactivate tempBuffer usage to allow rlVertex3f do its job + useTempBuffer = false; + + // Copy all transformed vertices to right VAO + for (int i = 0; i < tempBufferCount; i++) rlVertex3f(tempBuffer[i].x, tempBuffer[i].y, tempBuffer[i].z); + + // Reset temp buffer + tempBufferCount = 0; + } + + // Make sure vertexCount is the same for vertices-texcoords-normals-colors + // NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls. + switch (currentDrawMode) + { + case RL_LINES: + { + if (lines.vCounter != lines.cCounter) + { + int addColors = lines.vCounter - lines.cCounter; + + for (int i = 0; i < addColors; i++) + { + lines.colors[4*lines.cCounter] = lines.colors[4*lines.cCounter - 4]; + lines.colors[4*lines.cCounter + 1] = lines.colors[4*lines.cCounter - 3]; + lines.colors[4*lines.cCounter + 2] = lines.colors[4*lines.cCounter - 2]; + lines.colors[4*lines.cCounter + 3] = lines.colors[4*lines.cCounter - 1]; + + lines.cCounter++; + } + } + } break; + case RL_TRIANGLES: + { + if (triangles.vCounter != triangles.cCounter) + { + int addColors = triangles.vCounter - triangles.cCounter; + + for (int i = 0; i < addColors; i++) + { + triangles.colors[4*triangles.cCounter] = triangles.colors[4*triangles.cCounter - 4]; + triangles.colors[4*triangles.cCounter + 1] = triangles.colors[4*triangles.cCounter - 3]; + triangles.colors[4*triangles.cCounter + 2] = triangles.colors[4*triangles.cCounter - 2]; + triangles.colors[4*triangles.cCounter + 3] = triangles.colors[4*triangles.cCounter - 1]; + + triangles.cCounter++; + } + } + } break; + case RL_QUADS: + { + // Make sure colors count match vertex count + if (quads.vCounter != quads.cCounter) + { + int addColors = quads.vCounter - quads.cCounter; + + for (int i = 0; i < addColors; i++) + { + quads.colors[4*quads.cCounter] = quads.colors[4*quads.cCounter - 4]; + quads.colors[4*quads.cCounter + 1] = quads.colors[4*quads.cCounter - 3]; + quads.colors[4*quads.cCounter + 2] = quads.colors[4*quads.cCounter - 2]; + quads.colors[4*quads.cCounter + 3] = quads.colors[4*quads.cCounter - 1]; + + quads.cCounter++; + } + } + + // Make sure texcoords count match vertex count + if (quads.vCounter != quads.tcCounter) + { + int addTexCoords = quads.vCounter - quads.tcCounter; + + for (int i = 0; i < addTexCoords; i++) + { + quads.texcoords[2*quads.tcCounter] = 0.0f; + quads.texcoords[2*quads.tcCounter + 1] = 0.0f; + + quads.tcCounter++; + } + } + + // TODO: Make sure normals count match vertex count... if normals support is added in a future... :P + + } break; + default: break; + } + + // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, + // as well as depth buffer bit-depth (16bit or 24bit or 32bit) + // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) + currentDepth += (1.0f/20000.0f); + + // Verify internal buffers limits + // NOTE: This check is combined with usage of rlCheckBufferLimit() + if ((lines.vCounter/2 >= (MAX_LINES_BATCH - 2)) || + (triangles.vCounter/3 >= (MAX_TRIANGLES_BATCH - 3)) || + (quads.vCounter/4 >= (MAX_QUADS_BATCH - 4))) + { + // WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(), + // we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call! + // Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw + + // TODO: Undoubtely, current rlPushMatrix/rlPopMatrix should be redesigned... or removed... it's not working properly + + rlPopMatrix(); + rlglDraw(); + } +} + +// Define one vertex (position) +void rlVertex3f(float x, float y, float z) +{ + // NOTE: Temp buffer is processed and resetted at rlEnd() + // Between rlBegin() and rlEnd() can not be more than TEMP_VERTEX_BUFFER_SIZE rlVertex3f() calls + if (useTempBuffer && (tempBufferCount < TEMP_VERTEX_BUFFER_SIZE)) + { + tempBuffer[tempBufferCount].x = x; + tempBuffer[tempBufferCount].y = y; + tempBuffer[tempBufferCount].z = z; + tempBufferCount++; + } + else + { + switch (currentDrawMode) + { + case RL_LINES: + { + // Verify that MAX_LINES_BATCH limit not reached + if (lines.vCounter/2 < MAX_LINES_BATCH) + { + lines.vertices[3*lines.vCounter] = x; + lines.vertices[3*lines.vCounter + 1] = y; + lines.vertices[3*lines.vCounter + 2] = z; + + lines.vCounter++; + } + else TraceLog(LOG_ERROR, "MAX_LINES_BATCH overflow"); + + } break; + case RL_TRIANGLES: + { + // Verify that MAX_TRIANGLES_BATCH limit not reached + if (triangles.vCounter/3 < MAX_TRIANGLES_BATCH) + { + triangles.vertices[3*triangles.vCounter] = x; + triangles.vertices[3*triangles.vCounter + 1] = y; + triangles.vertices[3*triangles.vCounter + 2] = z; + + triangles.vCounter++; + } + else TraceLog(LOG_ERROR, "MAX_TRIANGLES_BATCH overflow"); + + } break; + case RL_QUADS: + { + // Verify that MAX_QUADS_BATCH limit not reached + if (quads.vCounter/4 < MAX_QUADS_BATCH) + { + quads.vertices[3*quads.vCounter] = x; + quads.vertices[3*quads.vCounter + 1] = y; + quads.vertices[3*quads.vCounter + 2] = z; + + quads.vCounter++; + + draws[drawsCounter - 1].vertexCount++; + } + else TraceLog(LOG_ERROR, "MAX_QUADS_BATCH overflow"); + + } break; + default: break; + } + } +} + +// Define one vertex (position) +void rlVertex2f(float x, float y) +{ + rlVertex3f(x, y, currentDepth); +} + +// Define one vertex (position) +void rlVertex2i(int x, int y) +{ + rlVertex3f((float)x, (float)y, currentDepth); +} + +// Define one vertex (texture coordinate) +// NOTE: Texture coordinates are limited to QUADS only +void rlTexCoord2f(float x, float y) +{ + if (currentDrawMode == RL_QUADS) + { + quads.texcoords[2*quads.tcCounter] = x; + quads.texcoords[2*quads.tcCounter + 1] = y; + + quads.tcCounter++; + } +} + +// Define one vertex (normal) +// NOTE: Normals limited to TRIANGLES only ? +void rlNormal3f(float x, float y, float z) +{ + // TODO: Normals usage... +} + +// Define one vertex (color) +void rlColor4ub(byte x, byte y, byte z, byte w) +{ + switch (currentDrawMode) + { + case RL_LINES: + { + lines.colors[4*lines.cCounter] = x; + lines.colors[4*lines.cCounter + 1] = y; + lines.colors[4*lines.cCounter + 2] = z; + lines.colors[4*lines.cCounter + 3] = w; + + lines.cCounter++; + + } break; + case RL_TRIANGLES: + { + triangles.colors[4*triangles.cCounter] = x; + triangles.colors[4*triangles.cCounter + 1] = y; + triangles.colors[4*triangles.cCounter + 2] = z; + triangles.colors[4*triangles.cCounter + 3] = w; + + triangles.cCounter++; + + } break; + case RL_QUADS: + { + quads.colors[4*quads.cCounter] = x; + quads.colors[4*quads.cCounter + 1] = y; + quads.colors[4*quads.cCounter + 2] = z; + quads.colors[4*quads.cCounter + 3] = w; + + quads.cCounter++; + + } break; + default: break; + } +} + +// Define one vertex (color) +void rlColor4f(float r, float g, float b, float a) +{ + rlColor4ub((byte)(r*255), (byte)(g*255), (byte)(b*255), (byte)(a*255)); +} + +// Define one vertex (color) +void rlColor3f(float x, float y, float z) +{ + rlColor4ub((byte)(x*255), (byte)(y*255), (byte)(z*255), 255); +} + +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition - OpenGL equivalent functions (common to 1.1, 3.3+, ES2) +//---------------------------------------------------------------------------------- + +// Enable texture usage +void rlEnableTexture(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, id); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (draws[drawsCounter - 1].textureId != id) + { + if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; + + if (drawsCounter >= MAX_DRAWS_BY_TEXTURE) rlglDraw(); + + draws[drawsCounter - 1].textureId = id; + draws[drawsCounter - 1].vertexCount = 0; + } +#endif +} + +// Disable texture usage +void rlDisableTexture(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +#else + // NOTE: If quads batch limit is reached, + // we force a draw call and next batch starts + if (quads.vCounter/4 >= MAX_QUADS_BATCH) rlglDraw(); +#endif +} + +// Set texture parameters (wrap mode/filter mode) +void rlTextureParameters(unsigned int id, int param, int value) +{ + glBindTexture(GL_TEXTURE_2D, id); + + switch (param) + { + case RL_TEXTURE_WRAP_S: + case RL_TEXTURE_WRAP_T: + { + if ((value == RL_WRAP_CLAMP_MIRROR) && !texClampMirrorSupported) TraceLog(LOG_WARNING, "Clamp mirror wrap mode not supported"); + else glTexParameteri(GL_TEXTURE_2D, param, value); + } break; + case RL_TEXTURE_MAG_FILTER: + case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break; + case RL_TEXTURE_ANISOTROPIC_FILTER: + { + if (value <= maxAnisotropicLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + else if (maxAnisotropicLevel > 0.0f) + { + TraceLog(LOG_WARNING, "[TEX ID %i] Maximum anisotropic filter level supported is %iX", id, maxAnisotropicLevel); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + } + else TraceLog(LOG_WARNING, "Anisotropic filtering not supported"); + } break; + default: break; + } + + glBindTexture(GL_TEXTURE_2D, 0); +} + +// Enable rendering to texture (fbo) +void rlEnableRenderTexture(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindFramebuffer(GL_FRAMEBUFFER, id); + + //glDisable(GL_CULL_FACE); // Allow double side drawing for texture flipping + //glCullFace(GL_FRONT); +#endif +} + +// Disable rendering to texture +void rlDisableRenderTexture(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + //glEnable(GL_CULL_FACE); + //glCullFace(GL_BACK); +#endif +} + +// Enable depth test +void rlEnableDepthTest(void) +{ + glEnable(GL_DEPTH_TEST); +} + +// Disable depth test +void rlDisableDepthTest(void) +{ + glDisable(GL_DEPTH_TEST); +} + +// Enable wire mode +void rlEnableWireMode(void) +{ +#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#endif +} + +// Disable wire mode +void rlDisableWireMode(void) +{ +#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif +} + +// Unload texture from GPU memory +void rlDeleteTextures(unsigned int id) +{ + if (id > 0) glDeleteTextures(1, &id); +} + +// Unload render texture from GPU memory +void rlDeleteRenderTextures(RenderTexture2D target) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (target.id > 0) glDeleteFramebuffers(1, &target.id); + if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id); + if (target.depth.id > 0) glDeleteTextures(1, &target.depth.id); + + TraceLog(LOG_INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id); +#endif +} + +// Unload shader from GPU memory +void rlDeleteShader(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (id != 0) glDeleteProgram(id); +#endif +} + +// Unload vertex data (VAO) from GPU memory +void rlDeleteVertexArrays(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vaoSupported) + { + if (id != 0) glDeleteVertexArrays(1, &id); + TraceLog(LOG_INFO, "[VAO ID %i] Unloaded model data from VRAM (GPU)", id); + } +#endif +} + +// Unload vertex data (VBO) from GPU memory +void rlDeleteBuffers(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (id != 0) + { + glDeleteBuffers(1, &id); + if (!vaoSupported) TraceLog(LOG_INFO, "[VBO ID %i] Unloaded model vertex data from VRAM (GPU)", id); + } +#endif +} + +// Clear color buffer with color +void rlClearColor(byte r, byte g, byte b, byte a) +{ + // Color values clamp to 0.0f(0) and 1.0f(255) + float cr = (float)r/255; + float cg = (float)g/255; + float cb = (float)b/255; + float ca = (float)a/255; + + glClearColor(cr, cg, cb, ca); +} + +// Clear used screen buffers (color and depth) +void rlClearScreenBuffers(void) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers: Color and Depth (Depth is used for 3D) + //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Stencil buffer not used... +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - rlgl Functions +//---------------------------------------------------------------------------------- + +// Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states +void rlglInit(int width, int height) +{ + // Check OpenGL information and capabilities + //------------------------------------------------------------------------------ + + // Print current OpenGL and GLSL version + TraceLog(LOG_INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); + TraceLog(LOG_INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); + TraceLog(LOG_INFO, "GPU: Version: %s", glGetString(GL_VERSION)); + TraceLog(LOG_INFO, "GPU: GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + + // NOTE: We can get a bunch of extra information about GPU capabilities (glGet*) + //int maxTexSize; + //glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); + //TraceLog(LOG_INFO, "GL_MAX_TEXTURE_SIZE: %i", maxTexSize); + + //GL_MAX_TEXTURE_IMAGE_UNITS + //GL_MAX_VIEWPORT_DIMS + + //int numAuxBuffers; + //glGetIntegerv(GL_AUX_BUFFERS, &numAuxBuffers); + //TraceLog(LOG_INFO, "GL_AUX_BUFFERS: %i", numAuxBuffers); + + //GLint numComp = 0; + //GLint format[32] = { 0 }; + //glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); + //glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, format); + //for (int i = 0; i < numComp; i++) TraceLog(LOG_INFO, "Supported compressed format: 0x%x", format[i]); + + // NOTE: We don't need that much data on screen... right now... + +#if defined(GRAPHICS_API_OPENGL_11) + //TraceLog(LOG_INFO, "OpenGL 1.1 (or driver default) profile initialized"); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Get supported extensions list + GLint numExt = 0; + +#if defined(GRAPHICS_API_OPENGL_33) + + // NOTE: On OpenGL 3.3 VAO and NPOT are supported by default + vaoSupported = true; + texNPOTSupported = true; + texFloatSupported = true; + + // We get a list of available extensions and we check for some of them (compressed textures) + // NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that) + glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); + +#ifdef _MSC_VER + const char **extList = malloc(sizeof(const char *)*numExt); +#else + const char *extList[numExt]; +#endif + + for (int i = 0; i < numExt; i++) extList[i] = (char *)glGetStringi(GL_EXTENSIONS, i); + +#elif defined(GRAPHICS_API_OPENGL_ES2) + char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string + + // NOTE: We have to duplicate string because glGetString() returns a const value + // If not duplicated, it fails in some systems (Raspberry Pi) + // Equivalent to function: char *strdup(const char *str) + char *extensionsDup; + size_t len = strlen(extensions) + 1; + void *newstr = malloc(len); + if (newstr == NULL) extensionsDup = NULL; + extensionsDup = (char *)memcpy(newstr, extensions, len); + + // NOTE: String could be splitted using strtok() function (string.h) + // NOTE: strtok() modifies the received string, it can not be const + + char *extList[512]; // Allocate 512 strings pointers (2 KB) + + extList[numExt] = strtok(extensionsDup, " "); + + while (extList[numExt] != NULL) + { + numExt++; + extList[numExt] = strtok(NULL, " "); + } + + free(extensionsDup); // Duplicated string must be deallocated + + numExt -= 1; +#endif + + TraceLog(LOG_INFO, "Number of supported extensions: %i", numExt); + + // Show supported extensions + //for (int i = 0; i < numExt; i++) TraceLog(LOG_INFO, "Supported extension: %s", extList[i]); + + // Check required extensions + for (int i = 0; i < numExt; i++) + { +#if defined(GRAPHICS_API_OPENGL_ES2) + // Check VAO support + // NOTE: Only check on OpenGL ES, OpenGL 3.3 has VAO support as core feature + if (strcmp(extList[i], (const char *)"GL_OES_vertex_array_object") == 0) + { + vaoSupported = true; + + // The extension is supported by our hardware and driver, try to get related functions pointers + // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... + glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); + glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); + glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); + //glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); // NOTE: Fails in WebGL, omitted + } + + // Check NPOT textures support + // NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature + if (strcmp(extList[i], (const char *)"GL_OES_texture_npot") == 0) texNPOTSupported = true; + + // Check texture float support + if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) texFloatSupported = true; +#endif + + // DDS texture compression support + if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_s3tc") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBKIT_WEBGL_compressed_texture_s3tc") == 0)) texCompDXTSupported = true; + + // ETC1 texture compression support + if ((strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_etc1") == 0)) texCompETC1Supported = true; + + // ETC2/EAC texture compression support + if (strcmp(extList[i], (const char *)"GL_ARB_ES3_compatibility") == 0) texCompETC2Supported = true; + + // PVR texture compression support + if (strcmp(extList[i], (const char *)"GL_IMG_texture_compression_pvrtc") == 0) texCompPVRTSupported = true; + + // ASTC texture compression support + if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) texCompASTCSupported = true; + + // Anisotropic texture filter support + if (strcmp(extList[i], (const char *)"GL_EXT_texture_filter_anisotropic") == 0) + { + texAnisotropicFilterSupported = true; + glGetFloatv(0x84FF, &maxAnisotropicLevel); // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + } + + // Clamp mirror wrap mode supported + if (strcmp(extList[i], (const char *)"GL_EXT_texture_mirror_clamp") == 0) texClampMirrorSupported = true; + + // Debug marker support + if(strcmp(extList[i], (const char *)"GL_EXT_debug_marker") == 0) debugMarkerSupported = true; + } + +#if defined(_MSC_VER) + //free(extList); +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + if (vaoSupported) TraceLog(LOG_INFO, "[EXTENSION] VAO extension detected, VAO functions initialized successfully"); + else TraceLog(LOG_WARNING, "[EXTENSION] VAO extension not found, VAO usage not supported"); + + if (texNPOTSupported) TraceLog(LOG_INFO, "[EXTENSION] NPOT textures extension detected, full NPOT textures supported"); + else TraceLog(LOG_WARNING, "[EXTENSION] NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); +#endif + + if (texCompDXTSupported) TraceLog(LOG_INFO, "[EXTENSION] DXT compressed textures supported"); + if (texCompETC1Supported) TraceLog(LOG_INFO, "[EXTENSION] ETC1 compressed textures supported"); + if (texCompETC2Supported) TraceLog(LOG_INFO, "[EXTENSION] ETC2/EAC compressed textures supported"); + if (texCompPVRTSupported) TraceLog(LOG_INFO, "[EXTENSION] PVRT compressed textures supported"); + if (texCompASTCSupported) TraceLog(LOG_INFO, "[EXTENSION] ASTC compressed textures supported"); + + if (texAnisotropicFilterSupported) TraceLog(LOG_INFO, "[EXTENSION] Anisotropic textures filtering supported (max: %.0fX)", maxAnisotropicLevel); + if (texClampMirrorSupported) TraceLog(LOG_INFO, "[EXTENSION] Clamp mirror wrap texture mode supported"); + + if (debugMarkerSupported) TraceLog(LOG_INFO, "[EXTENSION] Debug Marker supported"); + + // Initialize buffers, default shaders and default textures + //---------------------------------------------------------- + + // Init default white texture + unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) + + whiteTexture = rlLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1); + + if (whiteTexture != 0) TraceLog(LOG_INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); + else TraceLog(LOG_WARNING, "Base white texture could not be loaded"); + + // Init default Shader (customized for GL 3.3 and ES2) + defaultShader = LoadShaderDefault(); + currentShader = defaultShader; + + // Init default vertex arrays buffers (lines, triangles, quads) + LoadBuffersDefault(); + + // Init temp vertex buffer, used when transformation required (translate, rotate, scale) + tempBuffer = (Vector3 *)malloc(sizeof(Vector3)*TEMP_VERTEX_BUFFER_SIZE); + + for (int i = 0; i < TEMP_VERTEX_BUFFER_SIZE; i++) tempBuffer[i] = Vector3Zero(); + + // Init draw calls tracking system + draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWS_BY_TEXTURE); + + for (int i = 0; i < MAX_DRAWS_BY_TEXTURE; i++) + { + draws[i].vertexCount = 0; + draws[i].vaoId = 0; + draws[i].shaderId = 0; + draws[i].textureId = 0; + + draws[i].projection = MatrixIdentity(); + draws[i].modelview = MatrixIdentity(); + } + + drawsCounter = 1; + draws[0].textureId = whiteTexture; // Set default draw texture id + currentDrawMode = RL_TRIANGLES; // Set default draw mode + + // Init internal matrix stack (emulating OpenGL 1.1) + for (int i = 0; i < MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); + + // Init internal projection and modelview matrices + projection = MatrixIdentity(); + modelview = MatrixIdentity(); + currentMatrix = &modelview; +#endif // defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + + // Initialize OpenGL default states + //---------------------------------------------------------- + + // Init state: Depth test + glDepthFunc(GL_LEQUAL); // Type of depth testing to apply + glDisable(GL_DEPTH_TEST); // Disable depth testing for 2D (only used for 3D) + + // Init state: Blending mode + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed) + glEnable(GL_BLEND); // Enable color blending (required to work with transparencies) + + // Init state: Culling + // NOTE: All shapes/models triangles are drawn CCW + glCullFace(GL_BACK); // Cull the back face (default) + glFrontFace(GL_CCW); // Front face are defined counter clockwise (default) + glEnable(GL_CULL_FACE); // Enable backface culling + +#if defined(GRAPHICS_API_OPENGL_11) + // Init state: Color hints (deprecated in OpenGL 3.0+) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation + glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) +#endif + + // Init state: Color/Depth buffers clear + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black) + glClearDepth(1.0f); // Set clear depth value (default) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers (depth buffer required for 3D) + + // Store screen size into global variables + screenWidth = width; + screenHeight = height; + + TraceLog(LOG_INFO, "OpenGL default states initialized successfully"); +} + +// Vertex Buffer Object deinitialization (memory free) +void rlglClose(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + UnloadShaderDefault(); // Unload default shader + UnloadBuffersDefault(); // Unload default buffers (lines, triangles, quads) + glDeleteTextures(1, &whiteTexture); // Unload default texture + + TraceLog(LOG_INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture); + + free(draws); + free(tempBuffer); +#endif +} + +// Drawing batches: triangles, quads, lines +void rlglDraw(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: In a future version, models could be stored in a stack... + //for (int i = 0; i < modelsCount; i++) rlDrawMesh(models[i]->mesh, models[i]->material, models[i]->transform); + + // NOTE: Default buffers upload and draw + UpdateBuffersDefault(); + DrawBuffersDefault(); // NOTE: Stereo rendering is checked inside +#endif +} + +// Returns current OpenGL version +int rlGetVersion(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + return OPENGL_11; +#elif defined(GRAPHICS_API_OPENGL_21) + #if defined(__APPLE__) + return OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX + #else + return OPENGL_21; + #endif +#elif defined(GRAPHICS_API_OPENGL_33) + return OPENGL_33; +#elif defined(GRAPHICS_API_OPENGL_ES2) + return OPENGL_ES_20; +#endif +} + +// Check internal buffer overflow for a given number of vertex +bool rlCheckBufferLimit(int type, int vCount) +{ + bool overflow = false; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + switch (type) + { + case RL_LINES: overflow = ((lines.vCounter + vCount)/2 >= MAX_LINES_BATCH); break; + case RL_TRIANGLES: overflow = ((triangles.vCounter + vCount)/3 >= MAX_TRIANGLES_BATCH); break; + case RL_QUADS: overflow = ((quads.vCounter + vCount)/4 >= MAX_QUADS_BATCH); break; + default: break; + } +#endif + return overflow; +} + +// Set debug marker +void rlSetDebugMarker(const char *text) +{ +#if defined(GRAPHICS_API_OPENGL_33) + if (debugMarkerSupported) glInsertEventMarkerEXT(0, text); +#endif +} + +// Load OpenGL extensions +// NOTE: External loader function could be passed as a pointer +void rlLoadExtensions(void *loader) +{ +#if defined(GRAPHICS_API_OPENGL_33) + // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions (and lower versions) + #if !defined(__APPLE__) + if (!gladLoadGLLoader((GLADloadproc)loader)) TraceLog(LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); + else TraceLog(LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); + + #if defined(GRAPHICS_API_OPENGL_21) + if (GLAD_GL_VERSION_2_1) TraceLog(LOG_INFO, "OpenGL 2.1 profile supported"); + #elif defined(GRAPHICS_API_OPENGL_33) + if(GLAD_GL_VERSION_3_3) TraceLog(LOG_INFO, "OpenGL 3.3 Core profile supported"); + else TraceLog(LOG_ERROR, "OpenGL 3.3 Core profile not supported"); + #endif + #endif + + // With GLAD, we can check if an extension is supported using the GLAD_GL_xxx booleans + //if (GLAD_GL_ARB_vertex_array_object) // Use GL_ARB_vertex_array_object +#endif +} + +// Get world coordinates from screen coordinates +Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view) +{ + Vector3 result = { 0.0f, 0.0f, 0.0f }; + + // Calculate unproject matrix (multiply view patrix by projection matrix) and invert it + Matrix matViewProj = MatrixMultiply(view, proj); + matViewProj = MatrixInvert(matViewProj); + + // Create quaternion from source point + Quaternion quat = { source.x, source.y, source.z, 1.0f }; + + // Multiply quat point by unproject matrix + quat = QuaternionTransform(quat, matViewProj); + + // Normalized world points in vectors + result.x = quat.x/quat.w; + result.y = quat.y/quat.w; + result.z = quat.z/quat.w; + + return result; +} + +// Convert image data to OpenGL texture (returns OpenGL valid Id) +unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount) +{ + glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding + + GLuint id = 0; + + // Check texture format support by OpenGL 1.1 (compressed textures not supported) +#if defined(GRAPHICS_API_OPENGL_11) + if (format >= COMPRESSED_DXT1_RGB) + { + TraceLog(LOG_WARNING, "OpenGL 1.1 does not support GPU compressed texture formats"); + return id; + } +#endif + + if ((!texCompDXTSupported) && ((format == COMPRESSED_DXT1_RGB) || (format == COMPRESSED_DXT1_RGBA) || + (format == COMPRESSED_DXT3_RGBA) || (format == COMPRESSED_DXT5_RGBA))) + { + TraceLog(LOG_WARNING, "DXT compressed texture format not supported"); + return id; + } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((!texCompETC1Supported) && (format == COMPRESSED_ETC1_RGB)) + { + TraceLog(LOG_WARNING, "ETC1 compressed texture format not supported"); + return id; + } + + if ((!texCompETC2Supported) && ((format == COMPRESSED_ETC2_RGB) || (format == COMPRESSED_ETC2_EAC_RGBA))) + { + TraceLog(LOG_WARNING, "ETC2 compressed texture format not supported"); + return id; + } + + if ((!texCompPVRTSupported) && ((format == COMPRESSED_PVRT_RGB) || (format == COMPRESSED_PVRT_RGBA))) + { + TraceLog(LOG_WARNING, "PVRT compressed texture format not supported"); + return id; + } + + if ((!texCompASTCSupported) && ((format == COMPRESSED_ASTC_4x4_RGBA) || (format == COMPRESSED_ASTC_8x8_RGBA))) + { + TraceLog(LOG_WARNING, "ASTC compressed texture format not supported"); + return id; + } +#endif + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glGenTextures(1, &id); // Generate Pointer to the texture + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + //glActiveTexture(GL_TEXTURE0); // If not defined, using GL_TEXTURE0 by default (shader texture) +#endif + + glBindTexture(GL_TEXTURE_2D, id); + + int mipWidth = width; + int mipHeight = height; + int mipOffset = 0; // Mipmap data offset + + TraceLog(LOG_DEBUG, "Load texture from data memory address: 0x%x", data); + + // Load the different mipmap levels + for (int i = 0; i < mipmapCount; i++) + { + unsigned int mipSize = GetPixelDataSize(mipWidth, mipHeight, format); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + TraceLog(LOG_DEBUG, "Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); + + if (glInternalFormat != -1) + { + if (format < COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, (unsigned char *)data + mipOffset); + #if !defined(GRAPHICS_API_OPENGL_11) + else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, (unsigned char *)data + mipOffset); + #endif + + #if defined(GRAPHICS_API_OPENGL_33) + if (format == UNCOMPRESSED_GRAYSCALE) + { + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + else if (format == UNCOMPRESSED_GRAY_ALPHA) + { + #if defined(GRAPHICS_API_OPENGL_21) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; + #elif defined(GRAPHICS_API_OPENGL_33) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; + #endif + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + #endif + } + + mipWidth /= 2; + mipHeight /= 2; + mipOffset += mipSize; + + // Security check for NPOT textures + if (mipWidth < 1) mipWidth = 1; + if (mipHeight < 1) mipHeight = 1; + } + + // Texture parameters configuration + // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used +#if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used + if (texNPOTSupported) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis + } + else + { + // NOTE: If using negative texture coordinates (LoadOBJ()), it does not work! + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis + } +#else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis +#endif + + // Magnification and minification filters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Alternative: GL_LINEAR + +#if defined(GRAPHICS_API_OPENGL_33) + if (mipmapCount > 1) + { + // Activate Trilinear filtering if mipmaps are available + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } +#endif + + // At this point we have the texture loaded in GPU and texture parameters configured + + // NOTE: If mipmaps were not in data, they are not generated automatically + + // Unbind current texture + glBindTexture(GL_TEXTURE_2D, 0); + + if (id > 0) TraceLog(LOG_INFO, "[TEX ID %i] Texture created successfully (%ix%i - %i mipmaps)", id, width, height, mipmapCount); + else TraceLog(LOG_WARNING, "Texture could not be created"); + + return id; +} + +// Update already loaded texture in GPU with new data +void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data) +{ + glBindTexture(GL_TEXTURE_2D, id); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + if ((glInternalFormat != -1) && (format < COMPRESSED_DXT1_RGB)) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, glFormat, glType, (unsigned char *)data); + } + else TraceLog(LOG_WARNING, "Texture format updating not supported"); +} + +// Get OpenGL internal formats and data type from raylib PixelFormat +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType) +{ + *glInternalFormat = -1; + *glFormat = -1; + *glType = -1; + + switch (format) + { + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + #if !defined(GRAPHICS_API_OPENGL_11) + case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + #endif + #elif defined(GRAPHICS_API_OPENGL_33) + case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; + case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; + case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + #endif + #if !defined(GRAPHICS_API_OPENGL_11) + case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; + case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + #endif + default: TraceLog(LOG_WARNING, "Texture format not supported"); break; + } +} + +// Unload texture from GPU memory +void rlUnloadTexture(unsigned int id) +{ + if (id > 0) glDeleteTextures(1, &id); +} + +// Load a texture to be used for rendering (fbo with color and depth attachments) +RenderTexture2D rlLoadRenderTexture(int width, int height) +{ + RenderTexture2D target; + + target.id = 0; + + target.texture.id = 0; + target.texture.width = width; + target.texture.height = height; + target.texture.format = UNCOMPRESSED_R8G8B8A8; + target.texture.mipmaps = 1; + + target.depth.id = 0; + target.depth.width = width; + target.depth.height = height; + target.depth.format = 19; //DEPTH_COMPONENT_24BIT + target.depth.mipmaps = 1; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Create the texture that will serve as the color attachment for the framebuffer + glGenTextures(1, &target.texture.id); + glBindTexture(GL_TEXTURE_2D, target.texture.id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + +#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + #define USE_DEPTH_RENDERBUFFER +#else + #define USE_DEPTH_TEXTURE +#endif + +#if defined(USE_DEPTH_RENDERBUFFER) + // Create the renderbuffer that will serve as the depth attachment for the framebuffer. + glGenRenderbuffers(1, &target.depth.id); + glBindRenderbuffer(GL_RENDERBUFFER, target.depth.id); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); // GL_DEPTH_COMPONENT24 not supported on Android +#elif defined(USE_DEPTH_TEXTURE) + // NOTE: We can also use a texture for depth buffer (GL_ARB_depth_texture/GL_OES_depth_texture extension required) + // A renderbuffer is simpler than a texture and could offer better performance on embedded devices + glGenTextures(1, &target.depth.id); + glBindTexture(GL_TEXTURE_2D, target.depth.id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + glBindTexture(GL_TEXTURE_2D, 0); +#endif + + // Create the framebuffer object + glGenFramebuffers(1, &target.id); + glBindFramebuffer(GL_FRAMEBUFFER, target.id); + + // Attach color texture and depth renderbuffer to FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target.texture.id, 0); +#if defined(USE_DEPTH_RENDERBUFFER) + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, target.depth.id); +#elif defined(USE_DEPTH_TEXTURE) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, target.depth.id, 0); +#endif + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + TraceLog(LOG_WARNING, "Framebuffer object could not be created..."); + + switch (status) + { + case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(LOG_WARNING, "Framebuffer is unsupported"); break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TraceLog(LOG_WARNING, "Framebuffer incomplete attachment"); break; +#if defined(GRAPHICS_API_OPENGL_ES2) + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TraceLog(LOG_WARNING, "Framebuffer incomplete dimensions"); break; +#endif + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TraceLog(LOG_WARNING, "Framebuffer incomplete missing attachment"); break; + default: break; + } + + glDeleteTextures(1, &target.texture.id); + glDeleteTextures(1, &target.depth.id); + glDeleteFramebuffers(1, &target.id); + } + else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + + return target; +} + +// Generate mipmap data for selected texture +void rlGenerateMipmaps(Texture2D *texture) +{ + glBindTexture(GL_TEXTURE_2D, texture->id); + + // Check if texture is power-of-two (POT) + bool texIsPOT = false; + + if (((texture->width > 0) && ((texture->width & (texture->width - 1)) == 0)) && + ((texture->height > 0) && ((texture->height & (texture->height - 1)) == 0))) texIsPOT = true; + + if ((texIsPOT) || (texNPOTSupported)) + { +#if defined(GRAPHICS_API_OPENGL_11) + // WARNING: Manual mipmap generation only works for RGBA 32bit textures! + if (texture->format == UNCOMPRESSED_R8G8B8A8) + { + // Retrieve texture data from VRAM + void *data = rlReadTexturePixels(*texture); + + // NOTE: data size is reallocated to fit mipmaps data + // NOTE: CPU mipmap generation only supports RGBA 32bit data + int mipmapCount = GenerateMipmaps(data, texture->width, texture->height); + + int size = texture->width*texture->height*4; + int offset = size; + + int mipWidth = texture->width/2; + int mipHeight = texture->height/2; + + // Load the mipmaps + for (int level = 1; level < mipmapCount; level++) + { + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data + offset); + + size = mipWidth*mipHeight*4; + offset += size; + + mipWidth /= 2; + mipHeight /= 2; + } + + texture->mipmaps = mipmapCount + 1; + free(data); // Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data + + TraceLog(LOG_WARNING, "[TEX ID %i] Mipmaps [%i] generated manually on CPU side", texture->id, texture->mipmaps); + } + else TraceLog(LOG_WARNING, "[TEX ID %i] Mipmaps could not be generated for texture format", texture->id); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorythm: GL_FASTEST, GL_NICEST, GL_DONT_CARE + glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically + TraceLog(LOG_INFO, "[TEX ID %i] Mipmaps generated automatically", texture->id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps + + #define MIN(a,b) (((a)<(b))?(a):(b)) + #define MAX(a,b) (((a)>(b))?(a):(b)) + + texture->mipmaps = 1 + (int)floor(log(MAX(texture->width, texture->height))/log(2)); +#endif + } + else TraceLog(LOG_WARNING, "[TEX ID %i] Mipmaps can not be generated", texture->id); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +// Upload vertex data into a VAO (if supported) and VBO +// TODO: Check if mesh has already been loaded in GPU +void rlLoadMesh(Mesh *mesh, bool dynamic) +{ + mesh->vaoId = 0; // Vertex Array Object + mesh->vboId[0] = 0; // Vertex positions VBO + mesh->vboId[1] = 0; // Vertex texcoords VBO + mesh->vboId[2] = 0; // Vertex normals VBO + mesh->vboId[3] = 0; // Vertex colors VBO + mesh->vboId[4] = 0; // Vertex tangents VBO + mesh->vboId[5] = 0; // Vertex texcoords2 VBO + mesh->vboId[6] = 0; // Vertex indices VBO + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int drawHint = GL_STATIC_DRAW; + if (dynamic) drawHint = GL_DYNAMIC_DRAW; + + if (vaoSupported) + { + // Initialize Quads VAO (Buffer A) + glGenVertexArrays(1, &mesh->vaoId); + glBindVertexArray(mesh->vaoId); + } + + // NOTE: Attributes must be uploaded considering default locations points + + // Enable vertex attributes: position (shader-location = 0) + glGenBuffers(1, &mesh->vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->vertices, drawHint); + glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(0); + + // Enable vertex attributes: texcoords (shader-location = 1) + glGenBuffers(1, &mesh->vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords, drawHint); + glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(1); + + // Enable vertex attributes: normals (shader-location = 2) + if (mesh->normals != NULL) + { + glGenBuffers(1, &mesh->vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->normals, drawHint); + glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(2); + } + else + { + // Default color vertex attribute set to WHITE + glVertexAttrib3f(2, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(2); + } + + // Default color vertex attribute (shader-location = 3) + if (mesh->colors != NULL) + { + glGenBuffers(1, &mesh->vboId[3]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[3]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh->vertexCount, mesh->colors, drawHint); + glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(3); + } + else + { + // Default color vertex attribute set to WHITE + glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(3); + } + + // Default tangent vertex attribute (shader-location = 4) + if (mesh->tangents != NULL) + { + glGenBuffers(1, &mesh->vboId[4]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[4]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*mesh->vertexCount, mesh->tangents, drawHint); + glVertexAttribPointer(4, 4, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(4); + } + else + { + // Default tangents vertex attribute + glVertexAttrib4f(4, 0.0f, 0.0f, 0.0f, 0.0f); + glDisableVertexAttribArray(4); + } + + // Default texcoord2 vertex attribute (shader-location = 5) + if (mesh->texcoords2 != NULL) + { + glGenBuffers(1, &mesh->vboId[5]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[5]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords2, drawHint); + glVertexAttribPointer(5, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(5); + } + else + { + // Default texcoord2 vertex attribute + glVertexAttrib2f(5, 0.0f, 0.0f); + glDisableVertexAttribArray(5); + } + + if (mesh->indices != NULL) + { + glGenBuffers(1, &mesh->vboId[6]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->vboId[6]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*mesh->triangleCount*3, mesh->indices, GL_STATIC_DRAW); + } + + if (vaoSupported) + { + if (mesh->vaoId > 0) TraceLog(LOG_INFO, "[VAO ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId); + else TraceLog(LOG_WARNING, "Mesh could not be uploaded to VRAM (GPU)"); + } + else + { + TraceLog(LOG_INFO, "[VBOs] Mesh uploaded successfully to VRAM (GPU)"); + } +#endif +} + +// Update vertex data on GPU (upload new data to one buffer) +void rlUpdateMesh(Mesh mesh, int buffer, int numVertex) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Activate mesh VAO + if (vaoSupported) glBindVertexArray(mesh.vaoId); + + switch (buffer) + { + case 0: // Update vertices (vertex position) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.vertices, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.vertices); + + } break; + case 1: // Update texcoords (vertex texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords); + + } break; + case 2: // Update normals (vertex normals) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.normals, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.normals); + + } break; + case 3: // Update colors (vertex colors) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*numVertex, mesh.colors, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*numVertex, mesh.colors); + + } break; + case 4: // Update tangents (vertex tangents) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*numVertex, mesh.tangents, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*4*numVertex, mesh.tangents); + } break; + case 5: // Update texcoords2 (vertex second texture coordinates) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); + if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords2, GL_DYNAMIC_DRAW); + else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords2); + } break; + default: break; + } + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); + + // Another option would be using buffer mapping... + //mesh.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // Now we can modify vertices + //glUnmapBuffer(GL_ARRAY_BUFFER); +#endif +} + +// Draw a 3d mesh with material and transform +void rlDrawMesh(Mesh mesh, Material material, Matrix transform) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, material.maps[MAP_DIFFUSE].texture.id); + + // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model + glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array + glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array + if (mesh.normals != NULL) glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array + if (mesh.colors != NULL) glEnableClientState(GL_COLOR_ARRAY); // Enable colors array + + glVertexPointer(3, GL_FLOAT, 0, mesh.vertices); // Pointer to vertex coords array + glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords); // Pointer to texture coords array + if (mesh.normals != NULL) glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array + if (mesh.colors != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array + + rlPushMatrix(); + rlMultMatrixf(MatrixToFloat(transform)); + rlColor4ub(material.maps[MAP_DIFFUSE].color.r, material.maps[MAP_DIFFUSE].color.g, material.maps[MAP_DIFFUSE].color.b, material.maps[MAP_DIFFUSE].color.a); + + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, mesh.indices); + else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + rlPopMatrix(); + + glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array + glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array + if (mesh.normals != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array + if (mesh.colors != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable colors array + + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Bind shader program + glUseProgram(material.shader.id); + + // Matrices and other values required by shader + //----------------------------------------------------- + // Calculate and send to shader model matrix (used by PBR shader) + if (material.shader.locs[LOC_MATRIX_MODEL] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_MODEL], transform); + + // Upload to shader material.colDiffuse + if (material.shader.locs[LOC_COLOR_DIFFUSE] != -1) + glUniform4f(material.shader.locs[LOC_COLOR_DIFFUSE], (float)material.maps[MAP_DIFFUSE].color.r/255.0f, + (float)material.maps[MAP_DIFFUSE].color.g/255.0f, + (float)material.maps[MAP_DIFFUSE].color.b/255.0f, + (float)material.maps[MAP_DIFFUSE].color.a/255.0f); + + // Upload to shader material.colSpecular (if available) + if (material.shader.locs[LOC_COLOR_SPECULAR] != -1) + glUniform4f(material.shader.locs[LOC_COLOR_SPECULAR], (float)material.maps[MAP_SPECULAR].color.r/255.0f, + (float)material.maps[MAP_SPECULAR].color.g/255.0f, + (float)material.maps[MAP_SPECULAR].color.b/255.0f, + (float)material.maps[MAP_SPECULAR].color.a/255.0f); + + if (material.shader.locs[LOC_MATRIX_VIEW] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_VIEW], modelview); + if (material.shader.locs[LOC_MATRIX_PROJECTION] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_PROJECTION], projection); + + // At this point the modelview matrix just contains the view matrix (camera) + // That's because BeginMode3D() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() + Matrix matView = modelview; // View matrix (camera) + Matrix matProjection = projection; // Projection matrix (perspective) + + // Calculate model-view matrix combining matModel and matView + Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates + //----------------------------------------------------- + + // Bind active texture maps (if available) + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + { + if (material.maps[i].texture.id > 0) + { + glActiveTexture(GL_TEXTURE0 + i); + if ((i == MAP_IRRADIANCE) || (i == MAP_PREFILTER) || (i == MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, material.maps[i].texture.id); + else glBindTexture(GL_TEXTURE_2D, material.maps[i].texture.id); + + glUniform1i(material.shader.locs[LOC_MAP_DIFFUSE + i], i); + } + } + + // Bind vertex array objects (or VBOs) + if (vaoSupported) glBindVertexArray(mesh.vaoId); + else + { + // TODO: Simplify VBO binding into a for loop + + // Bind mesh VBO data: vertex position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_POSITION]); + + // Bind mesh VBO data: vertex texcoords (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TEXCOORD01]); + + // Bind mesh VBO data: vertex normals (shader-location = 2, if available) + if (material.shader.locs[LOC_VERTEX_NORMAL] != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_NORMAL]); + } + + // Bind mesh VBO data: vertex colors (shader-location = 3, if available) + if (material.shader.locs[LOC_VERTEX_COLOR] != -1) + { + if (mesh.vboId[3] != 0) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_COLOR]); + } + else + { + // Set default value for unused attribute + // NOTE: Required when using default shader and no VAO support + glVertexAttrib4f(material.shader.locs[LOC_VERTEX_COLOR], 1.0f, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(material.shader.locs[LOC_VERTEX_COLOR]); + } + } + + // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) + if (material.shader.locs[LOC_VERTEX_TANGENT] != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TANGENT], 4, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TANGENT]); + } + + // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) + if (material.shader.locs[LOC_VERTEX_TEXCOORD02] != -1) + { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TEXCOORD02], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TEXCOORD02]); + } + + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vboId[6]); + } + + int eyesCount = 1; +#if defined(SUPPORT_VR_SIMULATOR) + if (vrStereoRender) eyesCount = 2; +#endif + + for (int eye = 0; eye < eyesCount; eye++) + { + if (eyesCount == 1) modelview = matModelView; + #if defined(SUPPORT_VR_SIMULATOR) + else SetStereoView(eye, matProjection, matModelView); + #endif + + // Calculate model-view-projection matrix (MVP) + Matrix matMVP = MatrixMultiply(modelview, projection); // Transform to screen-space coordinates + + // Send combined model-view-projection matrix to shader + glUniformMatrix4fv(material.shader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); + + // Draw call! + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0); // Indexed vertices draw + else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); + } + + // Unbind all binded texture maps + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + { + glActiveTexture(GL_TEXTURE0 + i); // Set shader active texture + if ((i == MAP_IRRADIANCE) || (i == MAP_PREFILTER) || (i == MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + else glBindTexture(GL_TEXTURE_2D, 0); // Unbind current active texture + } + + // Unind vertex array objects (or VBOs) + if (vaoSupported) glBindVertexArray(0); + else + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + // Unbind shader program + glUseProgram(0); + + // Restore projection/modelview matrices + // NOTE: In stereo rendering matrices are being modified to fit every eye + projection = matProjection; + modelview = matView; +#endif +} + +// Unload mesh data from CPU and GPU +void rlUnloadMesh(Mesh *mesh) +{ + if (mesh->vertices != NULL) free(mesh->vertices); + if (mesh->texcoords != NULL) free(mesh->texcoords); + if (mesh->normals != NULL) free(mesh->normals); + if (mesh->colors != NULL) free(mesh->colors); + if (mesh->tangents != NULL) free(mesh->tangents); + if (mesh->texcoords2 != NULL) free(mesh->texcoords2); + if (mesh->indices != NULL) free(mesh->indices); + + if (mesh->baseVertices != NULL) free(mesh->baseVertices); + if (mesh->baseNormals != NULL) free(mesh->baseNormals); + if (mesh->weightBias != NULL) free(mesh->weightBias); + if (mesh->weightId != NULL) free(mesh->weightId); + + rlDeleteBuffers(mesh->vboId[0]); // vertex + rlDeleteBuffers(mesh->vboId[1]); // texcoords + rlDeleteBuffers(mesh->vboId[2]); // normals + rlDeleteBuffers(mesh->vboId[3]); // colors + rlDeleteBuffers(mesh->vboId[4]); // tangents + rlDeleteBuffers(mesh->vboId[5]); // texcoords2 + rlDeleteBuffers(mesh->vboId[6]); // indices + + rlDeleteVertexArrays(mesh->vaoId); +} + +// Read screen pixel data (color buffer) +unsigned char *rlReadScreenPixels(int width, int height) +{ + unsigned char *screenData = (unsigned char *)calloc(width*height*4, sizeof(unsigned char)); + + // NOTE: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData); + + // Flip image vertically! + unsigned char *imgData = (unsigned char *)malloc(width*height*sizeof(unsigned char)*4); + + for (int y = height - 1; y >= 0; y--) + { + for (int x = 0; x < (width*4); x++) + { + // Flip line + imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; + + // Set alpha component value to 255 (no trasparent image retrieval) + // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it! + if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255; + } + } + + free(screenData); + + return imgData; // NOTE: image data should be freed +} + +// Read texture pixel data +// NOTE: glGetTexImage() is not available on OpenGL ES 2.0 +// Texture2D width and height are required on OpenGL ES 2.0. There is no way to get it from texture id. +void *rlReadTexturePixels(Texture2D texture) +{ + void *pixels = NULL; + +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + glBindTexture(GL_TEXTURE_2D, texture.id); + + // NOTE: Using texture.id, we can retrieve some texture info (but not on OpenGL ES 2.0) + /* + int width, height, format; + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); + // Other texture info: GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE + */ + + // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding. + // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting. + // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) + // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(texture.format, &glInternalFormat, &glFormat, &glType); + unsigned int size = GetPixelDataSize(texture.width, texture.height, texture.format); + + if ((glInternalFormat != -1) && (texture.format < COMPRESSED_DXT1_RGB)) + { + pixels = (unsigned char *)malloc(size); + glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); + } + else TraceLog(LOG_WARNING, "Texture data retrieval not suported for pixel format"); + + glBindTexture(GL_TEXTURE_2D, 0); +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + RenderTexture2D fbo = rlLoadRenderTexture(texture.width, texture.height); + + // NOTE: Two possible Options: + // 1 - Bind texture to color fbo attachment and glReadPixels() + // 2 - Create an fbo, activate it, render quad with texture, glReadPixels() + +#define GET_TEXTURE_FBO_OPTION_1 // It works +#if defined(GET_TEXTURE_FBO_OPTION_1) + glBindFramebuffer(GL_FRAMEBUFFER, fbo.id); + glBindTexture(GL_TEXTURE_2D, 0); + + // Attach our texture to FBO -> Texture must be RGBA + // NOTE: Previoust attached texture is automatically detached + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.id, 0); + + pixels = (unsigned char *)malloc(texture.width*texture.height*4*sizeof(unsigned char)); + + // NOTE: We read data as RGBA because FBO texture is configured as RGBA, despite binding a RGB texture... + glReadPixels(0, 0, texture.width, texture.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Re-attach internal FBO color texture before deleting it + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.texture.id, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + +#elif defined(GET_TEXTURE_FBO_OPTION_2) + // Render texture to fbo + glBindFramebuffer(GL_FRAMEBUFFER, fbo.id); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearDepthf(1.0f); + //glDisable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); + //glDisable(GL_BLEND); + + glViewport(0, 0, texture.width, texture.height); + rlOrtho(0.0, texture.width, texture.height, 0.0, 0.0, 1.0); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glUseProgram(GetShaderDefault().id); + glBindTexture(GL_TEXTURE_2D, texture.id); + GenDrawQuad(); + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); + + pixels = (unsigned char *)malloc(texture.width*texture.height*4*sizeof(unsigned char)); + + glReadPixels(0, 0, texture.width, texture.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Bind framebuffer 0, which means render to back buffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, screenWidth, screenHeight); + +#endif // GET_TEXTURE_FBO_OPTION + + // Clean up temporal fbo + rlDeleteRenderTextures(fbo); + +#endif + + return pixels; +} + +/* +// TODO: Record draw calls to be processed in batch +// NOTE: Global state must be kept +void rlRecordDraw(void) +{ + // TODO: Before adding a new draw, check if anything changed from last stored draw +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + draws[drawsCounter].vertexCount = currentState.vertexCount; + draws[drawsCounter].vaoId = currentState.vaoId; // lines.id, trangles.id, quads.id? + draws[drawsCounter].textureId = currentState.textureId; // whiteTexture? + draws[drawsCounter].shaderId = currentState.shaderId; // defaultShader.id + draws[drawsCounter].projection = projection; + draws[drawsCounter].modelview = modelview; + + drawsCounter++; +#endif +} +*/ + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Shaders Functions +// NOTE: Those functions are exposed directly to the user in raylib.h +//---------------------------------------------------------------------------------- + +// Get default internal texture (white texture) +Texture2D GetTextureDefault(void) +{ + Texture2D texture; + + texture.id = whiteTexture; + texture.width = 1; + texture.height = 1; + texture.mipmaps = 1; + texture.format = UNCOMPRESSED_R8G8B8A8; + + return texture; +} + +// Get default shader +Shader GetShaderDefault(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + return defaultShader; +#else + Shader shader = { 0 }; + return shader; +#endif +} + +// Load text data from file +// NOTE: text chars array should be freed manually +char *LoadText(const char *fileName) +{ + FILE *textFile; + char *text = NULL; + + int count = 0; + + if (fileName != NULL) + { + textFile = fopen(fileName,"rt"); + + if (textFile != NULL) + { + fseek(textFile, 0, SEEK_END); + count = ftell(textFile); + rewind(textFile); + + if (count > 0) + { + text = (char *)malloc(sizeof(char)*(count + 1)); + count = fread(text, sizeof(char), count, textFile); + text[count] = '\0'; + } + + fclose(textFile); + } + else TraceLog(LOG_WARNING, "[%s] Text file could not be opened", fileName); + } + + return text; +} + +// Load shader from files and bind default locations +// NOTE: If shader string is NULL, using default vertex/fragment shaders +Shader LoadShader(const char *vsFileName, const char *fsFileName) +{ + Shader shader = { 0 }; + + char *vShaderStr = NULL; + char *fShaderStr = NULL; + + if (vsFileName != NULL) vShaderStr = LoadText(vsFileName); + if (fsFileName != NULL) fShaderStr = LoadText(fsFileName); + + shader = LoadShaderCode(vShaderStr, fShaderStr); + + if (vShaderStr != NULL) free(vShaderStr); + if (fShaderStr != NULL) free(fShaderStr); + + return shader; +} + +// Load shader from code strings +// NOTE: If shader string is NULL, using default vertex/fragment shaders +Shader LoadShaderCode(char *vsCode, char *fsCode) +{ + Shader shader = { 0 }; + + // NOTE: All locations must be reseted to -1 (no location) + for (int i = 0; i < MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int vertexShaderId = defaultVShaderId; + unsigned int fragmentShaderId = defaultFShaderId; + + if (vsCode != NULL) vertexShaderId = CompileShader(vsCode, GL_VERTEX_SHADER); + if (fsCode != NULL) fragmentShaderId = CompileShader(fsCode, GL_FRAGMENT_SHADER); + + if ((vertexShaderId == defaultVShaderId) && (fragmentShaderId == defaultFShaderId)) shader = defaultShader; + else + { + shader.id = LoadShaderProgram(vertexShaderId, fragmentShaderId); + + if (vertexShaderId != defaultVShaderId) glDeleteShader(vertexShaderId); + if (fragmentShaderId != defaultFShaderId) glDeleteShader(fragmentShaderId); + + if (shader.id == 0) + { + TraceLog(LOG_WARNING, "Custom shader could not be loaded"); + shader = defaultShader; + } + + // After shader loading, we TRY to set default location names + if (shader.id > 0) SetShaderDefaultLocations(&shader); + } + + // Get available shader uniforms + // NOTE: This information is useful for debug... + int uniformCount = -1; + + glGetProgramiv(shader.id, GL_ACTIVE_UNIFORMS, &uniformCount); + + for(int i = 0; i < uniformCount; i++) + { + int namelen = -1; + int num = -1; + char name[256]; // Assume no variable names longer than 256 + GLenum type = GL_ZERO; + + // Get the name of the uniforms + glGetActiveUniform(shader.id, i,sizeof(name) - 1, &namelen, &num, &type, name); + + name[namelen] = 0; + + // Get the location of the named uniform + GLuint location = glGetUniformLocation(shader.id, name); + + TraceLog(LOG_DEBUG, "[SHDR ID %i] Active uniform [%s] set at location: %i", shader.id, name, location); + } +#endif + + return shader; +} + +// Unload shader from GPU memory (VRAM) +void UnloadShader(Shader shader) +{ + if (shader.id > 0) + { + rlDeleteShader(shader.id); + TraceLog(LOG_INFO, "[SHDR ID %i] Unloaded shader program data", shader.id); + } +} + +// Begin custom shader mode +void BeginShaderMode(Shader shader) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (currentShader.id != shader.id) + { + rlglDraw(); + currentShader = shader; + } +#endif +} + +// End custom shader mode (returns to default shader) +void EndShaderMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + BeginShaderMode(defaultShader); +#endif +} + +// Get shader uniform location +int GetShaderLocation(Shader shader, const char *uniformName) +{ + int location = -1; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + location = glGetUniformLocation(shader.id, uniformName); + + if (location == -1) TraceLog(LOG_WARNING, "[SHDR ID %i] Shader uniform [%s] COULD NOT BE FOUND", shader.id, uniformName); + else TraceLog(LOG_INFO, "[SHDR ID %i] Shader uniform [%s] set at location: %i", shader.id, uniformName, location); +#endif + return location; +} + +// Set shader uniform value (float) +void SetShaderValue(Shader shader, int uniformLoc, const float *value, int size) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(shader.id); + + if (size == 1) glUniform1fv(uniformLoc, 1, value); // Shader uniform type: float + else if (size == 2) glUniform2fv(uniformLoc, 1, value); // Shader uniform type: vec2 + else if (size == 3) glUniform3fv(uniformLoc, 1, value); // Shader uniform type: vec3 + else if (size == 4) glUniform4fv(uniformLoc, 1, value); // Shader uniform type: vec4 + else TraceLog(LOG_WARNING, "Shader value float array size not supported"); + + //glUseProgram(0); // Avoid reseting current shader program, in case other uniforms are set +#endif +} + +// Set shader uniform value (int) +void SetShaderValuei(Shader shader, int uniformLoc, const int *value, int size) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(shader.id); + + if (size == 1) glUniform1iv(uniformLoc, 1, value); // Shader uniform type: int + else if (size == 2) glUniform2iv(uniformLoc, 1, value); // Shader uniform type: ivec2 + else if (size == 3) glUniform3iv(uniformLoc, 1, value); // Shader uniform type: ivec3 + else if (size == 4) glUniform4iv(uniformLoc, 1, value); // Shader uniform type: ivec4 + else TraceLog(LOG_WARNING, "Shader value int array size not supported"); + + //glUseProgram(0); +#endif +} + +// Set shader uniform value (matrix 4x4) +void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glUseProgram(shader.id); + + glUniformMatrix4fv(uniformLoc, 1, false, MatrixToFloat(mat)); + + //glUseProgram(0); +#endif +} + +// Set a custom projection matrix (replaces internal projection matrix) +void SetMatrixProjection(Matrix proj) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + projection = proj; +#endif +} + +// Set a custom modelview matrix (replaces internal modelview matrix) +void SetMatrixModelview(Matrix view) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + modelview = view; +#endif +} + +// Return internal modelview matrix +Matrix GetMatrixModelview() +{ + Matrix matrix = MatrixIdentity(); +#if defined(GRAPHICS_API_OPENGL_11) + float mat[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, mat); +#else + matrix = modelview; +#endif + return matrix; +} + +// Generate cubemap texture from HDR texture +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) +{ + Texture2D cubemap = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader + // Other locations should be setup externally in shader before calling the function + + // Set up depth face culling and cubemap seamless + glDisable(GL_CULL_FACE); +#if defined(GRAPHICS_API_OPENGL_33) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Flag not supported on OpenGL ES 2.0 +#endif + + + // Setup framebuffer + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + // Set up cubemap to render and attach to framebuffer + // NOTE: faces are stored with 16 bit floating point values + glGenTextures(1, &cubemap.id); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + for (unsigned int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#if defined(GRAPHICS_API_OPENGL_33) + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Flag not supported on OpenGL ES 2.0 +#endif + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Create projection (transposed) and different views for each face + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); + //MatrixTranspose(&fboProjection); + Matrix fboViews[6] = { + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }) + }; + + // Convert HDR equirectangular environment map to cubemap equivalent + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, skyHDR.id); + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_PROJECTION], fboProjection); + + // Note: don't forget to configure the viewport to the capture dimensions + glViewport(0, 0, size, size); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + for (unsigned int i = 0; i < 6; i++) + { + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap.id, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawCube(); + } + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, screenWidth, screenHeight); + //glEnable(GL_CULL_FACE); + + cubemap.width = size; + cubemap.height = size; +#endif + return cubemap; +} + +// Generate irradiance texture using cubemap data +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size) +{ + Texture2D irradiance = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader + // Other locations should be setup externally in shader before calling the function + + // Setup framebuffer + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + // Create an irradiance cubemap, and re-scale capture FBO to irradiance scale + glGenTextures(1, &irradiance.id); + glBindTexture(GL_TEXTURE_CUBE_MAP, irradiance.id); + for (unsigned int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Create projection (transposed) and different views for each face + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); + //MatrixTranspose(&fboProjection); + Matrix fboViews[6] = { + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }) + }; + + // Solve diffuse integral by convolution to create an irradiance cubemap + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_PROJECTION], fboProjection); + + // Note: don't forget to configure the viewport to the capture dimensions + glViewport(0, 0, size, size); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + for (unsigned int i = 0; i < 6; i++) + { + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradiance.id, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawCube(); + } + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, screenWidth, screenHeight); + + irradiance.width = size; + irradiance.height = size; +#endif + return irradiance; +} + +// Generate prefilter texture using cubemap data +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) +{ + Texture2D prefilter = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader + // Other locations should be setup externally in shader before calling the function + // TODO: Locations should be taken out of this function... too shader dependant... + int roughnessLoc = GetShaderLocation(shader, "roughness"); + + // Setup framebuffer + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + // Create a prefiltered HDR environment map + glGenTextures(1, &prefilter.id); + glBindTexture(GL_TEXTURE_CUBE_MAP, prefilter.id); + for (unsigned int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Generate mipmaps for the prefiltered HDR texture + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + + // Create projection (transposed) and different views for each face + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); + //MatrixTranspose(&fboProjection); + Matrix fboViews[6] = { + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }) + }; + + // Prefilter HDR and store data into mipmap levels + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_PROJECTION], fboProjection); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + #define MAX_MIPMAP_LEVELS 5 // Max number of prefilter texture mipmaps + + for (unsigned int mip = 0; mip < MAX_MIPMAP_LEVELS; mip++) + { + // Resize framebuffer according to mip-level size. + unsigned int mipWidth = size*(int) powf(0.5f, (float) mip); + unsigned int mipHeight = size* (int) powf(0.5f, (float) mip); + + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight); + glViewport(0, 0, mipWidth, mipHeight); + + float roughness = (float)mip/(float)(MAX_MIPMAP_LEVELS - 1); + glUniform1f(roughnessLoc, roughness); + + for (unsigned int i = 0; i < 6; ++i) + { + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilter.id, mip); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawCube(); + } + } + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, screenWidth, screenHeight); + + prefilter.width = size; + prefilter.height = size; +#endif + return prefilter; +} + +// Generate BRDF texture using cubemap data +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size) +{ + Texture2D brdf = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // Generate BRDF convolution texture + glGenTextures(1, &brdf.id); + glBindTexture(GL_TEXTURE_2D, brdf.id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, size, size, 0, GL_RG, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Render BRDF LUT into a quad using FBO + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdf.id, 0); + + glViewport(0, 0, size, size); + glUseProgram(shader.id); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawQuad(); + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, screenWidth, screenHeight); + + brdf.width = size; + brdf.height = size; +#endif + return brdf; +} + +// Begin blending mode (alpha, additive, multiplied) +// NOTE: Only 3 blending modes supported, default blend mode is alpha +void BeginBlendMode(int mode) +{ + if ((blendMode != mode) && (mode < 3)) + { + rlglDraw(); + + switch (mode) + { + case BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; + case BLEND_ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; // Alternative: glBlendFunc(GL_ONE, GL_ONE); + case BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break; + default: break; + } + + blendMode = mode; + } +} + +// End blending mode (reset to default: alpha blending) +void EndBlendMode(void) +{ + BeginBlendMode(BLEND_ALPHA); +} + +#if defined(SUPPORT_VR_SIMULATOR) +// Get VR device information for some standard devices +VrDeviceInfo GetVrDeviceInfo(int vrDeviceType) +{ + VrDeviceInfo hmd = { 0 }; // Current VR device info + + switch (vrDeviceType) + { + case HMD_DEFAULT_DEVICE: + case HMD_OCULUS_RIFT_CV1: + { + // Oculus Rift CV1 parameters + // NOTE: CV1 represents a complete HMD redesign compared to previous versions, + // new Fresnel-hybrid-asymmetric lenses have been added and, consequently, + // previous parameters (DK2) and distortion shader (DK2) doesn't work any more. + // I just defined a set of parameters for simulator that approximate to CV1 stereo rendering + // but result is not the same obtained with Oculus PC SDK. + hmd.hResolution = 2160; // HMD horizontal resolution in pixels + hmd.vResolution = 1200; // HMD vertical resolution in pixels + hmd.hScreenSize = 0.133793f; // HMD horizontal size in meters + hmd.vScreenSize = 0.0669f; // HMD vertical size in meters + hmd.vScreenCenter = 0.04678f; // HMD screen center in meters + hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters + hmd.lensSeparationDistance = 0.07f; // HMD lens separation distance in meters + hmd.interpupillaryDistance = 0.07f; // HMD IPD (distance between pupils) in meters + hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 + hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 + hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 + hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 + hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 + hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 + hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 + hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 + + TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift CV1)"); + } break; + case HMD_OCULUS_RIFT_DK2: + { + // Oculus Rift DK2 parameters + hmd.hResolution = 1280; // HMD horizontal resolution in pixels + hmd.vResolution = 800; // HMD vertical resolution in pixels + hmd.hScreenSize = 0.14976f; // HMD horizontal size in meters + hmd.vScreenSize = 0.09356f; // HMD vertical size in meters + hmd.vScreenCenter = 0.04678f; // HMD screen center in meters + hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters + hmd.lensSeparationDistance = 0.0635f; // HMD lens separation distance in meters + hmd.interpupillaryDistance = 0.064f; // HMD IPD (distance between pupils) in meters + hmd.lensDistortionValues[0] = 1.0f; // HMD lens distortion constant parameter 0 + hmd.lensDistortionValues[1] = 0.22f; // HMD lens distortion constant parameter 1 + hmd.lensDistortionValues[2] = 0.24f; // HMD lens distortion constant parameter 2 + hmd.lensDistortionValues[3] = 0.0f; // HMD lens distortion constant parameter 3 + hmd.chromaAbCorrection[0] = 0.996f; // HMD chromatic aberration correction parameter 0 + hmd.chromaAbCorrection[1] = -0.004f; // HMD chromatic aberration correction parameter 1 + hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 + hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 + + TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift DK2)"); + } break; + case HMD_OCULUS_GO: + { + // TODO: Provide device display and lens parameters + } + case HMD_VALVE_HTC_VIVE: + { + // TODO: Provide device display and lens parameters + } + case HMD_SONY_PSVR: + { + // TODO: Provide device display and lens parameters + } + default: break; + } + + return hmd; +} + +// Init VR simulator for selected device parameters +// NOTE: It modifies the global variable: VrStereoConfig vrConfig +void InitVrSimulator(VrDeviceInfo info) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Initialize framebuffer and textures for stereo rendering + // NOTE: Screen size should match HMD aspect ratio + vrConfig.stereoFbo = rlLoadRenderTexture(screenWidth, screenHeight); + +#if defined(SUPPORT_DISTORTION_SHADER) + // Load distortion shader + vrConfig.distortionShader = LoadShaderCode(NULL, distortionFShaderStr); + + if (vrConfig.distortionShader.id > 0) SetShaderDefaultLocations(&vrConfig.distortionShader); +#endif + + // Set VR configutarion parameters, including distortion shader + SetStereoConfig(info); + + vrSimulatorReady = true; +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + TraceLog(LOG_WARNING, "VR Simulator not supported on OpenGL 1.1"); +#endif +} + +// Close VR simulator for current device +void CloseVrSimulator(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vrSimulatorReady) + { + rlDeleteRenderTextures(vrConfig.stereoFbo); // Unload stereo framebuffer and texture + #if defined(SUPPORT_DISTORTION_SHADER) + UnloadShader(vrConfig.distortionShader); // Unload distortion shader + #endif + } +#endif +} + +// Detect if VR simulator is running +bool IsVrSimulatorReady(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + return vrSimulatorReady; +#else + return false; +#endif +} + +// Set VR distortion shader for stereoscopic rendering +// TODO: Review VR system to be more flexible, move distortion shader to user side +void SetVrDistortionShader(Shader shader) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + vrConfig.distortionShader = shader; + + //SetStereoConfig(info); // TODO: Must be reviewed to set new distortion shader uniform values... +#endif +} + +// Enable/Disable VR experience (device or simulator) +void ToggleVrMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + vrSimulatorReady = !vrSimulatorReady; + + if (!vrSimulatorReady) + { + vrStereoRender = false; + + // Reset viewport and default projection-modelview matrices + rlViewport(0, 0, screenWidth, screenHeight); + projection = MatrixOrtho(0.0, screenWidth, screenHeight, 0.0, 0.0, 1.0); + modelview = MatrixIdentity(); + } + else vrStereoRender = true; +#endif +} + +// Update VR tracking (position and orientation) and camera +// NOTE: Camera (position, target, up) gets update with head tracking information +void UpdateVrTracking(Camera *camera) +{ + // TODO: Simulate 1st person camera system +} + +// Begin Oculus drawing configuration +void BeginVrDrawing(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vrSimulatorReady) + { + // Setup framebuffer for stereo rendering + rlEnableRenderTexture(vrConfig.stereoFbo.id); + + // NOTE: If your application is configured to treat the texture as a linear format (e.g. GL_RGBA) + // and performs linear-to-gamma conversion in GLSL or does not care about gamma-correction, then: + // - Require OculusBuffer format to be OVR_FORMAT_R8G8B8A8_UNORM_SRGB + // - Do NOT enable GL_FRAMEBUFFER_SRGB + //glEnable(GL_FRAMEBUFFER_SRGB); + + //glViewport(0, 0, buffer.width, buffer.height); // Useful if rendering to separate framebuffers (every eye) + rlClearScreenBuffers(); // Clear current framebuffer(s) + + vrStereoRender = true; + } +#endif +} + +// End Oculus drawing process (and desktop mirror) +void EndVrDrawing(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (vrSimulatorReady) + { + vrStereoRender = false; // Disable stereo render + + rlDisableRenderTexture(); // Unbind current framebuffer + + rlClearScreenBuffers(); // Clear current framebuffer + + // Set viewport to default framebuffer size (screen size) + rlViewport(0, 0, screenWidth, screenHeight); + + // Let rlgl reconfigure internal matrices + rlMatrixMode(RL_PROJECTION); // Enable internal projection matrix + rlLoadIdentity(); // Reset internal projection matrix + rlOrtho(0.0, screenWidth, screenHeight, 0.0, 0.0, 1.0); // Recalculate internal projection matrix + rlMatrixMode(RL_MODELVIEW); // Enable internal modelview matrix + rlLoadIdentity(); // Reset internal modelview matrix + +#if defined(SUPPORT_DISTORTION_SHADER) + // Draw RenderTexture (stereoFbo) using distortion shader + currentShader = vrConfig.distortionShader; +#else + currentShader = GetShaderDefault(); +#endif + + rlEnableTexture(vrConfig.stereoFbo.texture.id); + + rlPushMatrix(); + rlBegin(RL_QUADS); + rlColor4ub(255, 255, 255, 255); + rlNormal3f(0.0f, 0.0f, 1.0f); + + // Bottom-left corner for texture and quad + rlTexCoord2f(0.0f, 1.0f); + rlVertex2f(0.0f, 0.0f); + + // Bottom-right corner for texture and quad + rlTexCoord2f(0.0f, 0.0f); + rlVertex2f(0.0f, (float)vrConfig.stereoFbo.texture.height); + + // Top-right corner for texture and quad + rlTexCoord2f(1.0f, 0.0f); + rlVertex2f( (float)vrConfig.stereoFbo.texture.width, (float)vrConfig.stereoFbo.texture.height); + + // Top-left corner for texture and quad + rlTexCoord2f(1.0f, 1.0f); + rlVertex2f( (float)vrConfig.stereoFbo.texture.width, 0.0f); + rlEnd(); + rlPopMatrix(); + + rlDisableTexture(); + + // Update and draw render texture fbo with distortion to backbuffer + UpdateBuffersDefault(); + DrawBuffersDefault(); + + // Restore defaultShader + currentShader = defaultShader; + + // Reset viewport and default projection-modelview matrices + rlViewport(0, 0, screenWidth, screenHeight); + projection = MatrixOrtho(0.0, screenWidth, screenHeight, 0.0, 0.0, 1.0); + modelview = MatrixIdentity(); + + rlDisableDepthTest(); + } +#endif +} +#endif // SUPPORT_VR_SIMULATOR + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Compile custom shader and return shader id +static unsigned int CompileShader(const char *shaderStr, int type) +{ + unsigned int shader = glCreateShader(type); + glShaderSource(shader, 1, &shaderStr, NULL); + + GLint success = 0; + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (success != GL_TRUE) + { + TraceLog(LOG_WARNING, "[SHDR ID %i] Failed to compile shader...", shader); + int maxLength = 0; + int length; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + +#if defined(_MSC_VER) + char *log = malloc(maxLength); +#else + char log[maxLength]; +#endif + glGetShaderInfoLog(shader, maxLength, &length, log); + + TraceLog(LOG_INFO, "%s", log); + +#if defined(_MSC_VER) + free(log); +#endif + } + else TraceLog(LOG_INFO, "[SHDR ID %i] Shader compiled successfully", shader); + + return shader; +} + +// Load custom shader strings and return program id +static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) +{ + unsigned int program = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + + GLint success = 0; + program = glCreateProgram(); + + glAttachShader(program, vShaderId); + glAttachShader(program, fShaderId); + + // NOTE: Default attribute shader locations must be binded before linking + glBindAttribLocation(program, 0, DEFAULT_ATTRIB_POSITION_NAME); + glBindAttribLocation(program, 1, DEFAULT_ATTRIB_TEXCOORD_NAME); + glBindAttribLocation(program, 2, DEFAULT_ATTRIB_NORMAL_NAME); + glBindAttribLocation(program, 3, DEFAULT_ATTRIB_COLOR_NAME); + glBindAttribLocation(program, 4, DEFAULT_ATTRIB_TANGENT_NAME); + glBindAttribLocation(program, 5, DEFAULT_ATTRIB_TEXCOORD2_NAME); + + // NOTE: If some attrib name is no found on the shader, it locations becomes -1 + + glLinkProgram(program); + + // NOTE: All uniform variables are intitialised to 0 when a program links + + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + TraceLog(LOG_WARNING, "[SHDR ID %i] Failed to link shader program...", program); + + int maxLength = 0; + int length; + + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + +#ifdef _MSC_VER + char *log = malloc(maxLength); +#else + char log[maxLength]; +#endif + glGetProgramInfoLog(program, maxLength, &length, log); + + TraceLog(LOG_INFO, "%s", log); + +#ifdef _MSC_VER + free(log); +#endif + glDeleteProgram(program); + + program = 0; + } + else TraceLog(LOG_INFO, "[SHDR ID %i] Shader program loaded successfully", program); +#endif + return program; +} + + +// Load default shader (just vertex positioning and texture coloring) +// NOTE: This shader program is used for batch buffers (lines, triangles, quads) +static Shader LoadShaderDefault(void) +{ + Shader shader = { 0 }; + + // NOTE: All locations must be reseted to -1 (no location) + for (int i = 0; i < MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; + + // Vertex shader directly defined, no external file required + char defaultVShaderStr[] = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" +#elif defined(GRAPHICS_API_OPENGL_ES2) + "#version 100 \n" +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + "attribute vec3 vertexPosition; \n" + "attribute vec2 vertexTexCoord; \n" + "attribute vec4 vertexColor; \n" + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec3 vertexPosition; \n" + "in vec2 vertexTexCoord; \n" + "in vec4 vertexColor; \n" + "out vec2 fragTexCoord; \n" + "out vec4 fragColor; \n" +#endif + "uniform mat4 mvp; \n" + "void main() \n" + "{ \n" + " fragTexCoord = vertexTexCoord; \n" + " fragColor = vertexColor; \n" + " gl_Position = mvp*vec4(vertexPosition, 1.0); \n" + "} \n"; + + // Fragment shader directly defined, no external file required + char defaultFShaderStr[] = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" +#elif defined(GRAPHICS_API_OPENGL_ES2) + "#version 100 \n" + "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec2 fragTexCoord; \n" + "in vec4 fragColor; \n" + "out vec4 finalColor; \n" +#endif + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" +#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0 + " gl_FragColor = texelColor*colDiffuse*fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + " vec4 texelColor = texture(texture0, fragTexCoord); \n" + " finalColor = texelColor*colDiffuse*fragColor; \n" +#endif + "} \n"; + + // NOTE: Compiled vertex/fragment shaders are kept for re-use + defaultVShaderId = CompileShader(defaultVShaderStr, GL_VERTEX_SHADER); // Compile default vertex shader + defaultFShaderId = CompileShader(defaultFShaderStr, GL_FRAGMENT_SHADER); // Compile default fragment shader + + shader.id = LoadShaderProgram(defaultVShaderId, defaultFShaderId); + + if (shader.id > 0) + { + TraceLog(LOG_INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); + + // Set default shader locations: attributes locations + shader.locs[LOC_VERTEX_POSITION] = glGetAttribLocation(shader.id, "vertexPosition"); + shader.locs[LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(shader.id, "vertexTexCoord"); + shader.locs[LOC_VERTEX_COLOR] = glGetAttribLocation(shader.id, "vertexColor"); + + // Set default shader locations: uniform locations + shader.locs[LOC_MATRIX_MVP] = glGetUniformLocation(shader.id, "mvp"); + shader.locs[LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader.id, "colDiffuse"); + shader.locs[LOC_MAP_DIFFUSE] = glGetUniformLocation(shader.id, "texture0"); + + // NOTE: We could also use below function but in case DEFAULT_ATTRIB_* points are + // changed for external custom shaders, we just use direct bindings above + //SetShaderDefaultLocations(&shader); + } + else TraceLog(LOG_WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); + + return shader; +} + +// Get location handlers to for shader attributes and uniforms +// NOTE: If any location is not found, loc point becomes -1 +static void SetShaderDefaultLocations(Shader *shader) +{ + // NOTE: Default shader attrib locations have been fixed before linking: + // vertex position location = 0 + // vertex texcoord location = 1 + // vertex normal location = 2 + // vertex color location = 3 + // vertex tangent location = 4 + // vertex texcoord2 location = 5 + + // Get handles to GLSL input attibute locations + shader->locs[LOC_VERTEX_POSITION] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_POSITION_NAME); + shader->locs[LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD_NAME); + shader->locs[LOC_VERTEX_TEXCOORD02] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD2_NAME); + shader->locs[LOC_VERTEX_NORMAL] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_NORMAL_NAME); + shader->locs[LOC_VERTEX_TANGENT] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TANGENT_NAME); + shader->locs[LOC_VERTEX_COLOR] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_COLOR_NAME); + + // Get handles to GLSL uniform locations (vertex shader) + shader->locs[LOC_MATRIX_MVP] = glGetUniformLocation(shader->id, "mvp"); + shader->locs[LOC_MATRIX_PROJECTION] = glGetUniformLocation(shader->id, "projection"); + shader->locs[LOC_MATRIX_VIEW] = glGetUniformLocation(shader->id, "view"); + + // Get handles to GLSL uniform locations (fragment shader) + shader->locs[LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader->id, "colDiffuse"); + shader->locs[LOC_MAP_DIFFUSE] = glGetUniformLocation(shader->id, "texture0"); + shader->locs[LOC_MAP_SPECULAR] = glGetUniformLocation(shader->id, "texture1"); + shader->locs[LOC_MAP_NORMAL] = glGetUniformLocation(shader->id, "texture2"); +} + +// Unload default shader +static void UnloadShaderDefault(void) +{ + glUseProgram(0); + + glDetachShader(defaultShader.id, defaultVShaderId); + glDetachShader(defaultShader.id, defaultFShaderId); + glDeleteShader(defaultVShaderId); + glDeleteShader(defaultFShaderId); + + glDeleteProgram(defaultShader.id); +} + +// Load default internal buffers (lines, triangles, quads) +static void LoadBuffersDefault(void) +{ + // [CPU] Allocate and initialize float array buffers to store vertex data (lines, triangles, quads) + //-------------------------------------------------------------------------------------------- + + // Lines - Initialize arrays (vertex position and color data) + lines.vertices = (float *)malloc(sizeof(float)*3*2*MAX_LINES_BATCH); // 3 float by vertex, 2 vertex by line + lines.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*2*MAX_LINES_BATCH); // 4 float by color, 2 colors by line + lines.texcoords = NULL; + lines.indices = NULL; + + for (int i = 0; i < (3*2*MAX_LINES_BATCH); i++) lines.vertices[i] = 0.0f; + for (int i = 0; i < (4*2*MAX_LINES_BATCH); i++) lines.colors[i] = 0; + + lines.vCounter = 0; + lines.cCounter = 0; + lines.tcCounter = 0; + + // Triangles - Initialize arrays (vertex position and color data) + triangles.vertices = (float *)malloc(sizeof(float)*3*3*MAX_TRIANGLES_BATCH); // 3 float by vertex, 3 vertex by triangle + triangles.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH); // 4 float by color, 3 colors by triangle + triangles.texcoords = NULL; + triangles.indices = NULL; + + for (int i = 0; i < (3*3*MAX_TRIANGLES_BATCH); i++) triangles.vertices[i] = 0.0f; + for (int i = 0; i < (4*3*MAX_TRIANGLES_BATCH); i++) triangles.colors[i] = 0; + + triangles.vCounter = 0; + triangles.cCounter = 0; + triangles.tcCounter = 0; + + // Quads - Initialize arrays (vertex position, texcoord, color data and indexes) + quads.vertices = (float *)malloc(sizeof(float)*3*4*MAX_QUADS_BATCH); // 3 float by vertex, 4 vertex by quad + quads.texcoords = (float *)malloc(sizeof(float)*2*4*MAX_QUADS_BATCH); // 2 float by texcoord, 4 texcoord by quad + quads.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*4*MAX_QUADS_BATCH); // 4 float by color, 4 colors by quad +#if defined(GRAPHICS_API_OPENGL_33) + quads.indices = (unsigned int *)malloc(sizeof(unsigned int)*6*MAX_QUADS_BATCH); // 6 int by quad (indices) +#elif defined(GRAPHICS_API_OPENGL_ES2) + quads.indices = (unsigned short *)malloc(sizeof(unsigned short)*6*MAX_QUADS_BATCH); // 6 int by quad (indices) +#endif + + for (int i = 0; i < (3*4*MAX_QUADS_BATCH); i++) quads.vertices[i] = 0.0f; + for (int i = 0; i < (2*4*MAX_QUADS_BATCH); i++) quads.texcoords[i] = 0.0f; + for (int i = 0; i < (4*4*MAX_QUADS_BATCH); i++) quads.colors[i] = 0; + + int k = 0; + + // Indices can be initialized right now + for (int i = 0; i < (6*MAX_QUADS_BATCH); i+=6) + { + quads.indices[i] = 4*k; + quads.indices[i+1] = 4*k+1; + quads.indices[i+2] = 4*k+2; + quads.indices[i+3] = 4*k; + quads.indices[i+4] = 4*k+2; + quads.indices[i+5] = 4*k+3; + + k++; + } + + quads.vCounter = 0; + quads.tcCounter = 0; + quads.cCounter = 0; + + TraceLog(LOG_INFO, "[CPU] Default buffers initialized successfully (lines, triangles, quads)"); + //-------------------------------------------------------------------------------------------- + + // [GPU] Upload vertex data and initialize VAOs/VBOs (lines, triangles, quads) + // NOTE: Default buffers are linked to use currentShader (defaultShader) + //-------------------------------------------------------------------------------------------- + + // Upload and link lines vertex buffers + if (vaoSupported) + { + // Initialize Lines VAO + glGenVertexArrays(1, &lines.vaoId); + glBindVertexArray(lines.vaoId); + } + + // Lines - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(2, &lines.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + + // Vertex color buffer (shader-location = 3) + glGenBuffers(2, &lines.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (lines)", lines.vaoId); + else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (lines)", lines.vboId[0], lines.vboId[1]); + + // Upload and link triangles vertex buffers + if (vaoSupported) + { + // Initialize Triangles VAO + glGenVertexArrays(1, &triangles.vaoId); + glBindVertexArray(triangles.vaoId); + } + + // Triangles - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &triangles.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &triangles.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (triangles)", triangles.vaoId); + else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (triangles)", triangles.vboId[0], triangles.vboId[1]); + + // Upload and link quads vertex buffers + if (vaoSupported) + { + // Initialize Quads VAO + glGenVertexArrays(1, &quads.vaoId); + glBindVertexArray(quads.vaoId); + } + + // Quads - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &quads.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + + // Vertex texcoord buffer (shader-location = 1) + glGenBuffers(1, &quads.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &quads.vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + // Fill index buffer + glGenBuffers(1, &quads.vboId[3]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); +#if defined(GRAPHICS_API_OPENGL_33) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); +#endif + + if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (quads)", quads.vaoId); + else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (quads)", quads.vboId[0], quads.vboId[1], quads.vboId[2], quads.vboId[3]); + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); + //-------------------------------------------------------------------------------------------- +} + +// Update default internal buffers (VAOs/VBOs) with vertex array data +// NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) +// TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) +static void UpdateBuffersDefault(void) +{ + // Update lines vertex buffers + if (lines.vCounter > 0) + { + // Activate Lines VAO + if (vaoSupported) glBindVertexArray(lines.vaoId); + + // Lines - vertex positions buffer + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*lines.vCounter, lines.vertices); // target - offset (in bytes) - size (in bytes) - data pointer + + // Lines - colors buffer + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*lines.cCounter, lines.colors); + } + + // Update triangles vertex buffers + if (triangles.vCounter > 0) + { + // Activate Triangles VAO + if (vaoSupported) glBindVertexArray(triangles.vaoId); + + // Triangles - vertex positions buffer + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*triangles.vCounter, triangles.vertices); + + // Triangles - colors buffer + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*triangles.cCounter, triangles.colors); + } + + // Update quads vertex buffers + if (quads.vCounter > 0) + { + // Activate Quads VAO + if (vaoSupported) glBindVertexArray(quads.vaoId); + + // Quads - vertex positions buffer + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*quads.vCounter, quads.vertices); + + // Quads - texture coordinates buffer + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*quads.vCounter, quads.texcoords); + + // Quads - colors buffer + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*quads.vCounter, quads.colors); + + // Another option would be using buffer mapping... + //quads.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // Now we can modify vertices + //glUnmapBuffer(GL_ARRAY_BUFFER); + } + //-------------------------------------------------------------- + + // Unbind the current VAO + if (vaoSupported) glBindVertexArray(0); +} + +// Draw default internal buffers vertex data +// NOTE: We draw in this order: lines, triangles, quads +static void DrawBuffersDefault(void) +{ + Matrix matProjection = projection; + Matrix matModelView = modelview; + + int eyesCount = 1; +#if defined(SUPPORT_VR_SIMULATOR) + if (vrStereoRender) eyesCount = 2; +#endif + + for (int eye = 0; eye < eyesCount; eye++) + { + #if defined(SUPPORT_VR_SIMULATOR) + if (eyesCount == 2) SetStereoView(eye, matProjection, matModelView); + #endif + + // Set current shader and upload current MVP matrix + if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) + { + glUseProgram(currentShader.id); + + // Create modelview-projection matrix + Matrix matMVP = MatrixMultiply(modelview, projection); + + glUniformMatrix4fv(currentShader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); + glUniform4f(currentShader.locs[LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1i(currentShader.locs[LOC_MAP_DIFFUSE], 0); + + // NOTE: Additional map textures not considered for default buffers drawing + } + + // Draw lines buffers + if (lines.vCounter > 0) + { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, whiteTexture); + + if (vaoSupported) + { + glBindVertexArray(lines.vaoId); + } + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + } + + glDrawArrays(GL_LINES, 0, lines.vCounter); + + if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + } + + // Draw triangles buffers + if (triangles.vCounter > 0) + { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, whiteTexture); + + if (vaoSupported) + { + glBindVertexArray(triangles.vaoId); + } + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + } + + glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); + + if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + } + + // Draw quads buffers + if (quads.vCounter > 0) + { + int quadsCount = 0; + int numIndicesToProcess = 0; + int indicesOffset = 0; + + if (vaoSupported) + { + glBindVertexArray(quads.vaoId); + } + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + + // Bind vertex attrib: texcoord (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); + + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); + } + + for (int i = 0; i < drawsCounter; i++) + { + quadsCount = draws[i].vertexCount/4; + numIndicesToProcess = quadsCount*6; // Get number of Quads*6 index by Quad + + //TraceLog(LOG_DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, draws[i].textureId); + + // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process + #if defined(GRAPHICS_API_OPENGL_33) + glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid *)(sizeof(GLuint)*indicesOffset)); + #elif defined(GRAPHICS_API_OPENGL_ES2) + glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLushort)*indicesOffset)); + #endif + //GLenum err; + //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(LOG_INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM! + + indicesOffset += draws[i].vertexCount/4*6; + } + + if (!vaoSupported) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + } + + if (vaoSupported) glBindVertexArray(0); // Unbind VAO + + glUseProgram(0); // Unbind shader program + } + + // Reset vertex counters for next frame + lines.vCounter = 0; + lines.cCounter = 0; + triangles.vCounter = 0; + triangles.cCounter = 0; + quads.vCounter = 0; + quads.tcCounter = 0; + quads.cCounter = 0; + + // Reset depth for next draw + currentDepth = -1.0f; + + // Restore projection/modelview matrices + projection = matProjection; + modelview = matModelView; + + // Reset draws counter + drawsCounter = 1; + draws[0].textureId = whiteTexture; + draws[0].vertexCount = 0; +} + +// Unload default internal buffers vertex data from CPU and GPU +static void UnloadBuffersDefault(void) +{ + // Unbind everything + if (vaoSupported) glBindVertexArray(0); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // Delete VBOs from GPU (VRAM) + glDeleteBuffers(1, &lines.vboId[0]); + glDeleteBuffers(1, &lines.vboId[1]); + glDeleteBuffers(1, &triangles.vboId[0]); + glDeleteBuffers(1, &triangles.vboId[1]); + glDeleteBuffers(1, &quads.vboId[0]); + glDeleteBuffers(1, &quads.vboId[1]); + glDeleteBuffers(1, &quads.vboId[2]); + glDeleteBuffers(1, &quads.vboId[3]); + + if (vaoSupported) + { + // Delete VAOs from GPU (VRAM) + glDeleteVertexArrays(1, &lines.vaoId); + glDeleteVertexArrays(1, &triangles.vaoId); + glDeleteVertexArrays(1, &quads.vaoId); + } + + // Free vertex arrays memory from CPU (RAM) + free(lines.vertices); + free(lines.colors); + + free(triangles.vertices); + free(triangles.colors); + + free(quads.vertices); + free(quads.texcoords); + free(quads.colors); + free(quads.indices); +} + +// Renders a 1x1 XY quad in NDC +static void GenDrawQuad(void) +{ + unsigned int quadVAO = 0; + unsigned int quadVBO = 0; + + float vertices[] = { + // Positions // Texture Coords + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + }; + + // Set up plane VAO + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + + // Fill buffer + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW); + + // Link vertex attributes + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)(3*sizeof(float))); + + // Draw quad + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + + glDeleteBuffers(1, &quadVBO); + glDeleteVertexArrays(1, &quadVAO); +} + +// Renders a 1x1 3D cube in NDC +static void GenDrawCube(void) +{ + unsigned int cubeVAO = 0; + unsigned int cubeVBO = 0; + + float vertices[] = { + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f + }; + + // Set up cube VAO + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &cubeVBO); + + // Fill buffer + glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + // Link vertex attributes + glBindVertexArray(cubeVAO); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(3*sizeof(float))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(6*sizeof(float))); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + // Draw cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); + + glDeleteBuffers(1, &cubeVBO); + glDeleteVertexArrays(1, &cubeVAO); +} + +#if defined(SUPPORT_VR_SIMULATOR) +// Configure stereo rendering (including distortion shader) with HMD device parameters +// NOTE: It modifies the global variable: VrStereoConfig vrConfig +static void SetStereoConfig(VrDeviceInfo hmd) +{ + // Compute aspect ratio + float aspect = ((float)hmd.hResolution*0.5f)/(float)hmd.vResolution; + + // Compute lens parameters + float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize; + float leftLensCenter[2] = { 0.25f + lensShift, 0.5f }; + float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; + float leftScreenCenter[2] = { 0.25f, 0.5f }; + float rightScreenCenter[2] = { 0.75f, 0.5f }; + + // Compute distortion scale parameters + // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] + float lensRadius = (float)fabs(-1.0f - 4.0f*lensShift); + float lensRadiusSq = lensRadius*lensRadius; + float distortionScale = hmd.lensDistortionValues[0] + + hmd.lensDistortionValues[1]*lensRadiusSq + + hmd.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq + + hmd.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; + + TraceLog(LOG_DEBUG, "VR: Distortion Scale: %f", distortionScale); + + float normScreenWidth = 0.5f; + float normScreenHeight = 1.0f; + float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; + float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; + + TraceLog(LOG_DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); + +#if defined(SUPPORT_DISTORTION_SHADER) + // Update distortion shader with lens and distortion-scale parameters + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftLensCenter"), leftLensCenter, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightLensCenter"), rightLensCenter, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftScreenCenter"), leftScreenCenter, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightScreenCenter"), rightScreenCenter, 2); + + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scale"), scale, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scaleIn"), scaleIn, 2); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "hmdWarpParam"), hmd.lensDistortionValues, 4); + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, 4); +#endif + + // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance) + // ...but with lens distortion it is increased (see Oculus SDK Documentation) + //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance); // Really need distortionScale? + float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance); + + // Compute camera projection matrices + float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] + Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); + vrConfig.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); + vrConfig.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); + + // Compute camera transformation matrices + // NOTE: Camera movement might seem more natural if we model the head. + // Our axis of rotation is the base of our head, so we might want to add + // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. + vrConfig.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); + + // Compute eyes Viewports + vrConfig.eyesViewport[0] = (Rectangle){ 0.0f, 0.0f, (float)hmd.hResolution/2, (float)hmd.vResolution }; + vrConfig.eyesViewport[1] = (Rectangle){ hmd.hResolution/2.0f, 0.0f, (float)hmd.hResolution/2, (float) hmd.vResolution }; +} + +// Set internal projection and modelview matrix depending on eyes tracking data +static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) +{ + Matrix eyeProjection = matProjection; + Matrix eyeModelView = matModelView; + + // Setup viewport and projection/modelview matrices using tracking data + rlViewport(eye*screenWidth/2, 0, screenWidth/2, screenHeight); + + // Apply view offset to modelview matrix + eyeModelView = MatrixMultiply(matModelView, vrConfig.eyesViewOffset[eye]); + + // Set current eye projection matrix + eyeProjection = vrConfig.eyesProjection[eye]; + + SetMatrixModelview(eyeModelView); + SetMatrixProjection(eyeProjection); +} +#endif // defined(SUPPORT_VR_SIMULATOR) + +#endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + +#if defined(GRAPHICS_API_OPENGL_11) +// Mipmaps data is generated after image data +// NOTE: Only works with RGBA (4 bytes) data! +static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) +{ + int mipmapCount = 1; // Required mipmap levels count (including base level) + int width = baseWidth; + int height = baseHeight; + int size = baseWidth*baseHeight*4; // Size in bytes (will include mipmaps...), RGBA only + + // Count mipmap levels required + while ((width != 1) && (height != 1)) + { + if (width != 1) width /= 2; + if (height != 1) height /= 2; + + TraceLog(LOG_DEBUG, "Next mipmap size: %i x %i", width, height); + + mipmapCount++; + + size += (width*height*4); // Add mipmap size (in bytes) + } + + TraceLog(LOG_DEBUG, "Total mipmaps required: %i", mipmapCount); + TraceLog(LOG_DEBUG, "Total size of data required: %i", size); + + unsigned char *temp = realloc(data, size); + + if (temp != NULL) data = temp; + else TraceLog(LOG_WARNING, "Mipmaps required memory could not be allocated"); + + width = baseWidth; + height = baseHeight; + size = (width*height*4); + + // Generate mipmaps + // NOTE: Every mipmap data is stored after data + Color *image = (Color *)malloc(width*height*sizeof(Color)); + Color *mipmap = NULL; + int offset = 0; + int j = 0; + + for (int i = 0; i < size; i += 4) + { + image[j].r = data[i]; + image[j].g = data[i + 1]; + image[j].b = data[i + 2]; + image[j].a = data[i + 3]; + j++; + } + + TraceLog(LOG_DEBUG, "Mipmap base (%ix%i)", width, height); + + for (int mip = 1; mip < mipmapCount; mip++) + { + mipmap = GenNextMipmap(image, width, height); + + offset += (width*height*4); // Size of last mipmap + j = 0; + + width /= 2; + height /= 2; + size = (width*height*4); // Mipmap size to store after offset + + // Add mipmap to data + for (int i = 0; i < size; i += 4) + { + data[offset + i] = mipmap[j].r; + data[offset + i + 1] = mipmap[j].g; + data[offset + i + 2] = mipmap[j].b; + data[offset + i + 3] = mipmap[j].a; + j++; + } + + free(image); + + image = mipmap; + mipmap = NULL; + } + + free(mipmap); // free mipmap data + + return mipmapCount; +} + +// Manual mipmap generation (basic scaling algorithm) +static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight) +{ + int x2, y2; + Color prow, pcol; + + int width = srcWidth/2; + int height = srcHeight/2; + + Color *mipmap = (Color *)malloc(width*height*sizeof(Color)); + + // Scaling algorithm works perfectly (box-filter) + for (int y = 0; y < height; y++) + { + y2 = 2*y; + + for (int x = 0; x < width; x++) + { + x2 = 2*x; + + prow.r = (srcData[y2*srcWidth + x2].r + srcData[y2*srcWidth + x2 + 1].r)/2; + prow.g = (srcData[y2*srcWidth + x2].g + srcData[y2*srcWidth + x2 + 1].g)/2; + prow.b = (srcData[y2*srcWidth + x2].b + srcData[y2*srcWidth + x2 + 1].b)/2; + prow.a = (srcData[y2*srcWidth + x2].a + srcData[y2*srcWidth + x2 + 1].a)/2; + + pcol.r = (srcData[(y2+1)*srcWidth + x2].r + srcData[(y2+1)*srcWidth + x2 + 1].r)/2; + pcol.g = (srcData[(y2+1)*srcWidth + x2].g + srcData[(y2+1)*srcWidth + x2 + 1].g)/2; + pcol.b = (srcData[(y2+1)*srcWidth + x2].b + srcData[(y2+1)*srcWidth + x2 + 1].b)/2; + pcol.a = (srcData[(y2+1)*srcWidth + x2].a + srcData[(y2+1)*srcWidth + x2 + 1].a)/2; + + mipmap[y*width + x].r = (prow.r + pcol.r)/2; + mipmap[y*width + x].g = (prow.g + pcol.g)/2; + mipmap[y*width + x].b = (prow.b + pcol.b)/2; + mipmap[y*width + x].a = (prow.a + pcol.a)/2; + } + } + + TraceLog(LOG_DEBUG, "Mipmap generated successfully (%ix%i)", width, height); + + return mipmap; +} +#endif + +#if defined(RLGL_STANDALONE) +// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) +void TraceLog(int msgType, const char *text, ...) +{ + va_list args; + va_start(args, text); + + switch (msgType) + { + case LOG_INFO: fprintf(stdout, "INFO: "); break; + case LOG_ERROR: fprintf(stdout, "ERROR: "); break; + case LOG_WARNING: fprintf(stdout, "WARNING: "); break; + case LOG_DEBUG: fprintf(stdout, "DEBUG: "); break; + default: break; + } + + vfprintf(stdout, text, args); + fprintf(stdout, "\n"); + + va_end(args); + + if (msgType == LOG_ERROR) exit(1); +} + +// Get pixel data size in bytes (image or texture) +// NOTE: Size depends on pixel format +int GetPixelDataSize(int width, int height, int format) +{ + int dataSize = 0; // Size in bytes + int bpp = 0; // Bits per pixel + + switch (format) + { + case UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case UNCOMPRESSED_GRAY_ALPHA: + case UNCOMPRESSED_R5G6B5: + case UNCOMPRESSED_R5G5B5A1: + case UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case UNCOMPRESSED_R8G8B8: bpp = 24; break; + case UNCOMPRESSED_R32: bpp = 32; break; + case UNCOMPRESSED_R32G32B32: bpp = 32*3; break; + case UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; + case COMPRESSED_DXT1_RGB: + case COMPRESSED_DXT1_RGBA: + case COMPRESSED_ETC1_RGB: + case COMPRESSED_ETC2_RGB: + case COMPRESSED_PVRT_RGB: + case COMPRESSED_PVRT_RGBA: bpp = 4; break; + case COMPRESSED_DXT3_RGBA: + case COMPRESSED_DXT5_RGBA: + case COMPRESSED_ETC2_EAC_RGBA: + case COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break; + case COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; + default: break; + } + + dataSize = width*height*bpp/8; // Total data size in bytes + + return dataSize; +} +#endif + +#endif // RLGL_IMPLEMENTATION \ No newline at end of file diff --git a/raylib/shaders.go b/raylib/shaders.go index 5ac86f9..29de9cd 100644 --- a/raylib/shaders.go +++ b/raylib/shaders.go @@ -1,4 +1,4 @@ -package raylib +package rl /* #include "raylib.h" diff --git a/raylib/shapes.c b/raylib/shapes.c index ed911b2..90f9582 100644 --- a/raylib/shapes.c +++ b/raylib/shapes.c @@ -153,9 +153,9 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) for (int i = 1; i <= LINE_DIVISIONS; i++) { // Cubic easing in-out - // NOTE: Easing is calcutated only for y position value - current.y = EaseCubicInOut(i, startPos.y, endPos.y - startPos.y, LINE_DIVISIONS); - current.x = previous.x + (endPos.x - startPos.x)/LINE_DIVISIONS; + // NOTE: Easing is calculated only for y position value + current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)LINE_DIVISIONS); + current.x = previous.x + (endPos.x - startPos.x)/ (float)LINE_DIVISIONS; DrawLineEx(previous, current, thick, color); @@ -173,6 +173,8 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) // NOTE: Gradient goes from center (color1) to border (color2) void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) { + if (rlCheckBufferLimit(RL_TRIANGLES, 3*36)) rlglDraw(); + rlBegin(RL_TRIANGLES); for (int i = 0; i < 360; i += 10) { @@ -189,8 +191,10 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co // Draw a color-filled circle (Vector version) // NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw) void DrawCircleV(Vector2 center, float radius, Color color) -{ +{ #if defined(SUPPORT_QUADS_DRAW_MODE) + if (rlCheckBufferLimit(RL_QUADS, 4*(36/2))) rlglDraw(); + rlEnableTexture(GetTextureDefault().id); // Default white texture rlBegin(RL_QUADS); @@ -207,6 +211,8 @@ void DrawCircleV(Vector2 center, float radius, Color color) rlDisableTexture(); #else + if (rlCheckBufferLimit(RL_TRIANGLES, 3*(36/2))) rlglDraw(); + rlBegin(RL_TRIANGLES); for (int i = 0; i < 360; i += 10) { @@ -223,6 +229,8 @@ void DrawCircleV(Vector2 center, float radius, Color color) // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) { + if (rlCheckBufferLimit(RL_LINES, 2*36)) rlglDraw(); + rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -251,27 +259,27 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color) #if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_FONT_TEXTURE) // Draw rectangle using font texture white character - rlEnableTexture(GetDefaultFont().texture.id); + rlEnableTexture(GetFontDefault().texture.id); rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); rlNormal3f(0.0f, 0.0f, 1.0f); // NOTE: Default raylib font character 95 is a white square - rlTexCoord2f((float)GetDefaultFont().chars[95].rec.x/GetDefaultFont().texture.width, - (float)GetDefaultFont().chars[95].rec.y/GetDefaultFont().texture.height); + rlTexCoord2f((float)GetFontDefault().chars[95].rec.x/GetFontDefault().texture.width, + (float)GetFontDefault().chars[95].rec.y/GetFontDefault().texture.height); rlVertex2f(position.x, position.y); - rlTexCoord2f((float)GetDefaultFont().chars[95].rec.x/GetDefaultFont().texture.width, - (float)(GetDefaultFont().chars[95].rec.y + GetDefaultFont().chars[95].rec.height)/GetDefaultFont().texture.height); + rlTexCoord2f((float)GetFontDefault().chars[95].rec.x/GetFontDefault().texture.width, + (float)(GetFontDefault().chars[95].rec.y + GetFontDefault().chars[95].rec.height)/GetFontDefault().texture.height); rlVertex2f(position.x, position.y + size.y); - rlTexCoord2f((float)(GetDefaultFont().chars[95].rec.x + GetDefaultFont().chars[95].rec.width)/GetDefaultFont().texture.width, - (float)(GetDefaultFont().chars[95].rec.y + GetDefaultFont().chars[95].rec.height)/GetDefaultFont().texture.height); + rlTexCoord2f((float)(GetFontDefault().chars[95].rec.x + GetFontDefault().chars[95].rec.width)/GetFontDefault().texture.width, + (float)(GetFontDefault().chars[95].rec.y + GetFontDefault().chars[95].rec.height)/GetFontDefault().texture.height); rlVertex2f(position.x + size.x, position.y + size.y); - rlTexCoord2f((float)(GetDefaultFont().chars[95].rec.x + GetDefaultFont().chars[95].rec.width)/GetDefaultFont().texture.width, - (float)GetDefaultFont().chars[95].rec.y/GetDefaultFont().texture.height); + rlTexCoord2f((float)(GetFontDefault().chars[95].rec.x + GetFontDefault().chars[95].rec.width)/GetFontDefault().texture.width, + (float)GetFontDefault().chars[95].rec.y/GetFontDefault().texture.height); rlVertex2f(position.x + size.x, position.y); rlEnd(); @@ -316,7 +324,7 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color) // Draw a color-filled rectangle void DrawRectangleRec(Rectangle rec, Color color) { - DrawRectangle(rec.x, rec.y, rec.width, rec.height, color); + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, color); } void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color) @@ -346,14 +354,14 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color // NOTE: Gradient goes from bottom (color1) to top (color2) void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2) { - DrawRectangleGradientEx((Rectangle){ posX, posY, width, height }, color1, color2, color2, color1); + DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color1, color2, color2, color1); } // Draw a horizontal-gradient-filled rectangle // NOTE: Gradient goes from bottom (color1) to top (color2) void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2) { - DrawRectangleGradientEx((Rectangle){ posX, posY, width, height }, color1, color1, color2, color2); + DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color1, color1, color2, color2); } // Draw a gradient-filled rectangle @@ -362,30 +370,30 @@ void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, { #if defined(SUPPORT_FONT_TEXTURE) // Draw rectangle using font texture white character - rlEnableTexture(GetDefaultFont().texture.id); + rlEnableTexture(GetFontDefault().texture.id); rlBegin(RL_QUADS); rlNormal3f(0.0f, 0.0f, 1.0f); // NOTE: Default raylib font character 95 is a white square rlColor4ub(col1.r, col1.g, col1.b, col1.a); - rlTexCoord2f(GetDefaultFont().chars[95].rec.x/GetDefaultFont().texture.width, - GetDefaultFont().chars[95].rec.y/GetDefaultFont().texture.height); + rlTexCoord2f(GetFontDefault().chars[95].rec.x/GetFontDefault().texture.width, + GetFontDefault().chars[95].rec.y/GetFontDefault().texture.height); rlVertex2f(rec.x, rec.y); rlColor4ub(col2.r, col2.g, col2.b, col2.a); - rlTexCoord2f(GetDefaultFont().chars[95].rec.x/GetDefaultFont().texture.width, - (GetDefaultFont().chars[95].rec.y + GetDefaultFont().chars[95].rec.height)/GetDefaultFont().texture.height); + rlTexCoord2f(GetFontDefault().chars[95].rec.x/GetFontDefault().texture.width, + (GetFontDefault().chars[95].rec.y + GetFontDefault().chars[95].rec.height)/GetFontDefault().texture.height); rlVertex2f(rec.x, rec.y + rec.height); rlColor4ub(col3.r, col3.g, col3.b, col3.a); - rlTexCoord2f((GetDefaultFont().chars[95].rec.x + GetDefaultFont().chars[95].rec.width)/GetDefaultFont().texture.width, - (GetDefaultFont().chars[95].rec.y + GetDefaultFont().chars[95].rec.height)/GetDefaultFont().texture.height); + rlTexCoord2f((GetFontDefault().chars[95].rec.x + GetFontDefault().chars[95].rec.width)/GetFontDefault().texture.width, + (GetFontDefault().chars[95].rec.y + GetFontDefault().chars[95].rec.height)/GetFontDefault().texture.height); rlVertex2f(rec.x + rec.width, rec.y + rec.height); rlColor4ub(col4.r, col4.g, col4.b, col4.a); - rlTexCoord2f((GetDefaultFont().chars[95].rec.x + GetDefaultFont().chars[95].rec.width)/GetDefaultFont().texture.width, - GetDefaultFont().chars[95].rec.y/GetDefaultFont().texture.height); + rlTexCoord2f((GetFontDefault().chars[95].rec.x + GetFontDefault().chars[95].rec.width)/GetFontDefault().texture.width, + GetFontDefault().chars[95].rec.y/GetFontDefault().texture.height); rlVertex2f(rec.x + rec.width, rec.y); rlEnd(); @@ -449,14 +457,14 @@ void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color) { if (lineThick > rec.width || lineThick > rec.height) { - if(rec.width > rec.height) lineThick = rec.height/2; - else if (rec.width < rec.height) lineThick = rec.width/2; + if(rec.width > rec.height) lineThick = (int)rec.height/2; + else if (rec.width < rec.height) lineThick = (int)rec.width/2; } - DrawRectangle(rec.x, rec.y, rec.width, lineThick, color); - DrawRectangle(rec.x - lineThick + rec.width, rec.y + lineThick, lineThick, rec.height - lineThick*2, color); - DrawRectangle(rec.x, rec.y + rec.height - lineThick, rec.width, lineThick, color); - DrawRectangle(rec.x, rec.y + lineThick, lineThick, rec.height - lineThick*2, color); + DrawRectangle( (int)rec.x, (int)rec.y, (int)rec.width, lineThick, color); + DrawRectangle( (int)(rec.x - lineThick + rec.width), (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2.0f), color); + DrawRectangle( (int)rec.x, (int)(rec.y + rec.height - lineThick), (int)rec.width, lineThick, color); + DrawRectangle( (int)rec.x, (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2), color); } // Draw a triangle @@ -504,6 +512,8 @@ void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) { if (sides < 3) sides = 3; + + if (rlCheckBufferLimit(RL_QUADS, 4*(360/sides))) rlglDraw(); rlPushMatrix(); rlTranslatef(center.x, center.y, 0.0); @@ -544,6 +554,8 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color) { if (pointsCount >= 3) { + if (rlCheckBufferLimit(RL_QUADS, pointsCount)) rlglDraw(); + #if defined(SUPPORT_QUADS_DRAW_MODE) rlEnableTexture(GetTextureDefault().id); // Default white texture @@ -579,6 +591,8 @@ void DrawPolyExLines(Vector2 *points, int pointsCount, Color color) { if (pointsCount >= 2) { + if (rlCheckBufferLimit(RL_LINES, pointsCount)) rlglDraw(); + rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -633,11 +647,9 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2) { bool collision = false; - - int dx = abs((rec1.x + rec1.width/2) - (rec2.x + rec2.width/2)); - int dy = abs((rec1.y + rec1.height/2) - (rec2.y + rec2.height/2)); - - if ((dx <= (rec1.width/2 + rec2.width/2)) && ((dy <= (rec1.height/2 + rec2.height/2)))) collision = true; + + if ((rec1.x <= (rec2.x + rec2.width) && (rec1.x + rec1.width) >= rec2.x) && + (rec1.y <= (rec2.y + rec2.height) && (rec1.y + rec1.height) >= rec2.y)) collision = true; return collision; } @@ -661,11 +673,11 @@ bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, floa // NOTE: Reviewed version to take into account corner limit case bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec) { - int recCenterX = rec.x + rec.width/2; - int recCenterY = rec.y + rec.height/2; + int recCenterX = (int)(rec.x + rec.width/2.0f); + int recCenterY = (int)(rec.y + rec.height/2.0f); - float dx = fabsf(center.x - recCenterX); - float dy = fabsf(center.y - recCenterY); + float dx = (float)fabs(center.x - recCenterX); + float dy = (float)fabs(center.y - recCenterY); if (dx > (rec.width/2.0f + radius)) { return false; } if (dy > (rec.height/2.0f + radius)) { return false; } @@ -686,8 +698,8 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) if (CheckCollisionRecs(rec1, rec2)) { - int dxx = abs(rec1.x - rec2.x); - int dyy = abs(rec1.y - rec2.y); + float dxx = (float)fabs(rec1.x - rec2.x); + float dyy = (float)fabs(rec1.y - rec2.y); if (rec1.x <= rec2.x) { @@ -754,8 +766,8 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) // NOTE: Required for DrawLineBezier() static float EaseCubicInOut(float t, float b, float c, float d) { - if ((t /= 0.5*d) < 1) - return 0.5*c*t*t*t + b; + if ((t /= 0.5f*d) < 1) + return 0.5f*c*t*t*t + b; t -= 2; - return 0.5*c*(t*t*t + 2) + b; + return 0.5f*c*(t*t*t + 2.0f) + b; } diff --git a/raylib/shapes.go b/raylib/shapes.go index e2b4706..4d59541 100644 --- a/raylib/shapes.go +++ b/raylib/shapes.go @@ -1,4 +1,4 @@ -package raylib +package rl /* #include "raylib.h" diff --git a/raylib/text.c b/raylib/text.c index aa22c30..3dbb261 100644 --- a/raylib/text.c +++ b/raylib/text.c @@ -36,9 +36,8 @@ * **********************************************************************************************/ -#include "config.h" - -#include "raylib.h" +#include "config.h" // Defines module configuration flags +#include "raylib.h" // Declares module functions #include // Required for: malloc(), free() #include // Required for: strlen() @@ -48,15 +47,13 @@ #include "utils.h" // Required for: fopen() Android mapping #if defined(SUPPORT_FILEFORMAT_TTF) - // Following libs are used on LoadTTF() - #define STBTT_STATIC // Define stb_truetype functions static to this module - #define STB_TRUETYPE_IMPLEMENTATION - #include "external/stb_truetype.h" // Required for: stbtt_BakeFontBitmap() -#endif + #define STB_RECT_PACK_IMPLEMENTATION + #include "external/stb_rect_pack.h" // Required for: ttf font rectangles packaging -// Rectangle packing functions (not used at the moment) -//#define STB_RECT_PACK_IMPLEMENTATION -//#include "stb_rect_pack.h" + #define STBTT_STATIC + #define STB_TRUETYPE_IMPLEMENTATION + #include "external/stb_truetype.h" // Required for: ttf font data reading +#endif //---------------------------------------------------------------------------------- // Defines and Macros @@ -89,9 +86,6 @@ static Font LoadImageFont(Image image, Color key, int firstChar); // Load a Imag #if defined(SUPPORT_FILEFORMAT_FNT) static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) #endif -#if defined(SUPPORT_FILEFORMAT_TTF) -static Font LoadTTF(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load spritefont from TTF data -#endif #if defined(SUPPORT_DEFAULT_FONT) extern void LoadDefaultFont(void); @@ -223,12 +217,12 @@ extern void LoadDefaultFont(void) { defaultFont.chars[i].value = 32 + i; // First char is 32 - defaultFont.chars[i].rec.x = currentPosX; - defaultFont.chars[i].rec.y = charsDivisor + currentLine*(charsHeight + charsDivisor); - defaultFont.chars[i].rec.width = charsWidth[i]; - defaultFont.chars[i].rec.height = charsHeight; + defaultFont.chars[i].rec.x = (float)currentPosX; + defaultFont.chars[i].rec.y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor)); + defaultFont.chars[i].rec.width = (float)charsWidth[i]; + defaultFont.chars[i].rec.height = (float)charsHeight; - testPosX += (defaultFont.chars[i].rec.width + charsDivisor); + testPosX += (int)(defaultFont.chars[i].rec.width + (float)charsDivisor); if (testPosX >= defaultFont.texture.width) { @@ -236,8 +230,8 @@ extern void LoadDefaultFont(void) currentPosX = 2*charsDivisor + charsWidth[i]; testPosX = currentPosX; - defaultFont.chars[i].rec.x = charsDivisor; - defaultFont.chars[i].rec.y = charsDivisor + currentLine*(charsHeight + charsDivisor); + defaultFont.chars[i].rec.x = (float)charsDivisor; + defaultFont.chars[i].rec.y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor)); } else currentPosX = testPosX; @@ -247,7 +241,7 @@ extern void LoadDefaultFont(void) defaultFont.chars[i].advanceX = 0; } - defaultFont.baseSize = defaultFont.chars[0].rec.height; + defaultFont.baseSize = (int)defaultFont.chars[0].rec.height; TraceLog(LOG_INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id); } @@ -261,7 +255,7 @@ extern void UnloadDefaultFont(void) #endif // SUPPORT_DEFAULT_FONT // Get the default font, useful to be used with extended parameters -Font GetDefaultFont() +Font GetFontDefault() { #if defined(SUPPORT_DEFAULT_FONT) return defaultFont; @@ -277,32 +271,40 @@ Font LoadFont(const char *fileName) // Default hardcoded values for ttf file loading #define DEFAULT_TTF_FONTSIZE 32 // Font first character (32 - space) #define DEFAULT_TTF_NUMCHARS 95 // ASCII 32..126 is 95 glyphs - #define DEFAULT_FIRST_CHAR 32 // Expected first char for image spritefont + #define DEFAULT_FIRST_CHAR 32 // Expected first char for image sprite font - Font spriteFont = { 0 }; + Font font = { 0 }; #if defined(SUPPORT_FILEFORMAT_TTF) - if (IsFileExtension(fileName, ".ttf")) spriteFont = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, 0, NULL); + if (IsFileExtension(fileName, ".ttf")) + { + font.baseSize = DEFAULT_TTF_FONTSIZE; + font.charsCount = DEFAULT_TTF_NUMCHARS; + font.chars = LoadFontData(fileName, font.baseSize, NULL, font.charsCount, FONT_DEFAULT); + Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 4, 0); + font.texture = LoadTextureFromImage(atlas); + UnloadImage(atlas); + } else #endif #if defined(SUPPORT_FILEFORMAT_FNT) - if (IsFileExtension(fileName, ".fnt")) spriteFont = LoadBMFont(fileName); + if (IsFileExtension(fileName, ".fnt")) font = LoadBMFont(fileName); else #endif { Image image = LoadImage(fileName); - if (image.data != NULL) spriteFont = LoadImageFont(image, MAGENTA, DEFAULT_FIRST_CHAR); + if (image.data != NULL) font = LoadImageFont(image, MAGENTA, DEFAULT_FIRST_CHAR); UnloadImage(image); } - if (spriteFont.texture.id == 0) + if (font.texture.id == 0) { TraceLog(LOG_WARNING, "[%s] Font could not be loaded, using default font", fileName); - spriteFont = GetDefaultFont(); + font = GetFontDefault(); } - else SetTextureFilter(spriteFont.texture, FILTER_POINT); // By default we set point filter (best performance) + else SetTextureFilter(font.texture, FILTER_POINT); // By default we set point filter (best performance) - return spriteFont; + return font; } // Load Font from TTF font file with generation parameters @@ -310,38 +312,249 @@ Font LoadFont(const char *fileName) // if array is NULL, default char set is selected 32..126 Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars) { - Font spriteFont = { 0 }; - int totalChars = 95; // Default charset [32..126] + Font font = { 0 }; + + font.baseSize = fontSize; + font.charsCount = (charsCount > 0) ? charsCount : 95; + font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT); + Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0); + font.texture = LoadTextureFromImage(atlas); + UnloadImage(atlas); + + return font; +} -#if defined(SUPPORT_FILEFORMAT_TTF) - if (IsFileExtension(fileName, ".ttf")) +// Load font data for further use +// NOTE: Requires TTF font and can generate SDF data +CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type) +{ + // NOTE: Using some SDF generation default values, + // trades off precision with ability to handle *smaller* sizes + #define SDF_CHAR_PADDING 4 + #define SDF_ON_EDGE_VALUE 128 + #define SDF_PIXEL_DIST_SCALE 64.0f + + #define BITMAP_ALPHA_THRESHOLD 80 + + // In case no chars count provided, default to 95 + charsCount = (charsCount > 0) ? charsCount : 95; + + CharInfo *chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); + + // Load font data (including pixel data) from TTF file + // NOTE: Loaded information should be enough to generate font image atlas, + // using any packaging method + FILE *fontFile = fopen(fileName, "rb"); // Load font file + + fseek(fontFile, 0, SEEK_END); + long size = ftell(fontFile); // Get file size + fseek(fontFile, 0, SEEK_SET); // Reset file pointer + + unsigned char *fontBuffer = (unsigned char *)malloc(size); + + fread(fontBuffer, size, 1, fontFile); + fclose(fontFile); + + // Init font for data reading + stbtt_fontinfo fontInfo; + if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!"); + + // Calculate font scale factor + float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize); + + // Calculate font basic metrics + // NOTE: ascent is equivalent to font baseline + int ascent, descent, lineGap; + stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap); + + // Fill fontChars in case not provided externally + // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space) + int genFontChars = false; + if (fontChars == NULL) genFontChars = true; + if (genFontChars) { - if (charsCount != 0) totalChars = charsCount; + fontChars = (int *)malloc(charsCount*sizeof(int)); + for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32; + } + + // NOTE: Using simple packaging, one char after another + for (int i = 0; i < charsCount; i++) + { + int chw = 0, chh = 0; // Character width and height (on generation) + int ch = fontChars[i]; // Character value to get info for + chars[i].value = ch; - if (fontChars == NULL) + // Render a unicode codepoint to a bitmap + // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap + // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be + // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide + + if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); + else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); + + if (type == FONT_BITMAP) { - fontChars = (int *)malloc(totalChars*sizeof(int)); - for (int i = 0; i < totalChars; i++) fontChars[i] = i + 32; // Default first character: SPACE[32] + // Aliased bitmap (black & white) font generation, avoiding anti-aliasing + // NOTE: For optimum results, bitmap font should be generated at base pixel size + for (int p = 0; p < chw*chh; p++) + { + if (chars[i].data[p] < BITMAP_ALPHA_THRESHOLD) chars[i].data[p] = 0; + else chars[i].data[p] = 255; + } } - spriteFont = LoadTTF(fileName, fontSize, totalChars, fontChars); - } -#endif + chars[i].rec.width = (float)chw; + chars[i].rec.height = (float)chh; + chars[i].offsetY += (int)((float)ascent*scaleFactor); + + // Get bounding box for character (may be offset to account for chars that dip above or below the line) + int chX1, chY1, chX2, chY2; + stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2); + + TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1); + TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1); - if (spriteFont.texture.id == 0) + stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); + chars[i].advanceX *= scaleFactor; + } + + free(fontBuffer); + if (genFontChars) free(fontChars); + + return chars; +} + +// Generate image font atlas using chars info +// NOTE: Packing method: 0-Default, 1-Skyline +Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod) +{ + Image atlas = { 0 }; + + // In case no chars count provided we suppose default of 95 + charsCount = (charsCount > 0) ? charsCount : 95; + + // Calculate image size based on required pixel area + // NOTE 1: Image is forced to be squared and POT... very conservative! + // NOTE 2: SDF font characters already contain an internal padding, + // so image size would result bigger than default font type + float requiredArea = 0; + for (int i = 0; i < charsCount; i++) requiredArea += ((chars[i].rec.width + 2*padding)*(chars[i].rec.height + 2*padding)); + float guessSize = sqrtf(requiredArea)*1.25f; + int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT + + atlas.width = imageSize; // Atlas bitmap width + atlas.height = imageSize; // Atlas bitmap height + atlas.data = (unsigned char *)calloc(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) + atlas.format = UNCOMPRESSED_GRAYSCALE; + atlas.mipmaps = 1; + + // DEBUG: We can see padding in the generated image setting a gray background... + //for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100; + + if (packMethod == 0) // Use basic packing algorythm { - TraceLog(LOG_WARNING, "[%s] Font could not be generated, using default font", fileName); - spriteFont = GetDefaultFont(); + int offsetX = padding; + int offsetY = padding; + + // NOTE: Using simple packaging, one char after another + for (int i = 0; i < charsCount; i++) + { + // Copy pixel data from fc.data to atlas + for (int y = 0; y < (int)chars[i].rec.height; y++) + { + for (int x = 0; x < (int)chars[i].rec.width; x++) + { + ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = chars[i].data[y*(int)chars[i].rec.width + x]; + } + } + + chars[i].rec.x = (float)offsetX; + chars[i].rec.y = (float)offsetY; + + // Move atlas position X for next character drawing + offsetX += ((int)chars[i].rec.width + 2*padding); + + if (offsetX >= (atlas.width - (int)chars[i].rec.width - padding)) + { + offsetX = padding; + + // NOTE: Be careful on offsetY for SDF fonts, by default SDF + // use an internal padding of 4 pixels, it means char rectangle + // height is bigger than fontSize, it could be up to (fontSize + 8) + offsetY += (fontSize + 2*padding); + + if (offsetY > (atlas.height - fontSize - padding)) break; + } + } + } + else if (packMethod == 1) // Use Skyline rect packing algorythm (stb_pack_rect) + { + TraceLog(LOG_DEBUG, "Using Skyline packing algorythm!"); + + stbrp_context *context = (stbrp_context *)malloc(sizeof(*context)); + stbrp_node *nodes = (stbrp_node *)malloc(charsCount*sizeof(*nodes)); + + stbrp_init_target(context, atlas.width, atlas.height, nodes, charsCount); + stbrp_rect *rects = (stbrp_rect *)malloc(charsCount*sizeof(stbrp_rect)); + + // Fill rectangles for packaging + for (int i = 0; i < charsCount; i++) + { + rects[i].id = i; + rects[i].w = (int)chars[i].rec.width + 2*padding; + rects[i].h = (int)chars[i].rec.height + 2*padding; + } + + // Package rectangles into atlas + stbrp_pack_rects(context, rects, charsCount); + + for (int i = 0; i < charsCount; i++) + { + chars[i].rec.x = rects[i].x + (float)padding; + chars[i].rec.y = rects[i].y + (float)padding; + + if (rects[i].was_packed) + { + // Copy pixel data from fc.data to atlas + for (int y = 0; y < (int)chars[i].rec.height; y++) + { + for (int x = 0; x < (int)chars[i].rec.width; x++) + { + ((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = chars[i].data[y*(int)chars[i].rec.width + x]; + } + } + } + else TraceLog(LOG_WARNING, "Character could not be packed: %i", i); + } + + free(nodes); + free(context); + } + + // TODO: Crop image if required for smaller size + + // Convert image data from GRAYSCALE to GRAY_ALPHA + // WARNING: ImageAlphaMask(&atlas, atlas) does not work in this case, requires manual operation + unsigned char *dataGrayAlpha = (unsigned char *)malloc(imageSize*imageSize*sizeof(unsigned char)*2); // Two channels + + for (int i = 0, k = 0; i < atlas.width*atlas.height; i++, k += 2) + { + dataGrayAlpha[k] = 255; + dataGrayAlpha[k + 1] = ((unsigned char *)atlas.data)[i]; } - return spriteFont; + free(atlas.data); + atlas.data = dataGrayAlpha; + atlas.format = UNCOMPRESSED_GRAY_ALPHA; + + return atlas; } // Unload Font from GPU memory (VRAM) void UnloadFont(Font font) { // NOTE: Make sure spriteFont is not default font (fallback) - if (font.texture.id != GetDefaultFont().texture.id) + if (font.texture.id != GetFontDefault().texture.id) { UnloadTexture(font.texture); free(font.chars); @@ -356,7 +569,7 @@ void UnloadFont(Font font) void DrawText(const char *text, int posX, int posY, int fontSize, Color color) { // Check if default font has been loaded - if (GetDefaultFont().texture.id != 0) + if (GetFontDefault().texture.id != 0) { Vector2 position = { (float)posX, (float)posY }; @@ -364,7 +577,7 @@ void DrawText(const char *text, int posX, int posY, int fontSize, Color color) if (fontSize < defaultFontSize) fontSize = defaultFontSize; int spacing = fontSize/defaultFontSize; - DrawTextEx(GetDefaultFont(), text, position, (float)fontSize, (float)spacing, color); + DrawTextEx(GetFontDefault(), text, position, (float)fontSize, (float)spacing, color); } } @@ -455,11 +668,11 @@ const char *SubText(const char *text, int position, int length) for (int c = 0 ; c < length ; c++) { - *(buffer+c) = *(text+position); + *(buffer + c) = *(text + position); text++; } - *(buffer+length) = '\0'; + *(buffer + length) = '\0'; return buffer; } @@ -470,13 +683,13 @@ int MeasureText(const char *text, int fontSize) Vector2 vec = { 0.0f, 0.0f }; // Check if default font has been loaded - if (GetDefaultFont().texture.id != 0) + if (GetFontDefault().texture.id != 0) { int defaultFontSize = 10; // Default Font chars height in pixel if (fontSize < defaultFontSize) fontSize = defaultFontSize; int spacing = fontSize/defaultFontSize; - vec = MeasureTextEx(GetDefaultFont(), text, (float)fontSize, (float)spacing); + vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing); } return (int)vec.x; @@ -629,15 +842,15 @@ static Font LoadImageFont(Image image, Color key, int firstChar) { tempCharValues[index] = firstChar + index; - tempCharRecs[index].x = xPosToRead; - tempCharRecs[index].y = lineSpacing + lineToRead*(charHeight + lineSpacing); - tempCharRecs[index].height = charHeight; + tempCharRecs[index].x = (float)xPosToRead; + tempCharRecs[index].y = (float)(lineSpacing + lineToRead*(charHeight + lineSpacing)); + tempCharRecs[index].height = (float)charHeight; int charWidth = 0; while (!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++; - tempCharRecs[index].width = charWidth; + tempCharRecs[index].width = (float)charWidth; index++; @@ -682,7 +895,7 @@ static Font LoadImageFont(Image image, Color key, int firstChar) spriteFont.chars[i].advanceX = 0; } - spriteFont.baseSize = spriteFont.chars[0].rec.height; + spriteFont.baseSize = (int)spriteFont.chars[0].rec.height; TraceLog(LOG_INFO, "Image file loaded correctly as Font"); @@ -764,22 +977,23 @@ static Font LoadBMFont(const char *fileName) { Image imCopy = ImageCopy(imFont); - for (int i = 0; i < imCopy.width*imCopy.height; i++) ((unsigned char *)imCopy.data)[i] = 0xff; // WHITE pixel + for (int i = 0; i < imCopy.width*imCopy.height; i++) ((unsigned char *)imCopy.data)[i] = 0xff; ImageAlphaMask(&imCopy, imFont); font.texture = LoadTextureFromImage(imCopy); UnloadImage(imCopy); } else font.texture = LoadTextureFromImage(imFont); + + UnloadImage(imFont); + free(texPath); + + // Fill font characters info data font.baseSize = fontSize; font.charsCount = charsCount; font.chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); - UnloadImage(imFont); - - free(texPath); - int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX; for (int i = 0; i < charsCount; i++) @@ -790,7 +1004,7 @@ static Font LoadBMFont(const char *fileName) // Save data properly in sprite font font.chars[i].value = charId; - font.chars[i].rec = (Rectangle){ charX, charY, charWidth, charHeight }; + font.chars[i].rec = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; font.chars[i].offsetX = charOffsetX; font.chars[i].offsetY = charOffsetY; font.chars[i].advanceX = charAdvanceX; @@ -801,116 +1015,10 @@ static Font LoadBMFont(const char *fileName) if (font.texture.id == 0) { UnloadFont(font); - font = GetDefaultFont(); + font = GetFontDefault(); } else TraceLog(LOG_INFO, "[%s] Font loaded successfully", fileName); return font; } -#endif - -#if defined(SUPPORT_FILEFORMAT_TTF) -// Generate a sprite font from TTF file data (font size required) -// TODO: Review texture packing method and generation (use oversampling) -static Font LoadTTF(const char *fileName, int fontSize, int charsCount, int *fontChars) -{ - #define MAX_TTF_SIZE 16 // Maximum ttf file size in MB - - // NOTE: Font texture size is predicted (being as much conservative as possible) - // Predictive method consist of supposing same number of chars by line-column (sqrtf) - // and a maximum character width of 3/4 of fontSize... it worked ok with all my tests... - - // Calculate next power-of-two value - float guessSize = ceilf((float)fontSize*3/4)*ceilf(sqrtf((float)charsCount)); - int textureSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT - - TraceLog(LOG_INFO, "TTF spritefont loading: Predicted texture size: %ix%i", textureSize, textureSize); - - unsigned char *ttfBuffer = (unsigned char *)malloc(MAX_TTF_SIZE*1024*1024); - unsigned char *dataBitmap = (unsigned char *)malloc(textureSize*textureSize*sizeof(unsigned char)); // One channel bitmap returned! - stbtt_bakedchar *charData = (stbtt_bakedchar *)malloc(sizeof(stbtt_bakedchar)*charsCount); - - Font font = { 0 }; - - FILE *ttfFile = fopen(fileName, "rb"); - - if (ttfFile == NULL) - { - TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName); - return font; - } - - // NOTE: We try reading up to 16 MB of elements of 1 byte - fread(ttfBuffer, 1, MAX_TTF_SIZE*1024*1024, ttfFile); - - // Find font baseline (vertical origin of the font) - // NOTE: This value is required because y-offset depends on it! - stbtt_fontinfo fontInfo; - int ascent, baseline; - float scale; - - stbtt_InitFont(&fontInfo, ttfBuffer, 0); - scale = stbtt_ScaleForPixelHeight(&fontInfo, fontSize); - stbtt_GetFontVMetrics(&fontInfo, &ascent, 0, 0); - baseline = (int)(ascent*scale); - - if (fontChars[0] != 32) TraceLog(LOG_WARNING, "TTF spritefont loading: first character is not SPACE(32) character"); - - // NOTE: Using stb_truetype crappy packing method, no guarantee the font fits the image... - // TODO: Replace this function by a proper packing method and support random chars order, - // we already receive a list (fontChars) with the ordered expected characters - int result = stbtt_BakeFontBitmap(ttfBuffer, 0, fontSize, dataBitmap, textureSize, textureSize, fontChars[0], charsCount, charData); - - //if (result > 0) TraceLog(LOG_INFO, "TTF spritefont loading: first unused row of generated bitmap: %i", result); - if (result < 0) TraceLog(LOG_WARNING, "TTF spritefont loading: Not all the characters fit in the font"); - - free(ttfBuffer); - - // Convert image data from grayscale to to UNCOMPRESSED_GRAY_ALPHA - unsigned char *dataGrayAlpha = (unsigned char *)malloc(textureSize*textureSize*sizeof(unsigned char)*2); // Two channels - - for (int i = 0, k = 0; i < textureSize*textureSize; i++, k += 2) - { - dataGrayAlpha[k] = 255; - dataGrayAlpha[k + 1] = dataBitmap[i]; - } - - free(dataBitmap); - - // Sprite font generation from TTF extracted data - Image image; - image.width = textureSize; - image.height = textureSize; - image.mipmaps = 1; - image.format = UNCOMPRESSED_GRAY_ALPHA; - image.data = dataGrayAlpha; - - font.texture = LoadTextureFromImage(image); - - //SavePNG("generated_ttf_image.png", (unsigned char *)image.data, image.width, image.height, 2); - - UnloadImage(image); // Unloads dataGrayAlpha - - font.baseSize = fontSize; - font.charsCount = charsCount; - font.chars = (CharInfo *)malloc(font.charsCount*sizeof(CharInfo)); - - for (int i = 0; i < font.charsCount; i++) - { - font.chars[i].value = fontChars[i]; - - font.chars[i].rec.x = (int)charData[i].x0; - font.chars[i].rec.y = (int)charData[i].y0; - font.chars[i].rec.width = (int)charData[i].x1 - (int)charData[i].x0; - font.chars[i].rec.height = (int)charData[i].y1 - (int)charData[i].y0; - - font.chars[i].offsetX = charData[i].xoff; - font.chars[i].offsetY = baseline + charData[i].yoff; - font.chars[i].advanceX = (int)charData[i].xadvance; - } - - free(charData); - - return font; -} -#endif +#endif \ No newline at end of file diff --git a/raylib/text.go b/raylib/text.go index 909a2bd..26b3cda 100644 --- a/raylib/text.go +++ b/raylib/text.go @@ -1,4 +1,4 @@ -package raylib +package rl /* #include "raylib.h" @@ -17,9 +17,9 @@ func (s *Font) cptr() *C.Font { return (*C.Font)(unsafe.Pointer(s)) } -// GetDefaultFont - Get the default Font -func GetDefaultFont() Font { - ret := C.GetDefaultFont() +// GetFontDefault - Get the default Font +func GetFontDefault() Font { + ret := C.GetFontDefault() v := newFontFromPointer(unsafe.Pointer(&ret)) return v } diff --git a/raylib/textures.c b/raylib/textures.c index 56d2aa8..3099bf6 100644 --- a/raylib/textures.c +++ b/raylib/textures.c @@ -19,12 +19,15 @@ * Selecte desired fileformats to be supported for image data loading. Some of those formats are * supported by default, to remove support, just comment unrequired #define in this module * +* #define SUPPORT_IMAGE_EXPORT +* Support image export in multiple file formats +* * #define SUPPORT_IMAGE_MANIPULATION * Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... * If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT() * * #define SUPPORT_IMAGE_GENERATION -* Support proedural image generation functionality (gradient, spot, perlin-noise, cellular) +* Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) * * DEPENDENCIES: * stb_image - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC) @@ -52,9 +55,9 @@ * 3. This notice may not be removed or altered from any source distribution. * **********************************************************************************************/ -#include "config.h" -#include "raylib.h" +#include "config.h" // Defines module configuration flags +#include "raylib.h" // Declares module functions #include // Required for: malloc(), free() #include // Required for: strcmp(), strrchr(), strncmp() @@ -103,6 +106,11 @@ // NOTE: Used to read image data (multiple formats support) #endif +#if defined(SUPPORT_IMAGE_EXPORT) + #define STB_IMAGE_WRITE_IMPLEMENTATION + #include "external/stb_image_write.h" // Required for: stbi_write_*() +#endif + #if defined(SUPPORT_IMAGE_MANIPULATION) #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() @@ -140,6 +148,7 @@ static Image LoadPKM(const char *fileName); // Load PKM file #endif #if defined(SUPPORT_FILEFORMAT_KTX) static Image LoadKTX(const char *fileName); // Load KTX file +static void SaveKTX(Image image, const char *fileName); // Save image data as KTX file #endif #if defined(SUPPORT_FILEFORMAT_PVR) static Image LoadPVR(const char *fileName); // Load PVR file @@ -314,7 +323,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int // NOTE: fread() returns num read elements instead of bytes, // to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element) - int bytes = fread(image.data, 1, size, rawFile); + size_t bytes = fread(image.data, 1, size, rawFile); // Check if data has been read successfully if (bytes < size) @@ -405,87 +414,231 @@ void UnloadRenderTexture(RenderTexture2D target) } // Get pixel data from image in the form of Color struct array -// TODO: Support float pixel data retrieval Color *GetImageData(Image image) { Color *pixels = (Color *)malloc(image.width*image.height*sizeof(Color)); - for (int i = 0, k = 0; i < image.width*image.height; i++) + if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats"); + else { - switch (image.format) + if ((image.format == UNCOMPRESSED_R32) || + (image.format == UNCOMPRESSED_R32G32B32) || + (image.format == UNCOMPRESSED_R32G32B32A32)) TraceLog(LOG_WARNING, "32bit pixel format converted to 8bit per channel"); + + for (int i = 0, k = 0; i < image.width*image.height; i++) { - case UNCOMPRESSED_GRAYSCALE: + switch (image.format) { - pixels[i].r = ((unsigned char *)image.data)[i]; - pixels[i].g = ((unsigned char *)image.data)[i]; - pixels[i].b = ((unsigned char *)image.data)[i]; - pixels[i].a = 255; + case UNCOMPRESSED_GRAYSCALE: + { + pixels[i].r = ((unsigned char *)image.data)[i]; + pixels[i].g = ((unsigned char *)image.data)[i]; + pixels[i].b = ((unsigned char *)image.data)[i]; + pixels[i].a = 255; - } break; - case UNCOMPRESSED_GRAY_ALPHA: - { - pixels[i].r = ((unsigned char *)image.data)[k]; - pixels[i].g = ((unsigned char *)image.data)[k]; - pixels[i].b = ((unsigned char *)image.data)[k]; - pixels[i].a = ((unsigned char *)image.data)[k + 1]; + } break; + case UNCOMPRESSED_GRAY_ALPHA: + { + pixels[i].r = ((unsigned char *)image.data)[k]; + pixels[i].g = ((unsigned char *)image.data)[k]; + pixels[i].b = ((unsigned char *)image.data)[k]; + pixels[i].a = ((unsigned char *)image.data)[k + 1]; - k += 2; - } break; - case UNCOMPRESSED_R5G5B5A1: - { - unsigned short pixel = ((unsigned short *)image.data)[i]; + k += 2; + } break; + case UNCOMPRESSED_R5G5B5A1: + { + unsigned short pixel = ((unsigned short *)image.data)[i]; - pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31)); - pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31)); - pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31)); - pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255); + pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31)); + pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31)); + pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31)); + pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255); - } break; - case UNCOMPRESSED_R5G6B5: - { - unsigned short pixel = ((unsigned short *)image.data)[i]; + } break; + case UNCOMPRESSED_R5G6B5: + { + unsigned short pixel = ((unsigned short *)image.data)[i]; - pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31)); - pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63)); - pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31)); - pixels[i].a = 255; + pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31)); + pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63)); + pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31)); + pixels[i].a = 255; - } break; - case UNCOMPRESSED_R4G4B4A4: - { - unsigned short pixel = ((unsigned short *)image.data)[i]; + } break; + case UNCOMPRESSED_R4G4B4A4: + { + unsigned short pixel = ((unsigned short *)image.data)[i]; - pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15)); - pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15)); - pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15)); - pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15)); + pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15)); + pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15)); + pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15)); + pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15)); - } break; - case UNCOMPRESSED_R8G8B8A8: - { - pixels[i].r = ((unsigned char *)image.data)[k]; - pixels[i].g = ((unsigned char *)image.data)[k + 1]; - pixels[i].b = ((unsigned char *)image.data)[k + 2]; - pixels[i].a = ((unsigned char *)image.data)[k + 3]; + } break; + case UNCOMPRESSED_R8G8B8A8: + { + pixels[i].r = ((unsigned char *)image.data)[k]; + pixels[i].g = ((unsigned char *)image.data)[k + 1]; + pixels[i].b = ((unsigned char *)image.data)[k + 2]; + pixels[i].a = ((unsigned char *)image.data)[k + 3]; - k += 4; - } break; - case UNCOMPRESSED_R8G8B8: - { - pixels[i].r = (unsigned char)((unsigned char *)image.data)[k]; - pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1]; - pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2]; - pixels[i].a = 255; + k += 4; + } break; + case UNCOMPRESSED_R8G8B8: + { + pixels[i].r = (unsigned char)((unsigned char *)image.data)[k]; + pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1]; + pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2]; + pixels[i].a = 255; - k += 3; - } break; - default: TraceLog(LOG_WARNING, "Format not supported for pixel data retrieval"); break; + k += 3; + } break; + case UNCOMPRESSED_R32: + { + pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f); + pixels[i].g = 0; + pixels[i].b = 0; + pixels[i].a = 255; + + } break; + case UNCOMPRESSED_R32G32B32: + { + pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f); + pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f); + pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f); + pixels[i].a = 255; + + k += 3; + } + case UNCOMPRESSED_R32G32B32A32: + { + pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f); + pixels[i].g = (unsigned char)(((float *)image.data)[k]*255.0f); + pixels[i].b = (unsigned char)(((float *)image.data)[k]*255.0f); + pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f); + + k += 4; + } + default: break; + } } } return pixels; } +// Get pixel data from image as Vector4 array (float normalized) +Vector4 *GetImageDataNormalized(Image image) +{ + Vector4 *pixels = (Vector4 *)malloc(image.width*image.height*sizeof(Vector4)); + + if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats"); + else + { + for (int i = 0, k = 0; i < image.width*image.height; i++) + { + switch (image.format) + { + case UNCOMPRESSED_GRAYSCALE: + { + pixels[i].x = (float)((unsigned char *)image.data)[i]/255.0f; + pixels[i].y = (float)((unsigned char *)image.data)[i]/255.0f; + pixels[i].z = (float)((unsigned char *)image.data)[i]/255.0f; + pixels[i].w = 1.0f; + + } break; + case UNCOMPRESSED_GRAY_ALPHA: + { + pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f; + pixels[i].y = (float)((unsigned char *)image.data)[k]/255.0f; + pixels[i].z = (float)((unsigned char *)image.data)[k]/255.0f; + pixels[i].w = (float)((unsigned char *)image.data)[k + 1]/255.0f; + + k += 2; + } break; + case UNCOMPRESSED_R5G5B5A1: + { + unsigned short pixel = ((unsigned short *)image.data)[i]; + + pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31); + pixels[i].y = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31); + pixels[i].z = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31); + pixels[i].w = ((pixel & 0b0000000000000001) == 0) ? 0.0f : 1.0f; + + } break; + case UNCOMPRESSED_R5G6B5: + { + unsigned short pixel = ((unsigned short *)image.data)[i]; + + pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31); + pixels[i].y = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63); + pixels[i].z = (float)(pixel & 0b0000000000011111)*(1.0f/31); + pixels[i].w = 1.0f; + + } break; + case UNCOMPRESSED_R4G4B4A4: + { + unsigned short pixel = ((unsigned short *)image.data)[i]; + + pixels[i].x = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15); + pixels[i].y = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15); + pixels[i].z = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15); + pixels[i].w = (float)(pixel & 0b0000000000001111)*(1.0f/15); + + } break; + case UNCOMPRESSED_R8G8B8A8: + { + pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f; + pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f; + pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f; + pixels[i].w = (float)((unsigned char *)image.data)[k + 3]/255.0f; + + k += 4; + } break; + case UNCOMPRESSED_R8G8B8: + { + pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f; + pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f; + pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f; + pixels[i].w = 1.0f; + + k += 3; + } break; + case UNCOMPRESSED_R32: + { + pixels[i].x = ((float *)image.data)[k]; + pixels[i].y = 0.0f; + pixels[i].z = 0.0f; + pixels[i].w = 1.0f; + + } break; + case UNCOMPRESSED_R32G32B32: + { + pixels[i].x = ((float *)image.data)[k]; + pixels[i].y = ((float *)image.data)[k + 1]; + pixels[i].z = ((float *)image.data)[k + 2]; + pixels[i].w = 1.0f; + + k += 3; + } + case UNCOMPRESSED_R32G32B32A32: + { + pixels[i].x = ((float *)image.data)[k]; + pixels[i].y = ((float *)image.data)[k + 1]; + pixels[i].z = ((float *)image.data)[k + 2]; + pixels[i].w = ((float *)image.data)[k + 3]; + + k += 4; + } + default: break; + } + } + } + + return pixels; +} + // Get pixel data size in bytes (image or texture) // NOTE: Size depends on pixel format int GetPixelDataSize(int width, int height, int format) @@ -562,12 +715,33 @@ void UpdateTexture(Texture2D texture, const void *pixels) rlUpdateTexture(texture.id, texture.width, texture.height, texture.format, pixels); } -// Export image as a PNG file -void ExportImage(const char *fileName, Image image) +// Export image data to file +// NOTE: File format depends on fileName extension +void ExportImage(Image image, const char *fileName) { + int success = 0; + // NOTE: Getting Color array as RGBA unsigned char values unsigned char *imgData = (unsigned char *)GetImageData(image); - SavePNG(fileName, imgData, image.width, image.height, 4); + + if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4); + else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData); + else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData); + else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100 + else if (IsFileExtension(fileName, ".ktx")) SaveKTX(image, fileName); + else if (IsFileExtension(fileName, ".raw")) + { + // Export raw pixel data (without header) + // NOTE: It's up to the user to track image parameters + FILE *rawFile = fopen(fileName, "wb"); + fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile); + fclose(rawFile); + } + else if (IsFileExtension(fileName, ".h")) { } // TODO: Export pixel data as an array of bytes + + if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName); + else TraceLog(LOG_WARNING, "Image could not be exported."); + free(imgData); } @@ -643,8 +817,7 @@ void ImageToPOT(Image *image, Color fillColor) int format = image->format; // Store image data format to reconvert later - // TODO: Image width and height changes... do we want to store new values or keep the old ones? - // NOTE: Issues when using image.width and image.height for sprite animations... + // NOTE: Image size changes, new width and height *image = LoadImageEx(pixelsPOT, potWidth, potHeight); free(pixelsPOT); // Free POT pixels data @@ -656,11 +829,11 @@ void ImageToPOT(Image *image, Color fillColor) // Convert image data to desired format void ImageFormat(Image *image, int newFormat) { - if (image->format != newFormat) + if ((newFormat != 0) && (image->format != newFormat)) { if ((image->format < COMPRESSED_DXT1_RGB) && (newFormat < COMPRESSED_DXT1_RGB)) { - Color *pixels = GetImageData(*image); + Vector4 *pixels = GetImageDataNormalized(*image); // Supports 8 to 32 bit per channel free(image->data); // WARNING! We loose mipmaps data --> Regenerated at the end... image->data = NULL; @@ -676,18 +849,18 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0; i < image->width*image->height; i++) { - ((unsigned char *)image->data)[i] = (unsigned char)((float)pixels[i].r*0.299f + (float)pixels[i].g*0.587f + (float)pixels[i].b*0.114f); + ((unsigned char *)image->data)[i] = (unsigned char)((pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f)*255.0f); } } break; case UNCOMPRESSED_GRAY_ALPHA: { - image->data = (unsigned char *)malloc(image->width*image->height*2*sizeof(unsigned char)); + image->data = (unsigned char *)malloc(image->width*image->height*2*sizeof(unsigned char)); - for (int i = 0; i < image->width*image->height*2; i += 2, k++) + for (int i = 0; i < image->width*image->height*2; i += 2, k++) { - ((unsigned char *)image->data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f); - ((unsigned char *)image->data)[i + 1] = pixels[k].a; + ((unsigned char *)image->data)[i] = (unsigned char)((pixels[k].x*0.299f + (float)pixels[k].y*0.587f + (float)pixels[k].z*0.114f)*255.0f); + ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].w*255.0f); } } break; @@ -701,9 +874,9 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0; i < image->width*image->height; i++) { - r = (unsigned char)(round((float)pixels[i].r*31.0f/255)); - g = (unsigned char)(round((float)pixels[i].g*63.0f/255)); - b = (unsigned char)(round((float)pixels[i].b*31.0f/255)); + r = (unsigned char)(round(pixels[i].x*31.0f)); + g = (unsigned char)(round(pixels[i].y*63.0f)); + b = (unsigned char)(round(pixels[i].z*31.0f)); ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b; } @@ -715,9 +888,9 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++) { - ((unsigned char *)image->data)[i] = pixels[k].r; - ((unsigned char *)image->data)[i + 1] = pixels[k].g; - ((unsigned char *)image->data)[i + 2] = pixels[k].b; + ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f); + ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f); + ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f); } } break; case UNCOMPRESSED_R5G5B5A1: @@ -733,10 +906,10 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0; i < image->width*image->height; i++) { - r = (unsigned char)(round((float)pixels[i].r*31.0f/255)); - g = (unsigned char)(round((float)pixels[i].g*31.0f/255)); - b = (unsigned char)(round((float)pixels[i].b*31.0f/255)); - a = (pixels[i].a > ALPHA_THRESHOLD) ? 1 : 0; + r = (unsigned char)(round(pixels[i].x*31.0f)); + g = (unsigned char)(round(pixels[i].y*31.0f)); + b = (unsigned char)(round(pixels[i].z*31.0f)); + a = (pixels[i].w > ((float)ALPHA_THRESHOLD/255.0f)) ? 1 : 0; ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a; } @@ -753,10 +926,10 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0; i < image->width*image->height; i++) { - r = (unsigned char)(round((float)pixels[i].r*15.0f/255)); - g = (unsigned char)(round((float)pixels[i].g*15.0f/255)); - b = (unsigned char)(round((float)pixels[i].b*15.0f/255)); - a = (unsigned char)(round((float)pixels[i].a*15.0f/255)); + r = (unsigned char)(round(pixels[i].x*15.0f)); + g = (unsigned char)(round(pixels[i].y*15.0f)); + b = (unsigned char)(round(pixels[i].z*15.0f)); + a = (unsigned char)(round(pixels[i].w*15.0f)); ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a; } @@ -768,19 +941,21 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++) { - ((unsigned char *)image->data)[i] = pixels[k].r; - ((unsigned char *)image->data)[i + 1] = pixels[k].g; - ((unsigned char *)image->data)[i + 2] = pixels[k].b; - ((unsigned char *)image->data)[i + 3] = pixels[k].a; + ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f); + ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f); + ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f); + ((unsigned char *)image->data)[i + 3] = (unsigned char)(pixels[k].w*255.0f); } } break; case UNCOMPRESSED_R32: { + // WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit + image->data = (float *)malloc(image->width*image->height*sizeof(float)); for (int i = 0; i < image->width*image->height; i++) { - ((float *)image->data)[i] = (float)((float)pixels[i].r*0.299f/255.0f + (float)pixels[i].g*0.587f/255.0f + (float)pixels[i].b*0.114f/255.0f); + ((float *)image->data)[i] = (float)(pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f); } } break; case UNCOMPRESSED_R32G32B32: @@ -789,9 +964,9 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++) { - ((float *)image->data)[i] = (float)pixels[k].r/255.0f; - ((float *)image->data)[i + 1] = (float)pixels[k].g/255.0f; - ((float *)image->data)[i + 2] = (float)pixels[k].b/255.0f; + ((float *)image->data)[i] = pixels[k].x; + ((float *)image->data)[i + 1] = pixels[k].y; + ((float *)image->data)[i + 2] = pixels[k].z; } } break; case UNCOMPRESSED_R32G32B32A32: @@ -800,10 +975,10 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++) { - ((float *)image->data)[i] = (float)pixels[k].r/255.0f; - ((float *)image->data)[i + 1] = (float)pixels[k].g/255.0f; - ((float *)image->data)[i + 2] = (float)pixels[k].b/255.0f; - ((float *)image->data)[i + 3] = (float)pixels[k].a/255.0f; + ((float *)image->data)[i] = pixels[k].x; + ((float *)image->data)[i + 1] = pixels[k].y; + ((float *)image->data)[i + 2] = pixels[k].z; + ((float *)image->data)[i + 3] = pixels[k].w; } } break; default: break; @@ -811,13 +986,13 @@ void ImageFormat(Image *image, int newFormat) free(pixels); pixels = NULL; + // In case original image had mipmaps, generate mipmaps for formated image // NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost if (image->mipmaps > 1) { image->mipmaps = 1; - assert(image->data != NULL); - ImageMipmaps(image); + if (image->data != NULL) ImageMipmaps(image); } } else TraceLog(LOG_WARNING, "Image data format is compressed, can not be converted"); @@ -903,16 +1078,16 @@ void ImageAlphaCrop(Image *image, float threshold) minx = i%image->width; miny = -(-((i/image->width) + 1) + 1); - if (crop.y == 0) crop.y = miny; + if (crop.y == 0.0f) crop.y = (float)miny; - if (crop.x == 0) crop.x = minx; - else if (minx < crop.x) crop.x = minx; + if (crop.x == 0.0f) crop.x = (float)minx; + else if (minx < crop.x) crop.x = (float)minx; - if (crop.width == 0) crop.width = minx; - else if (crop.width < minx) crop.width = minx; + if (crop.width == 0.0f) crop.width = (float)minx; + else if (crop.width < minx) crop.width = (float)minx; - if (crop.height == 0) crop.height = miny; - else if (crop.height < miny) crop.height = miny; + if (crop.height == 0.0f) crop.height = (float)miny; + else if (crop.height < (float)miny) crop.height = (float)miny; } } @@ -1063,6 +1238,29 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight) free(pixels); } +// Resize canvas and fill with color +// NOTE: Resize offset is relative to the top-left corner of the original image +void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, int offsetY, Color color) +{ + Image imTemp = GenImageColor(newWidth, newHeight, color); + Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; + Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)srcRec.width, (float)srcRec.height }; + + // TODO: Review different scaling situations + + if ((newWidth > image->width) && (newHeight > image->height)) + { + ImageDraw(&imTemp, *image, srcRec, dstRec); + ImageFormat(&imTemp, image->format); + UnloadImage(*image); + *image = imTemp; + } + else + { + // TODO: ImageCrop(), define proper cropping rectangle + } +} + // Generate all mipmap levels for a provided image // NOTE 1: Supports POT and NPOT images // NOTE 2: image.data is scaled to include mipmap levels @@ -1268,7 +1466,6 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) { srcRec.height = src.height - srcRec.y; TraceLog(LOG_WARNING, "Source rectangle height out of bounds, rescaled height: %i", srcRec.height); - cropRequired = true; } Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it @@ -1280,10 +1477,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) if (dstRec.y < 0) dstRec.y = 0; // Scale source image in case destination rec size is different than source rec size - if ((dstRec.width != srcRec.width) || (dstRec.height != srcRec.height)) - { - ImageResize(&srcCopy, dstRec.width, dstRec.height); - } + if ((dstRec.width != srcRec.width) || (dstRec.height != srcRec.height)) ImageResize(&srcCopy, (int)dstRec.width, (int)dstRec.height); if ((dstRec.x + dstRec.width) > dst->width) { @@ -1363,9 +1557,9 @@ Image ImageText(const char *text, int fontSize, Color color) { int defaultFontSize = 10; // Default Font chars height in pixel if (fontSize < defaultFontSize) fontSize = defaultFontSize; - int spacing = (float)fontSize/defaultFontSize; + int spacing = fontSize / defaultFontSize; - Image imText = ImageTextEx(GetDefaultFont(), text, (float)fontSize, (float)spacing, color); + Image imText = ImageTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing, color); return imText; } @@ -1379,7 +1573,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co unsigned char character; // Current character // TODO: ISSUE: Measured text size does not seem to be correct... issue on ImageDraw() - Vector2 imSize = MeasureTextEx(font, text, font.baseSize, spacing); + Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); TraceLog(LOG_DEBUG, "Text Image size: %f, %f", imSize.x, imSize.y); @@ -1388,7 +1582,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Define ImageFont struct? or include Image spritefont in Font struct? Image imFont = GetTextureData(font.texture); - ImageColorTint(&imFont, tint); // Apply color tint to font + ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8); // Make sure image format could be properly colored! + + ImageColorTint(&imFont, tint); // Apply color tint to font // Create image to store text Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); @@ -1421,12 +1617,12 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co if ((unsigned char)text[i] != ' ') { - ImageDraw(&imText, imFont, letter.rec, (Rectangle){ posX + letter.offsetX, - letter.offsetY, letter.rec.width, letter.rec.height }); + ImageDraw(&imText, imFont, letter.rec, (Rectangle){ (float)(posX + letter.offsetX), + (float)letter.offsetY, (float)letter.rec.width, (float)letter.rec.height }); } - if (letter.advanceX == 0) posX += letter.rec.width + spacing; - else posX += letter.advanceX + spacing; + if (letter.advanceX == 0) posX += (int)(letter.rec.width + spacing); + else posX += letter.advanceX + (int)spacing; } } @@ -1439,7 +1635,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co TraceLog(LOG_INFO, "Image text scaled by factor: %f", scaleFactor); // Using nearest-neighbor scaling algorithm for default font - if (font.texture.id == GetDefaultFont().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); + if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); } @@ -1449,9 +1645,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Draw rectangle within an image void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color) { - Image imRec = GenImageColor(rec.width, rec.height, color); + Image imRec = GenImageColor((int)rec.width, (int)rec.height, color); - Rectangle dstRec = { position.x, position.y, imRec.width, imRec.height }; + Rectangle dstRec = { position.x, position.y, (float)imRec.width, (float)imRec.height }; ImageDraw(dst, imRec, rec, dstRec); @@ -1462,7 +1658,7 @@ void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color) { // NOTE: For default font, sapcing is set to desired font size / default font size (10) - ImageDrawTextEx(dst, position, GetDefaultFont(), text, (float)fontSize, (float)fontSize/10, color); + ImageDrawTextEx(dst, position, GetFontDefault(), text, (float)fontSize, (float)fontSize/10, color); } // Draw text (custom sprite font) within an image (destination) @@ -1470,8 +1666,8 @@ void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text, { Image imText = ImageTextEx(font, text, fontSize, spacing, color); - Rectangle srcRec = { 0, 0, imText.width, imText.height }; - Rectangle dstRec = { position.x, position.y, imText.width, imText.height }; + Rectangle srcRec = { 0.0f, 0.0f, (float)imText.width, (float)imText.height }; + Rectangle dstRec = { position.x, position.y, (float)imText.width, (float)imText.height }; ImageDraw(dst, imText, srcRec, dstRec); @@ -1482,7 +1678,7 @@ void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text, void ImageFlipVertical(Image *image) { Color *srcPixels = GetImageData(*image); - Color *dstPixels = (Color *)malloc(sizeof(Color)*image->width*image->height); + Color *dstPixels = (Color *)malloc(image->width*image->height*sizeof(Color)); for (int y = 0; y < image->height; y++) { @@ -1506,7 +1702,7 @@ void ImageFlipVertical(Image *image) void ImageFlipHorizontal(Image *image) { Color *srcPixels = GetImageData(*image); - Color *dstPixels = (Color *)malloc(sizeof(Color)*image->width*image->height); + Color *dstPixels = (Color *)malloc(image->width*image->height*sizeof(Color)); for (int y = 0; y < image->height; y++) { @@ -1526,6 +1722,58 @@ void ImageFlipHorizontal(Image *image) image->data = processed.data; } +// Rotate image clockwise 90deg +void ImageRotateCW(Image *image) +{ + Color *srcPixels = GetImageData(*image); + Color *rotPixels = (Color *)malloc(image->width*image->height*sizeof(Color)); + + for (int y = 0; y < image->height; y++) + { + for (int x = 0; x < image->width; x++) + { + rotPixels[x*image->height + (image->height - y - 1)] = srcPixels[y*image->width + x]; + } + } + + Image processed = LoadImageEx(rotPixels, image->height, image->width); + ImageFormat(&processed, image->format); + UnloadImage(*image); + + free(srcPixels); + free(rotPixels); + + image->data = processed.data; + image->width = processed.width; + image->height = processed.height; +} + +// Rotate image counter-clockwise 90deg +void ImageRotateCCW(Image *image) +{ + Color *srcPixels = GetImageData(*image); + Color *rotPixels = (Color *)malloc(image->width*image->height*sizeof(Color)); + + for (int y = 0; y < image->height; y++) + { + for (int x = 0; x < image->width; x++) + { + rotPixels[x*image->height + y] = srcPixels[y*image->width + (image->width - x - 1)]; + } + } + + Image processed = LoadImageEx(rotPixels, image->height, image->width); + ImageFormat(&processed, image->format); + UnloadImage(*image); + + free(srcPixels); + free(rotPixels); + + image->data = processed.data; + image->width = processed.width; + image->height = processed.height; +} + // Modify image color: tint void ImageColorTint(Image *image, Color color) { @@ -1540,10 +1788,11 @@ void ImageColorTint(Image *image, Color color) { for (int x = 0; x < image->width; x++) { - unsigned char r = 255*((float)pixels[y*image->width + x].r/255*cR); - unsigned char g = 255*((float)pixels[y*image->width + x].g/255*cG); - unsigned char b = 255*((float)pixels[y*image->width + x].b/255*cB); - unsigned char a = 255*((float)pixels[y*image->width + x].a/255*cA); + int index = y * image->width + x; + unsigned char r = 255*((float)pixels[index].r/255*cR); + unsigned char g = 255*((float)pixels[index].g/255*cG); + unsigned char b = 255*((float)pixels[index].b/255*cB); + unsigned char a = 255*((float)pixels[index].a/255*cA); pixels[y*image->width + x].r = r; pixels[y*image->width + x].g = g; @@ -1682,6 +1931,36 @@ void ImageColorBrightness(Image *image, int brightness) image->data = processed.data; } + +// Modify image color: replace color +void ImageColorReplace(Image *image, Color color, Color replace) +{ + Color *pixels = GetImageData(*image); + + for (int y = 0; y < image->height; y++) + { + for (int x = 0; x < image->width; x++) + { + if ((pixels[y*image->width + x].r == color.r) && + (pixels[y*image->width + x].g == color.g) && + (pixels[y*image->width + x].b == color.b) && + (pixels[y*image->width + x].a == color.a)) + { + pixels[y*image->width + x].r = replace.r; + pixels[y*image->width + x].g = replace.g; + pixels[y*image->width + x].b = replace.b; + pixels[y*image->width + x].a = replace.a; + } + } + } + + Image processed = LoadImageEx(pixels, image->width, image->height); + ImageFormat(&processed, image->format); + UnloadImage(*image); + free(pixels); + + image->data = processed.data; +} #endif // SUPPORT_IMAGE_MANIPULATION #if defined(SUPPORT_IMAGE_GENERATION) @@ -1761,8 +2040,8 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner, float dist = hypotf((float)x - centerX, (float)y - centerY); float factor = (dist - radius*density)/(radius*(1.0f - density)); - factor = fmax(factor, 0.f); - factor = fmin(factor, 1.f); // dist can be bigger than radius so we have to check + factor = (float)fmax(factor, 0.f); + factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor)); pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor)); @@ -1860,7 +2139,7 @@ Image GenImageCellular(int width, int height, int tileSize) { int y = (i/seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1); int x = (i%seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1); - seeds[i] = (Vector2){x, y}; + seeds[i] = (Vector2){ (float)x, (float)y}; } for (int y = 0; y < height; y++) @@ -1871,7 +2150,7 @@ Image GenImageCellular(int width, int height, int tileSize) { int tileX = x/tileSize; - float minDistance = strtod("Inf", NULL); + float minDistance = (float)strtod("Inf", NULL); // Check all adjacent tiles for (int i = -1; i < 2; i++) @@ -1884,8 +2163,8 @@ Image GenImageCellular(int width, int height, int tileSize) Vector2 neighborSeed = seeds[(tileY + j)*seedsPerRow + tileX + i]; - float dist = hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y); - minDistance = fmin(minDistance, dist); + float dist = (float)hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y); + minDistance = (float)fmin(minDistance, dist); } } @@ -2019,9 +2298,9 @@ void DrawTextureV(Texture2D texture, Vector2 position, Color tint) // Draw a Texture2D with extended parameters void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint) { - Rectangle sourceRec = { 0, 0, texture.width, texture.height }; - Rectangle destRec = { position.x, position.y, texture.width*scale, texture.height*scale }; - Vector2 origin = { 0, 0 }; + Rectangle sourceRec = { 0.0f, 0.0f, (float)texture.width, (float)texture.height }; + Rectangle destRec = { position.x, position.y, (float)texture.width*scale, (float)texture.height*scale }; + Vector2 origin = { 0.0f, 0.0f }; DrawTexturePro(texture, sourceRec, destRec, origin, rotation, tint); } @@ -2029,8 +2308,8 @@ void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float sc // Draw a part of a texture (defined by a rectangle) void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint) { - Rectangle destRec = { position.x, position.y, sourceRec.width, sourceRec.height }; - Vector2 origin = { 0, 0 }; + Rectangle destRec = { position.x, position.y, sourceRec.width, (float)fabs(sourceRec.height) }; + Vector2 origin = { 0.0f, 0.0f }; DrawTexturePro(texture, sourceRec, destRec, origin, 0.0f, tint); } @@ -2042,6 +2321,9 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V // Check if texture is valid if (texture.id > 0) { + float width = (float)texture.width; + float height = (float)texture.height; + if (sourceRec.width < 0) sourceRec.x -= sourceRec.width; if (sourceRec.height < 0) sourceRec.y -= sourceRec.height; @@ -2057,19 +2339,19 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer // Bottom-left corner for texture and quad - rlTexCoord2f(sourceRec.x/texture.width, sourceRec.y/texture.height); + rlTexCoord2f(sourceRec.x/width, sourceRec.y/height); rlVertex2f(0.0f, 0.0f); // Bottom-right corner for texture and quad - rlTexCoord2f(sourceRec.x/texture.width, (sourceRec.y + sourceRec.height)/texture.height); + rlTexCoord2f(sourceRec.x/width, (sourceRec.y + sourceRec.height)/height); rlVertex2f(0.0f, destRec.height); // Top-right corner for texture and quad - rlTexCoord2f((sourceRec.x + sourceRec.width)/texture.width, (sourceRec.y + sourceRec.height)/texture.height); + rlTexCoord2f((sourceRec.x + sourceRec.width)/width, (sourceRec.y + sourceRec.height)/height); rlVertex2f(destRec.width, destRec.height); // Top-left corner for texture and quad - rlTexCoord2f((sourceRec.x + sourceRec.width)/texture.width, sourceRec.y/texture.height); + rlTexCoord2f((sourceRec.x + sourceRec.width)/width, sourceRec.y/height); rlVertex2f(destRec.width, 0.0f); rlEnd(); rlPopMatrix(); @@ -2078,6 +2360,202 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V } } +void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destRec, Vector2 origin, float rotation, Color tint) +{ + if (texture.id > 0) + { + float width = (float)texture.width; + float height = (float)texture.height; + + float patchWidth = (destRec.width <= 0.0f)? 0.0f : destRec.width; + float patchHeight = (destRec.height <= 0.0f)? 0.0f : destRec.height; + + if (nPatchInfo.sourceRec.width < 0) nPatchInfo.sourceRec.x -= nPatchInfo.sourceRec.width; + if (nPatchInfo.sourceRec.height < 0) nPatchInfo.sourceRec.y -= nPatchInfo.sourceRec.height; + if (nPatchInfo.type == NPT_3PATCH_HORIZONTAL) patchHeight = nPatchInfo.sourceRec.height; + if (nPatchInfo.type == NPT_3PATCH_VERTICAL) patchWidth = nPatchInfo.sourceRec.width; + + bool drawCenter = true; + bool drawMiddle = true; + float leftBorder = (float)nPatchInfo.left; + float topBorder = (float)nPatchInfo.top; + float rightBorder = (float)nPatchInfo.right; + float bottomBorder = (float)nPatchInfo.bottom; + + // adjust the lateral (left and right) border widths in case patchWidth < texture.width + if (patchWidth <= (leftBorder + rightBorder) && nPatchInfo.type != NPT_3PATCH_VERTICAL) + { + drawCenter = false; + leftBorder = (leftBorder / (leftBorder + rightBorder)) * patchWidth; + rightBorder = patchWidth - leftBorder; + } + // adjust the lateral (top and bottom) border heights in case patchHeight < texture.height + if (patchHeight <= (topBorder + bottomBorder) && nPatchInfo.type != NPT_3PATCH_HORIZONTAL) + { + drawMiddle = false; + topBorder = (topBorder / (topBorder + bottomBorder)) * patchHeight; + bottomBorder = patchHeight - topBorder; + } + + Vector2 vertA, vertB, vertC, vertD; + vertA.x = 0.0f; // outer left + vertA.y = 0.0f; // outer top + vertB.x = leftBorder; // inner left + vertB.y = topBorder; // inner top + vertC.x = patchWidth - rightBorder; // inner right + vertC.y = patchHeight - bottomBorder; // inner bottom + vertD.x = patchWidth; // outer right + vertD.y = patchHeight; // outer bottom + + Vector2 coordA, coordB, coordC, coordD; + coordA.x = nPatchInfo.sourceRec.x / width; + coordA.y = nPatchInfo.sourceRec.y / height; + coordB.x = (nPatchInfo.sourceRec.x + leftBorder) / width; + coordB.y = (nPatchInfo.sourceRec.y + topBorder) / height; + coordC.x = (nPatchInfo.sourceRec.x + nPatchInfo.sourceRec.width - rightBorder) / width; + coordC.y = (nPatchInfo.sourceRec.y + nPatchInfo.sourceRec.height - bottomBorder) / height; + coordD.x = (nPatchInfo.sourceRec.x + nPatchInfo.sourceRec.width) / width; + coordD.y = (nPatchInfo.sourceRec.y + nPatchInfo.sourceRec.height) / height; + + rlEnableTexture(texture.id); + + rlPushMatrix(); + rlTranslatef(destRec.x, destRec.y, 0); + rlRotatef(rotation, 0, 0, 1); + rlTranslatef(-origin.x, -origin.y, 0); + + rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer + + if (nPatchInfo.type == NPT_9PATCH) + { + // ------------------------------------------------------------ + // TOP-LEFT QUAD + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad + if (drawCenter) + { + // TOP-CENTER QUAD + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad + } + // TOP-RIGHT QUAD + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad + if (drawMiddle) + { + // ------------------------------------------------------------ + // MIDDLE-LEFT QUAD + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad + if (drawCenter) + { + // MIDDLE-CENTER QUAD + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-left corner for texture and quad + } + + // MIDDLE-RIGHT QUAD + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-left corner for texture and quad + } + + // ------------------------------------------------------------ + // BOTTOM-LEFT QUAD + rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad + if (drawCenter) + { + // BOTTOM-CENTER QUAD + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-left corner for texture and quad + } + + // BOTTOM-RIGHT QUAD + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-left corner for texture and quad + } + else if (nPatchInfo.type == NPT_3PATCH_VERTICAL) + { + // TOP QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad + if (drawCenter) + { + // MIDDLE QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad + } + // BOTTOM QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad + } + else if (nPatchInfo.type == NPT_3PATCH_HORIZONTAL) + { + // LEFT QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad + if (drawCenter) + { + // CENTER QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad + } + // RIGHT QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad + } + rlEnd(); + rlPopMatrix(); + + rlDisableTexture(); + + } +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -2378,7 +2856,11 @@ static Image LoadKTX(const char *fileName) // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 // KTX file Header (64 bytes) - // https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + // v2.0 - http://github.khronos.org/KTX-Specification/ + + // TODO: Support KTX 2.2 specs! + typedef struct { char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 @@ -2432,7 +2914,7 @@ static Image LoadKTX(const char *fileName) if (ktxHeader.keyValueDataSize > 0) { - for (int i = 0; i < ktxHeader.keyValueDataSize; i++) fread(&unused, sizeof(unsigned char), 1, ktxFile); + for (unsigned int i = 0; i < ktxHeader.keyValueDataSize; i++) fread(&unused, sizeof(unsigned char), 1U, ktxFile); } int dataSize; @@ -2452,6 +2934,93 @@ static Image LoadKTX(const char *fileName) return image; } + +// Save image data as KTX file +// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018) +static void SaveKTX(Image image, const char *fileName) +{ + // KTX file Header (64 bytes) + // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + // v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation + + typedef struct { + char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n" + unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 + unsigned int glType; // For compressed textures, glType must equal 0 + unsigned int glTypeSize; // For compressed texture data, usually 1 + unsigned int glFormat; // For compressed textures is 0 + unsigned int glInternalFormat; // Compressed internal format + unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) // KTX 2.0: UInt32 vkFormat + unsigned int width; // Texture image width in pixels + unsigned int height; // Texture image height in pixels + unsigned int depth; // For 2D textures is 0 + unsigned int elements; // Number of array elements, usually 0 + unsigned int faces; // Cubemap faces, for no-cubemap = 1 + unsigned int mipmapLevels; // Non-mipmapped textures = 1 + unsigned int keyValueDataSize; // Used to encode any arbitrary data... // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0 + // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)... + // KTX 2.0 defines additional header elements... + } KTXHeader; + + // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize + + FILE *ktxFile = fopen(fileName, "wb"); + + if (ktxFile == NULL) TraceLog(LOG_WARNING, "[%s] KTX image file could not be created", fileName); + else + { + KTXHeader ktxHeader; + + // KTX identifier (v2.2) + //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' }; + //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; + + // Get the image header + strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature + ktxHeader.endianness = 0; + ktxHeader.glType = 0; // Obtained from image.format + ktxHeader.glTypeSize = 1; + ktxHeader.glFormat = 0; // Obtained from image.format + ktxHeader.glInternalFormat = 0; // Obtained from image.format + ktxHeader.glBaseInternalFormat = 0; + ktxHeader.width = image.width; + ktxHeader.height = image.height; + ktxHeader.depth = 0; + ktxHeader.elements = 0; + ktxHeader.faces = 1; + ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats) + ktxHeader.keyValueDataSize = 0; // No extra data after the header + + rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function + ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only + + // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC + + if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export."); + else + { + fwrite(&ktxHeader, 1, sizeof(KTXHeader), ktxFile); + + int width = image.width; + int height = image.height; + int dataOffset = 0; + + // Save all mipmaps data + for (int i = 0; i < image.mipmaps; i++) + { + unsigned int dataSize = GetPixelDataSize(width, height, image.format); + fwrite(&dataSize, 1, sizeof(unsigned int), ktxFile); + fwrite((unsigned char *)image.data + dataOffset, 1, dataSize, ktxFile); + + width /= 2; + height /= 2; + dataOffset += dataSize; + } + } + + fclose(ktxFile); // Close file pointer + } +} #endif #if defined(SUPPORT_FILEFORMAT_PVR) @@ -2674,7 +3243,7 @@ static Image LoadASTC(const char *fileName) fread(image.data, dataSize, 1, astcFile); if (bpp == 8) image.format = COMPRESSED_ASTC_4x4_RGBA; - else if (bpp == 2) image.format = COMPRESSED_ASTC_4x4_RGBA; + else if (bpp == 2) image.format = COMPRESSED_ASTC_8x8_RGBA; } else TraceLog(LOG_WARNING, "[%s] ASTC block size configuration not supported", fileName); } diff --git a/raylib/textures.go b/raylib/textures.go index 3879a75..c91de4e 100644 --- a/raylib/textures.go +++ b/raylib/textures.go @@ -1,4 +1,4 @@ -package raylib +package rl /* #include "raylib.h" @@ -127,13 +127,20 @@ func UnloadRenderTexture(target RenderTexture2D) { C.UnloadRenderTexture(*ctarget) } -// GetImageData - Get pixel data from image +// GetImageData - Get pixel data from image as a Color slice func GetImageData(img *Image) []Color { cimg := img.cptr() ret := C.GetImageData(*cimg) return (*[1 << 24]Color)(unsafe.Pointer(ret))[0 : img.Width*img.Height] } +// GetImageDataNormalized - Get pixel data from image as Vector4 slice (float normalized) +func GetImageDataNormalized(img *Image) []Vector4 { + cimg := img.cptr() + ret := C.GetImageDataNormalized(*cimg) + return (*[1 << 24]Vector4)(unsafe.Pointer(ret))[0 : img.Width*img.Height] +} + // GetPixelDataSize - Get pixel data size in bytes (image or texture) func GetPixelDataSize(width, height, format int32) int32 { cwidth := (C.int)(width) @@ -159,12 +166,12 @@ func UpdateTexture(texture Texture2D, pixels []Color) { } // ExportImage - Export image as a PNG file -func ExportImage(name string, image Image) { +func ExportImage(image Image, name string) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) cimage := image.cptr() - C.ExportImage(cname, *cimage) + C.ExportImage(*cimage, cname) } // ImageToPOT - Convert image to POT (power-of-two) @@ -250,6 +257,17 @@ func ImageResizeNN(image *Image, newWidth, newHeight int32) { C.ImageResizeNN(cimage, cnewWidth, cnewHeight) } +// ImageResizeCanvas - Resize canvas and fill with color +func ImageResizeCanvas(image *Image, newWidth, newHeight, offsetX, offsetY int32, color Color) { + cimage := image.cptr() + cnewWidth := (C.int)(newWidth) + cnewHeight := (C.int)(newHeight) + coffsetX := (C.int)(offsetX) + coffsetY := (C.int)(offsetY) + ccolor := color.cptr() + C.ImageResizeCanvas(cimage, cnewWidth, cnewHeight, coffsetX, coffsetY, *ccolor) +} + // ImageMipmaps - Generate all mipmap levels for a provided image func ImageMipmaps(image *Image) { cimage := image.cptr() @@ -334,6 +352,18 @@ func ImageFlipHorizontal(image *Image) { C.ImageFlipHorizontal(cimage) } +// ImageRotateCW - Rotate image clockwise 90deg +func ImageRotateCW(image *Image) { + cimage := image.cptr() + C.ImageRotateCW(cimage) +} + +// ImageRotateCCW - Rotate image counter-clockwise 90deg +func ImageRotateCCW(image *Image) { + cimage := image.cptr() + C.ImageRotateCCW(cimage) +} + // ImageColorTint - Modify image color: tint func ImageColorTint(image *Image, color Color) { cimage := image.cptr() @@ -367,6 +397,14 @@ func ImageColorBrightness(image *Image, brightness int32) { C.ImageColorBrightness(cimage, cbrightness) } +// ImageColorReplace - Modify image color: replace color +func ImageColorReplace(image *Image, color, replace Color) { + cimage := image.cptr() + ccolor := color.cptr() + creplace := replace.cptr() + C.ImageColorReplace(cimage, *ccolor, *creplace) +} + // GenImageColor - Generate image: plain color func GenImageColor(width, height int, color Color) *Image { cwidth := (C.int)(width) diff --git a/raylib/utils.c b/raylib/utils.c index 9d9d8b5..b295104 100644 --- a/raylib/utils.c +++ b/raylib/utils.c @@ -4,21 +4,10 @@ * * CONFIGURATION: * -* #define SUPPORT_SAVE_PNG (defined by default) -* Support saving image data as PNG fileformat -* NOTE: Requires stb_image_write library -* -* #define SUPPORT_SAVE_BMP -* Support saving image data as BMP fileformat -* NOTE: Requires stb_image_write library -* * #define SUPPORT_TRACELOG * Show TraceLog() output messages * NOTE: By default LOG_DEBUG traces not shown * -* DEPENDENCIES: -* stb_image_write - BMP/PNG writting functions -* * * LICENSE: zlib/libpng * @@ -57,10 +46,10 @@ #include // Required for: va_list, va_start(), vfprintf(), va_end() #include // Required for: strlen(), strrchr(), strcmp() -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) - #define STB_IMAGE_WRITE_IMPLEMENTATION - #include "external/stb_image_write.h" // Required for: stbi_write_bmp(), stbi_write_png() -#endif +/* This should be in , but Travis doesn't find it... */ +FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), + int (*writefn)(void *, const char *, int), + fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)); //---------------------------------------------------------------------------------- // Global Variables Definition @@ -68,6 +57,7 @@ // Log types messages supported flags (bit based) static unsigned char logTypeFlags = LOG_INFO | LOG_WARNING | LOG_ERROR; +static TraceLogCallback logCallback = NULL; #if defined(PLATFORM_ANDROID) AAssetManager *assetManager; @@ -93,11 +83,26 @@ void SetTraceLog(unsigned char types) logTypeFlags = types; } +// Set a trace log callback to enable custom logging bypassing raylib's one +void SetTraceLogCallback(TraceLogCallback callback) +{ + logCallback = callback; +} + // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) void TraceLog(int msgType, const char *text, ...) { #if defined(SUPPORT_TRACELOG) static char buffer[128]; + va_list args; + va_start(args, text); + + if (logCallback) + { + logCallback(msgType, text, args); + va_end(args); + return; + } switch(msgType) { @@ -111,9 +116,6 @@ void TraceLog(int msgType, const char *text, ...) strcat(buffer, text); strcat(buffer, "\n"); - va_list args; - va_start(args, text); - #if defined(PLATFORM_ANDROID) switch(msgType) { @@ -137,32 +139,10 @@ void TraceLog(int msgType, const char *text, ...) va_end(args); if (msgType == LOG_ERROR) exit(1); // If LOG_ERROR message, exit program - + #endif // SUPPORT_TRACELOG } -#if defined(SUPPORT_SAVE_BMP) -// Creates a BMP image file from an array of pixel data -void SaveBMP(const char *fileName, unsigned char *imgData, int width, int height, int compSize) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) - stbi_write_bmp(fileName, width, height, compSize, imgData); - TraceLog(LOG_INFO, "BMP Image saved: %s", fileName); -#endif -} -#endif - -#if defined(SUPPORT_SAVE_PNG) -// Creates a PNG image file from an array of pixel data -void SavePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) - stbi_write_png(fileName, width, height, compSize, imgData, width*compSize); - TraceLog(LOG_INFO, "PNG Image saved: %s", fileName); -#endif -} -#endif - // Keep track of memory allocated // NOTE: mallocType defines the type of data allocated /* diff --git a/raylib/utils.go b/raylib/utils.go index c1c35f6..4a32702 100644 --- a/raylib/utils.go +++ b/raylib/utils.go @@ -1,6 +1,6 @@ // +build !android,!windows -package raylib +package rl /* #include "raylib.h" diff --git a/raylib/utils.h b/raylib/utils.h index 1d1c541..08b3396 100644 --- a/raylib/utils.h +++ b/raylib/utils.h @@ -32,10 +32,6 @@ #include // Required for: AAssetManager #endif -#ifndef SUPPORT_SAVE_PNG -#define SUPPORT_SAVE_PNG 1 -#endif - //---------------------------------------------------------------------------------- // Some basic Defines //---------------------------------------------------------------------------------- @@ -58,15 +54,6 @@ extern "C" { // Prevents name mangling of functions //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- -//#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) -#if defined(SUPPORT_SAVE_BMP) -void SaveBMP(const char *fileName, unsigned char *imgData, int width, int height, int compSize); -#endif -#if defined(SUPPORT_SAVE_PNG) -void SavePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize); -#endif -//#endif - #if defined(PLATFORM_ANDROID) void InitAssetManager(AAssetManager *manager); // Initialize asset manager from android app FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() diff --git a/raylib/utils_android.go b/raylib/utils_android.go index 08ab482..01d75dd 100644 --- a/raylib/utils_android.go +++ b/raylib/utils_android.go @@ -1,6 +1,6 @@ // +build android -package raylib +package rl /* #include "raylib.h" diff --git a/raylib/utils_windows.go b/raylib/utils_windows.go index a617ad7..c9cf4cc 100644 --- a/raylib/utils_windows.go +++ b/raylib/utils_windows.go @@ -1,6 +1,6 @@ // +build windows -package raylib +package rl /* #include "raylib.h" diff --git a/raymath/raymath.go b/raymath/raymath.go index 757702c..68712cb 100644 --- a/raymath/raymath.go +++ b/raymath/raymath.go @@ -8,43 +8,43 @@ import ( ) // Vector2Zero - Vector with components value 0.0 -func Vector2Zero() raylib.Vector2 { - return raylib.NewVector2(0.0, 0.0) +func Vector2Zero() rl.Vector2 { + return rl.NewVector2(0.0, 0.0) } // Vector2One - Vector with components value 1.0 -func Vector2One() raylib.Vector2 { - return raylib.NewVector2(1.0, 1.0) +func Vector2One() rl.Vector2 { + return rl.NewVector2(1.0, 1.0) } // Vector2Add - Add two vectors (v1 + v2) -func Vector2Add(v1, v2 raylib.Vector2) raylib.Vector2 { - return raylib.NewVector2(v1.X+v2.X, v1.Y+v2.Y) +func Vector2Add(v1, v2 rl.Vector2) rl.Vector2 { + return rl.NewVector2(v1.X+v2.X, v1.Y+v2.Y) } // Vector2Subtract - Subtract two vectors (v1 - v2) -func Vector2Subtract(v1, v2 raylib.Vector2) raylib.Vector2 { - return raylib.NewVector2(v1.X-v2.X, v1.Y-v2.Y) +func Vector2Subtract(v1, v2 rl.Vector2) rl.Vector2 { + return rl.NewVector2(v1.X-v2.X, v1.Y-v2.Y) } // Vector2Length - Calculate vector length -func Vector2Length(v raylib.Vector2) float32 { +func Vector2Length(v rl.Vector2) float32 { return float32(math.Sqrt(float64((v.X * v.X) + (v.Y * v.Y)))) } // Vector2DotProduct - Calculate two vectors dot product -func Vector2DotProduct(v1, v2 raylib.Vector2) float32 { +func Vector2DotProduct(v1, v2 rl.Vector2) float32 { return (v1.X*v2.X + v1.Y*v2.Y) } // Vector2Distance - Calculate distance between two vectors -func Vector2Distance(v1, v2 raylib.Vector2) float32 { +func Vector2Distance(v1, v2 rl.Vector2) float32 { return float32(math.Sqrt(float64((v1.X-v2.X)*(v1.X-v2.X) + (v1.Y-v2.Y)*(v1.Y-v2.Y)))) } // Vector2Angle - Calculate angle between two vectors in X-axis -func Vector2Angle(v1, v2 raylib.Vector2) float32 { - angle := float32(math.Atan2(float64(v2.Y-v1.Y), float64(v2.X-v1.X)) * (180.0 / float64(raylib.Pi))) +func Vector2Angle(v1, v2 rl.Vector2) float32 { + angle := float32(math.Atan2(float64(v2.Y-v1.Y), float64(v2.X-v1.X)) * (180.0 / float64(rl.Pi))) if angle < 0 { angle += 360.0 @@ -54,53 +54,53 @@ func Vector2Angle(v1, v2 raylib.Vector2) float32 { } // Vector2Scale - Scale vector (multiply by value) -func Vector2Scale(v *raylib.Vector2, scale float32) { +func Vector2Scale(v *rl.Vector2, scale float32) { v.X *= scale v.Y *= scale } // Vector2Negate - Negate vector -func Vector2Negate(v *raylib.Vector2) { +func Vector2Negate(v *rl.Vector2) { v.X = -v.X v.Y = -v.Y } // Vector2Divide - Divide vector by a float value -func Vector2Divide(v *raylib.Vector2, div float32) { +func Vector2Divide(v *rl.Vector2, div float32) { v.X = v.X / div v.Y = v.Y / div } // Vector2Normalize - Normalize provided vector -func Vector2Normalize(v *raylib.Vector2) { +func Vector2Normalize(v *rl.Vector2) { Vector2Divide(v, Vector2Length(*v)) } // Vector2CrossProduct - Calculate two vectors cross product -func Vector2CrossProduct(v1, v2 raylib.Vector2) float32 { +func Vector2CrossProduct(v1, v2 rl.Vector2) float32 { return v1.X*v2.Y - v1.Y*v2.X } // Vector2Cross - Calculate the cross product of a vector and a value -func Vector2Cross(value float32, vector raylib.Vector2) raylib.Vector2 { - return raylib.NewVector2(-value*vector.Y, value*vector.X) +func Vector2Cross(value float32, vector rl.Vector2) rl.Vector2 { + return rl.NewVector2(-value*vector.Y, value*vector.X) } // Vector2LenSqr - Returns the len square root of a vector -func Vector2LenSqr(vector raylib.Vector2) float32 { +func Vector2LenSqr(vector rl.Vector2) float32 { return vector.X*vector.X + vector.Y*vector.Y } // Mat2Radians - Creates a matrix 2x2 from a given radians value -func Mat2Radians(radians float32) raylib.Mat2 { +func Mat2Radians(radians float32) rl.Mat2 { c := float32(math.Cos(float64(radians))) s := float32(math.Sin(float64(radians))) - return raylib.NewMat2(c, -s, s, c) + return rl.NewMat2(c, -s, s, c) } // Mat2Set - Set values from radians to a created matrix 2x2 -func Mat2Set(matrix *raylib.Mat2, radians float32) { +func Mat2Set(matrix *rl.Mat2, radians float32) { cos := float32(math.Cos(float64(radians))) sin := float32(math.Sin(float64(radians))) @@ -111,33 +111,33 @@ func Mat2Set(matrix *raylib.Mat2, radians float32) { } // Mat2Transpose - Returns the transpose of a given matrix 2x2 -func Mat2Transpose(matrix raylib.Mat2) raylib.Mat2 { - return raylib.NewMat2(matrix.M00, matrix.M10, matrix.M01, matrix.M11) +func Mat2Transpose(matrix rl.Mat2) rl.Mat2 { + return rl.NewMat2(matrix.M00, matrix.M10, matrix.M01, matrix.M11) } // Mat2MultiplyVector2 - Multiplies a vector by a matrix 2x2 -func Mat2MultiplyVector2(matrix raylib.Mat2, vector raylib.Vector2) raylib.Vector2 { - return raylib.NewVector2(matrix.M00*vector.X+matrix.M01*vector.Y, matrix.M10*vector.X+matrix.M11*vector.Y) +func Mat2MultiplyVector2(matrix rl.Mat2, vector rl.Vector2) rl.Vector2 { + return rl.NewVector2(matrix.M00*vector.X+matrix.M01*vector.Y, matrix.M10*vector.X+matrix.M11*vector.Y) } // Vector3Zero - Vector with components value 0.0 -func Vector3Zero() raylib.Vector3 { - return raylib.NewVector3(0.0, 0.0, 0.0) +func Vector3Zero() rl.Vector3 { + return rl.NewVector3(0.0, 0.0, 0.0) } // Vector3One - Vector with components value 1.0 -func Vector3One() raylib.Vector3 { - return raylib.NewVector3(1.0, 1.0, 1.0) +func Vector3One() rl.Vector3 { + return rl.NewVector3(1.0, 1.0, 1.0) } // Vector3Add - Add two vectors -func Vector3Add(v1, v2 raylib.Vector3) raylib.Vector3 { - return raylib.NewVector3(v1.X+v2.X, v1.Y+v2.Y, v1.Z+v2.Z) +func Vector3Add(v1, v2 rl.Vector3) rl.Vector3 { + return rl.NewVector3(v1.X+v2.X, v1.Y+v2.Y, v1.Z+v2.Z) } // Vector3Multiply - Multiply vector by scalar -func Vector3Multiply(v raylib.Vector3, scalar float32) raylib.Vector3 { - result := raylib.Vector3{} +func Vector3Multiply(v rl.Vector3, scalar float32) rl.Vector3 { + result := rl.Vector3{} result.X = v.X * scalar result.Y = v.Y * scalar @@ -147,8 +147,8 @@ func Vector3Multiply(v raylib.Vector3, scalar float32) raylib.Vector3 { } // Vector3MultiplyV - Multiply vector by vector -func Vector3MultiplyV(v1, v2 raylib.Vector3) raylib.Vector3 { - result := raylib.Vector3{} +func Vector3MultiplyV(v1, v2 rl.Vector3) rl.Vector3 { + result := rl.Vector3{} result.X = v1.X * v2.X result.Y = v1.Y * v2.Y @@ -158,13 +158,13 @@ func Vector3MultiplyV(v1, v2 raylib.Vector3) raylib.Vector3 { } // Vector3Subtract - Subtract two vectors -func Vector3Subtract(v1, v2 raylib.Vector3) raylib.Vector3 { - return raylib.NewVector3(v1.X-v2.X, v1.Y-v2.Y, v1.Z-v2.Z) +func Vector3Subtract(v1, v2 rl.Vector3) rl.Vector3 { + return rl.NewVector3(v1.X-v2.X, v1.Y-v2.Y, v1.Z-v2.Z) } // Vector3CrossProduct - Calculate two vectors cross product -func Vector3CrossProduct(v1, v2 raylib.Vector3) raylib.Vector3 { - result := raylib.Vector3{} +func Vector3CrossProduct(v1, v2 rl.Vector3) rl.Vector3 { + result := rl.Vector3{} result.X = v1.Y*v2.Z - v1.Z*v2.Y result.Y = v1.Z*v2.X - v1.X*v2.Z @@ -174,19 +174,19 @@ func Vector3CrossProduct(v1, v2 raylib.Vector3) raylib.Vector3 { } // Vector3Perpendicular - Calculate one vector perpendicular vector -func Vector3Perpendicular(v raylib.Vector3) raylib.Vector3 { - result := raylib.Vector3{} +func Vector3Perpendicular(v rl.Vector3) rl.Vector3 { + result := rl.Vector3{} min := math.Abs(float64(v.X)) - cardinalAxis := raylib.NewVector3(1.0, 0.0, 0.0) + cardinalAxis := rl.NewVector3(1.0, 0.0, 0.0) if math.Abs(float64(v.Y)) < min { min = math.Abs(float64(v.Y)) - cardinalAxis = raylib.NewVector3(0.0, 1.0, 0.0) + cardinalAxis = rl.NewVector3(0.0, 1.0, 0.0) } if math.Abs(float64(v.Z)) < min { - cardinalAxis = raylib.NewVector3(0.0, 0.0, 1.0) + cardinalAxis = rl.NewVector3(0.0, 0.0, 1.0) } result = Vector3CrossProduct(v, cardinalAxis) @@ -195,17 +195,17 @@ func Vector3Perpendicular(v raylib.Vector3) raylib.Vector3 { } // Vector3Length - Calculate vector length -func Vector3Length(v raylib.Vector3) float32 { +func Vector3Length(v rl.Vector3) float32 { return float32(math.Sqrt(float64(v.X*v.X + v.Y*v.Y + v.Z*v.Z))) } // Vector3DotProduct - Calculate two vectors dot product -func Vector3DotProduct(v1, v2 raylib.Vector3) float32 { +func Vector3DotProduct(v1, v2 rl.Vector3) float32 { return v1.X*v2.X + v1.Y*v2.Y + v1.Z*v2.Z } // Vector3Distance - Calculate distance between two vectors -func Vector3Distance(v1, v2 raylib.Vector3) float32 { +func Vector3Distance(v1, v2 rl.Vector3) float32 { dx := v2.X - v1.X dy := v2.Y - v1.Y dz := v2.Z - v1.Z @@ -214,21 +214,21 @@ func Vector3Distance(v1, v2 raylib.Vector3) float32 { } // Vector3Scale - Scale provided vector -func Vector3Scale(v *raylib.Vector3, scale float32) { +func Vector3Scale(v *rl.Vector3, scale float32) { v.X *= scale v.Y *= scale v.Z *= scale } // Vector3Negate - Negate provided vector (invert direction) -func Vector3Negate(v *raylib.Vector3) { +func Vector3Negate(v *rl.Vector3) { v.X = -v.X v.Y = -v.Y v.Z = -v.Z } // Vector3Normalize - Normalize provided vector -func Vector3Normalize(v *raylib.Vector3) { +func Vector3Normalize(v *rl.Vector3) { var length, ilength float32 length = Vector3Length(*v) @@ -245,7 +245,7 @@ func Vector3Normalize(v *raylib.Vector3) { } // Vector3Transform - Transforms a Vector3 by a given Matrix -func Vector3Transform(v *raylib.Vector3, mat raylib.Matrix) { +func Vector3Transform(v *rl.Vector3, mat rl.Matrix) { x := v.X y := v.Y z := v.Z @@ -256,8 +256,8 @@ func Vector3Transform(v *raylib.Vector3, mat raylib.Matrix) { } // Vector3Lerp - Calculate linear interpolation between two vectors -func Vector3Lerp(v1, v2 raylib.Vector3, amount float32) raylib.Vector3 { - result := raylib.Vector3{} +func Vector3Lerp(v1, v2 rl.Vector3, amount float32) rl.Vector3 { + result := rl.Vector3{} result.X = v1.X + amount*(v2.X-v1.X) result.Y = v1.Y + amount*(v2.Y-v1.Y) @@ -267,12 +267,12 @@ func Vector3Lerp(v1, v2 raylib.Vector3, amount float32) raylib.Vector3 { } // Vector3Reflect - Calculate reflected vector to normal -func Vector3Reflect(vector, normal raylib.Vector3) raylib.Vector3 { +func Vector3Reflect(vector, normal rl.Vector3) rl.Vector3 { // I is the original vector // N is the normal of the incident plane // R = I - (2*N*( DotProduct[ I,N] )) - result := raylib.Vector3{} + result := rl.Vector3{} dotProduct := Vector3DotProduct(vector, normal) @@ -284,8 +284,8 @@ func Vector3Reflect(vector, normal raylib.Vector3) raylib.Vector3 { } // Vector3Min - Return min value for each pair of components -func Vector3Min(vec1, vec2 raylib.Vector3) raylib.Vector3 { - result := raylib.Vector3{} +func Vector3Min(vec1, vec2 rl.Vector3) rl.Vector3 { + result := rl.Vector3{} result.X = float32(math.Min(float64(vec1.X), float64(vec2.X))) result.Y = float32(math.Min(float64(vec1.Y), float64(vec2.Y))) @@ -295,8 +295,8 @@ func Vector3Min(vec1, vec2 raylib.Vector3) raylib.Vector3 { } // Vector3Max - Return max value for each pair of components -func Vector3Max(vec1, vec2 raylib.Vector3) raylib.Vector3 { - result := raylib.Vector3{} +func Vector3Max(vec1, vec2 rl.Vector3) rl.Vector3 { + result := rl.Vector3{} result.X = float32(math.Max(float64(vec1.X), float64(vec2.X))) result.Y = float32(math.Max(float64(vec1.Y), float64(vec2.Y))) @@ -306,7 +306,7 @@ func Vector3Max(vec1, vec2 raylib.Vector3) raylib.Vector3 { } // Vector3Barycenter - Barycenter coords for p in triangle abc -func Vector3Barycenter(p, a, b, c raylib.Vector3) raylib.Vector3 { +func Vector3Barycenter(p, a, b, c rl.Vector3) rl.Vector3 { v0 := Vector3Subtract(b, a) v1 := Vector3Subtract(c, a) v2 := Vector3Subtract(p, a) @@ -318,7 +318,7 @@ func Vector3Barycenter(p, a, b, c raylib.Vector3) raylib.Vector3 { denom := d00*d11 - d01*d01 - result := raylib.Vector3{} + result := rl.Vector3{} result.Y = (d11*d20 - d01*d21) / denom result.Z = (d00*d21 - d01*d20) / denom @@ -328,7 +328,7 @@ func Vector3Barycenter(p, a, b, c raylib.Vector3) raylib.Vector3 { } // MatrixDeterminant - Compute matrix determinant -func MatrixDeterminant(mat raylib.Matrix) float32 { +func MatrixDeterminant(mat rl.Matrix) float32 { var result float32 a00 := mat.M0 @@ -359,13 +359,13 @@ func MatrixDeterminant(mat raylib.Matrix) float32 { } // MatrixTrace - Returns the trace of the matrix (sum of the values along the diagonal) -func MatrixTrace(mat raylib.Matrix) float32 { +func MatrixTrace(mat rl.Matrix) float32 { return mat.M0 + mat.M5 + mat.M10 + mat.M15 } // MatrixTranspose - Transposes provided matrix -func MatrixTranspose(mat *raylib.Matrix) { - var temp raylib.Matrix +func MatrixTranspose(mat *rl.Matrix) { + var temp rl.Matrix temp.M0 = mat.M0 temp.M1 = mat.M4 @@ -388,8 +388,8 @@ func MatrixTranspose(mat *raylib.Matrix) { } // MatrixInvert - Invert provided matrix -func MatrixInvert(mat *raylib.Matrix) { - var temp raylib.Matrix +func MatrixInvert(mat *rl.Matrix) { + var temp rl.Matrix a00 := mat.M0 a01 := mat.M1 @@ -445,7 +445,7 @@ func MatrixInvert(mat *raylib.Matrix) { } // MatrixNormalize - Normalize provided matrix -func MatrixNormalize(mat *raylib.Matrix) { +func MatrixNormalize(mat *rl.Matrix) { det := MatrixDeterminant(*mat) mat.M0 /= det @@ -467,8 +467,8 @@ func MatrixNormalize(mat *raylib.Matrix) { } // MatrixIdentity - Returns identity matrix -func MatrixIdentity() raylib.Matrix { - return raylib.NewMatrix( +func MatrixIdentity() rl.Matrix { + return rl.NewMatrix( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, @@ -476,7 +476,7 @@ func MatrixIdentity() raylib.Matrix { } // MatrixAdd - Add two matrices -func MatrixAdd(left, right raylib.Matrix) raylib.Matrix { +func MatrixAdd(left, right rl.Matrix) rl.Matrix { result := MatrixIdentity() result.M0 = left.M0 + right.M0 @@ -500,7 +500,7 @@ func MatrixAdd(left, right raylib.Matrix) raylib.Matrix { } // MatrixSubtract - Subtract two matrices (left - right) -func MatrixSubtract(left, right raylib.Matrix) raylib.Matrix { +func MatrixSubtract(left, right rl.Matrix) rl.Matrix { result := MatrixIdentity() result.M0 = left.M0 - right.M0 @@ -524,8 +524,8 @@ func MatrixSubtract(left, right raylib.Matrix) raylib.Matrix { } // MatrixTranslate - Returns translation matrix -func MatrixTranslate(x, y, z float32) raylib.Matrix { - return raylib.NewMatrix( +func MatrixTranslate(x, y, z float32) rl.Matrix { + return rl.NewMatrix( 1.0, 0.0, 0.0, x, 0.0, 1.0, 0.0, y, 0.0, 0.0, 1.0, z, @@ -533,8 +533,8 @@ func MatrixTranslate(x, y, z float32) raylib.Matrix { } // MatrixRotate - Returns rotation matrix for an angle around an specified axis (angle in radians) -func MatrixRotate(axis raylib.Vector3, angle float32) raylib.Matrix { - var result raylib.Matrix +func MatrixRotate(axis rl.Vector3, angle float32) rl.Matrix { + var result rl.Matrix mat := MatrixIdentity() @@ -602,7 +602,7 @@ func MatrixRotate(axis raylib.Vector3, angle float32) raylib.Matrix { } // MatrixRotateX - Returns x-rotation matrix (angle in radians) -func MatrixRotateX(angle float32) raylib.Matrix { +func MatrixRotateX(angle float32) rl.Matrix { result := MatrixIdentity() cosres := float32(math.Cos(float64(angle))) @@ -617,7 +617,7 @@ func MatrixRotateX(angle float32) raylib.Matrix { } // MatrixRotateY - Returns y-rotation matrix (angle in radians) -func MatrixRotateY(angle float32) raylib.Matrix { +func MatrixRotateY(angle float32) rl.Matrix { result := MatrixIdentity() cosres := float32(math.Cos(float64(angle))) @@ -632,7 +632,7 @@ func MatrixRotateY(angle float32) raylib.Matrix { } // MatrixRotateZ - Returns z-rotation matrix (angle in radians) -func MatrixRotateZ(angle float32) raylib.Matrix { +func MatrixRotateZ(angle float32) rl.Matrix { result := MatrixIdentity() cosres := float32(math.Cos(float64(angle))) @@ -647,8 +647,8 @@ func MatrixRotateZ(angle float32) raylib.Matrix { } // MatrixScale - Returns scaling matrix -func MatrixScale(x, y, z float32) raylib.Matrix { - result := raylib.NewMatrix( +func MatrixScale(x, y, z float32) rl.Matrix { + result := rl.NewMatrix( x, 0.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, 0.0, z, 0.0, @@ -658,8 +658,8 @@ func MatrixScale(x, y, z float32) raylib.Matrix { } // MatrixMultiply - Returns two matrix multiplication -func MatrixMultiply(left, right raylib.Matrix) raylib.Matrix { - var result raylib.Matrix +func MatrixMultiply(left, right rl.Matrix) rl.Matrix { + var result rl.Matrix result.M0 = right.M0*left.M0 + right.M1*left.M4 + right.M2*left.M8 + right.M3*left.M12 result.M1 = right.M0*left.M1 + right.M1*left.M5 + right.M2*left.M9 + right.M3*left.M13 @@ -682,8 +682,8 @@ func MatrixMultiply(left, right raylib.Matrix) raylib.Matrix { } // MatrixFrustum - Returns perspective projection matrix -func MatrixFrustum(left, right, bottom, top, near, far float32) raylib.Matrix { - var result raylib.Matrix +func MatrixFrustum(left, right, bottom, top, near, far float32) rl.Matrix { + var result rl.Matrix rl := right - left tb := top - bottom @@ -713,16 +713,16 @@ func MatrixFrustum(left, right, bottom, top, near, far float32) raylib.Matrix { } // MatrixPerspective - Returns perspective projection matrix -func MatrixPerspective(fovy, aspect, near, far float32) raylib.Matrix { - top := near * float32(math.Tan(float64(fovy*raylib.Pi)/360.0)) +func MatrixPerspective(fovy, aspect, near, far float32) rl.Matrix { + top := near * float32(math.Tan(float64(fovy*rl.Pi)/360.0)) right := top * aspect return MatrixFrustum(-right, right, -top, top, near, far) } // MatrixOrtho - Returns orthographic projection matrix -func MatrixOrtho(left, right, bottom, top, near, far float32) raylib.Matrix { - var result raylib.Matrix +func MatrixOrtho(left, right, bottom, top, near, far float32) rl.Matrix { + var result rl.Matrix rl := (right - left) tb := (top - bottom) @@ -749,8 +749,8 @@ func MatrixOrtho(left, right, bottom, top, near, far float32) raylib.Matrix { } // MatrixLookAt - Returns camera look-at matrix (view matrix) -func MatrixLookAt(eye, target, up raylib.Vector3) raylib.Matrix { - var result raylib.Matrix +func MatrixLookAt(eye, target, up rl.Vector3) rl.Matrix { + var result rl.Matrix z := Vector3Subtract(eye, target) Vector3Normalize(&z) @@ -780,12 +780,12 @@ func MatrixLookAt(eye, target, up raylib.Vector3) raylib.Matrix { } // QuaternionLength - Compute the length of a quaternion -func QuaternionLength(quat raylib.Quaternion) float32 { +func QuaternionLength(quat rl.Quaternion) float32 { return float32(math.Sqrt(float64(quat.X*quat.X + quat.Y*quat.Y + quat.Z*quat.Z + quat.W*quat.W))) } // QuaternionNormalize - Normalize provided quaternion -func QuaternionNormalize(q *raylib.Quaternion) { +func QuaternionNormalize(q *rl.Quaternion) { var length, ilength float32 length = QuaternionLength(*q) @@ -803,7 +803,7 @@ func QuaternionNormalize(q *raylib.Quaternion) { } // QuaternionInvert - Invert provided quaternion -func QuaternionInvert(quat *raylib.Quaternion) { +func QuaternionInvert(quat *rl.Quaternion) { length := QuaternionLength(*quat) lengthSq := length * length @@ -818,8 +818,8 @@ func QuaternionInvert(quat *raylib.Quaternion) { } // QuaternionMultiply - Calculate two quaternion multiplication -func QuaternionMultiply(q1, q2 raylib.Quaternion) raylib.Quaternion { - var result raylib.Quaternion +func QuaternionMultiply(q1, q2 rl.Quaternion) rl.Quaternion { + var result rl.Quaternion qax := q1.X qay := q1.Y @@ -839,8 +839,8 @@ func QuaternionMultiply(q1, q2 raylib.Quaternion) raylib.Quaternion { } // QuaternionSlerp - Calculates spherical linear interpolation between two quaternions -func QuaternionSlerp(q1, q2 raylib.Quaternion, amount float32) raylib.Quaternion { - var result raylib.Quaternion +func QuaternionSlerp(q1, q2 rl.Quaternion, amount float32) rl.Quaternion { + var result rl.Quaternion cosHalfTheta := q1.X*q2.X + q1.Y*q2.Y + q1.Z*q2.Z + q1.W*q2.W @@ -870,8 +870,8 @@ func QuaternionSlerp(q1, q2 raylib.Quaternion, amount float32) raylib.Quaternion } // QuaternionFromMatrix - Returns a quaternion for a given rotation matrix -func QuaternionFromMatrix(matrix raylib.Matrix) raylib.Quaternion { - var result raylib.Quaternion +func QuaternionFromMatrix(matrix rl.Matrix) rl.Quaternion { + var result rl.Quaternion trace := MatrixTrace(matrix) @@ -919,8 +919,8 @@ func QuaternionFromMatrix(matrix raylib.Matrix) raylib.Quaternion { } // QuaternionToMatrix - Returns a matrix for a given quaternion -func QuaternionToMatrix(q raylib.Quaternion) raylib.Matrix { - var result raylib.Matrix +func QuaternionToMatrix(q rl.Quaternion) rl.Matrix { + var result rl.Matrix x := q.X y := q.Y @@ -964,8 +964,8 @@ func QuaternionToMatrix(q raylib.Quaternion) raylib.Matrix { } // QuaternionFromAxisAngle - Returns rotation quaternion for an angle and axis -func QuaternionFromAxisAngle(axis raylib.Vector3, angle float32) raylib.Quaternion { - result := raylib.NewQuaternion(0.0, 0.0, 0.0, 1.0) +func QuaternionFromAxisAngle(axis rl.Vector3, angle float32) rl.Quaternion { + result := rl.NewQuaternion(0.0, 0.0, 0.0, 1.0) if Vector3Length(axis) != 0.0 { angle *= 0.5 @@ -987,12 +987,12 @@ func QuaternionFromAxisAngle(axis raylib.Vector3, angle float32) raylib.Quaterni } // QuaternionToAxisAngle - Returns the rotation angle and axis for a given quaternion -func QuaternionToAxisAngle(q raylib.Quaternion, outAxis *raylib.Vector3, outAngle *float32) { +func QuaternionToAxisAngle(q rl.Quaternion, outAxis *rl.Vector3, outAngle *float32) { if math.Abs(float64(q.W)) > 1.0 { QuaternionNormalize(&q) } - resAxis := raylib.NewVector3(0.0, 0.0, 0.0) + resAxis := rl.NewVector3(0.0, 0.0, 0.0) resAngle := 2.0 * float32(math.Acos(float64(q.W))) den := float32(math.Sqrt(float64(1.0 - q.W*q.W))) @@ -1012,7 +1012,7 @@ func QuaternionToAxisAngle(q raylib.Quaternion, outAxis *raylib.Vector3, outAngl } // QuaternionTransform - Transform a quaternion given a transformation matrix -func QuaternionTransform(q *raylib.Quaternion, mat raylib.Matrix) { +func QuaternionTransform(q *rl.Quaternion, mat rl.Matrix) { x := q.X y := q.Y z := q.Z diff --git a/rres/rres.go b/rres/rres.go index 3b5f258..6337dae 100644 --- a/rres/rres.go +++ b/rres/rres.go @@ -200,13 +200,13 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data Data) { // Read rres file header err := binary.Read(reader, binary.LittleEndian, &fileHeader) if err != nil { - raylib.TraceLog(raylib.LogWarning, err.Error()) + rl.TraceLog(rl.LogWarning, err.Error()) return } // Verify "rRES" identifier if string(fileHeader.ID[:]) != "rRES" { - raylib.TraceLog(raylib.LogWarning, "not a valid raylib resource file") + rl.TraceLog(rl.LogWarning, "not a valid raylib resource file") return } @@ -216,7 +216,7 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data Data) { // Read resource info and parameters err = binary.Read(reader, binary.LittleEndian, &infoHeader) if err != nil { - raylib.TraceLog(raylib.LogWarning, err.Error()) + rl.TraceLog(rl.LogWarning, err.Error()) return } @@ -236,17 +236,17 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data Data) { // Decompress data data.Data, err = Decompress(b, int(infoHeader.CompType)) if err != nil { - raylib.TraceLog(raylib.LogWarning, "[ID %d] %v", infoHeader.ID, err) + rl.TraceLog(rl.LogWarning, "[ID %d] %v", infoHeader.ID, err) } // Decrypt data data.Data, err = Decrypt(key, data.Data, int(infoHeader.CryptoType)) if err != nil { - raylib.TraceLog(raylib.LogWarning, "[ID %d] %v", infoHeader.ID, err) + rl.TraceLog(rl.LogWarning, "[ID %d] %v", infoHeader.ID, err) } if data.Data != nil && len(data.Data) == int(infoHeader.UncompSize) { - raylib.TraceLog(raylib.LogInfo, "[ID %d] Resource data loaded successfully", infoHeader.ID) + rl.TraceLog(rl.LogInfo, "[ID %d] Resource data loaded successfully", infoHeader.ID) } } else { // Skip required data to read next resource infoHeader @@ -255,7 +255,7 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data Data) { } if data.Data == nil { - raylib.TraceLog(raylib.LogWarning, "[ID %d] Requested resource could not be found", rresID) + rl.TraceLog(rl.LogWarning, "[ID %d] Requested resource could not be found", rresID) } return