Skip to content

Instantly share code, notes, and snippets.

@cigrainger
Last active April 22, 2024 15:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cigrainger/a93008a7a9313e4394c244af2d5ae849 to your computer and use it in GitHub Desktop.
Save cigrainger/a93008a7a9313e4394c244af2d5ae849 to your computer and use it in GitHub Desktop.

Have GPT-4 write a song

Mix.install(
  [
    {:midiex, "~> 0.6.1"},
    {:instructor, "~> 0.0.5"}
  ],
  config: [
    instructor: [
      adapter: Instructor.Adapters.OpenAI,
      openai: [api_key: System.fetch_env!("LB_OPENAI_API_KEY")]
    ]
  ]
)

Section

defmodule Song do
  use Ecto.Schema
  use Instructor.Validator

  defmodule Message do
    use Ecto.Schema
    use Instructor.Validator

    @doc """
    ## Field Descriptions:
    - message: a midi message as bytes, represented as a string with the three bytes (status, data 1, and data 2), e.g. "0x90,0x3C,0x40".
    - duration: the duration for playing the note in ms
    """
    @primary_key false
    embedded_schema do
      field(:message, :string)
      field(:duration, :integer)
    end
  end

  @doc """
  ## Field Descriptions:
  - messages: an array of midi binary messages
  """
  @primary_key false
  embedded_schema do
    embeds_many(:messages, Message)
  end
end
{:ok, %{messages: messages}} =
  Instructor.chat_completion(
    model: "gpt-4-turbo",
    response_model: Song,
    max_retries: 3,
    messages: [
      %{
        role: "user",
        content: """
        Your purpose is to write songs to be played through midi. 

        Write a super funky bass line in midi messages as bytes. Make sure it slaps.
        """
      }
    ]
  )
messages
alias Midiex.Listener
alias Midiex.Message, as: M
out_port = Midiex.ports(~r/Garage/, :output) |> List.first()
# Make a connection
out_conn = Midiex.open(out_port)
for %{message: message, duration: duration} <- messages do
  message
  |> String.split(",")
  |> Enum.map(fn hex_byte ->
    hex_byte
    |> String.trim_leading("0x")
    |> String.to_integer(16)
  end)
  |> :binary.list_to_bin()
  |> then(&Midiex.send_msg(out_conn, &1))

  :timer.sleep(duration)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment