Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Playing spatialized 3D sound

examples/audio/sound_positioning.livemd

Playing spatialized 3D sound

Mix.install([
  {:zexray, github: "jn-jairo/zexray", depth: 1}
])

Code example

Example complexity rating: [★★☆☆] 2/4

defmodule Example do
  @resources_dir System.tmp_dir!() <> "/zexray/resources"
  @resources_url "https://github.com/raysan5/raylib/raw/3e336e4470f7975af67f716d4d809441883d7eef"

  use Zexray.Enum
  use Zexray.Type

  def download_resources do
    File.mkdir_p!(@resources_dir)

    resources = %{
      "#{@resources_dir}/coin.wav" => "#{@resources_url}/examples/audio/resources/coin.wav"
    }

    :inets.start()
    :ssl.start()

    Enum.each(resources, fn {file, url} ->
      if not File.exists?(file) do
        {:ok, :saved_to_file} =
          :httpc.request(:get, {~c"#{url}", []}, [], stream: ~c"#{file}")
      end
    end)
  end

  @screen_width 800
  @screen_height 450
  @title "zexray [audio] example - Playing spatialized 3D sound"

  @buffer_size 4096

  def init do
    # Initialize window
    Zexray.Window.with_window(@screen_width, @screen_height, @title, fn ->
      # Initialize audio device
      Zexray.Audio.with_audio(fn ->
        # Set our game to run at 60 frames-per-second
        Zexray.Timing.set_target_fps(60)

        Zexray.Audio.set_buffer_size(@buffer_size)

        Zexray.Cursor.disable()

        # Manage resources loading/unloading
        Zexray.Resource.with_resource(
          fn ->
            sound =
              Zexray.Resource.with_resource_async(
                fn ->
                  Zexray.Audio.load_wave("#{@resources_dir}/coin.wav", :resource)
                  # Format to 32 bits stereo to allow extra positioning effect
                  |> Zexray.Audio.wave_format(44_100, 32, 2)
                end,
                fn wave ->
                  Zexray.Audio.load_sound_stream_from_wave(wave, :resource)
                end
              )
              |> Zexray.Audio.set_looping(true)
              |> Zexray.Audio.play()

            camera =
              type_camera_3d(
                position: type_vector3(x: 0, y: 1, z: 0),
                target: type_vector3(x: 0, y: 1, z: 1),
                up: type_vector3(x: 0, y: 1, z: 0),
                fovy: 60,
                projection: enum_camera_projection(:perspective)
              )
              |> Zexray.Resource.new!()

            max_distance = 20

            {
              sound,
              camera,
              max_distance
            }
          end,
          &amp;loop/1
        )
      end)
    end)
  end

  defp loop({sound, camera, max_distance} = state) do
    # Detect window close button or ESC key
    if Zexray.Window.should_close?() do
      :ok
    else
      # Update

      Zexray.Camera.update(camera, enum_camera_mode(:free))

      th = Zexray.Timing.get_time()

      sphere_pos =
        type_vector3(
          x: 5 * :math.cos(th),
          y: 0,
          z: 5 * :math.sin(th)
        )

      Zexray.Audio.with_mode_3d(camera, max_distance, fn ->
        sound
        |> Zexray.Audio.set_position(sphere_pos)
        |> Zexray.Audio.update()
      end)

      # Draw

      Zexray.Drawing.with_drawing(fn ->
        Zexray.Drawing.clear_background(enum_color(:raywhite))

        Zexray.Drawing.with_mode_3d(camera, fn ->
          Zexray.Shape3D.draw_grid(10, 2)
          Zexray.Shape3D.draw_sphere(sphere_pos, 0.5, enum_color(:red))
        end)
      end)

      loop(state)
    end
  end
end
Example.download_resources()
Example.init()