Compiling rtmidi with CGO / Golang

In this post I write a step-by-step guide on how to build the gomidi library for Go, using the rtmidi driver for windows. The process was very painful in my machine, when I tried to build it using an old installation of MinGW and TDM-GCC. But if you are starting from scratch, you should have no problem.

Before we start, it is worth noting that it is possible to use the midicat driver with gomidi. Midicat is a statically pre-compiled executable file and is way easier and faster to get it working with gomidi. Even thought midicat is easier, sometimes I experienced ocasional jitter when dealing with too much midi messages. That is the main reason I switched to rtmidi and, after I switch it did not happen again.

Another issue I noted is that after using midicat, the resource handles still open. So, there is definitely a resource leak in midicat implementation. Forcing killing “midicat.exe” is a sure way to see the Blue Screen of Death 😐.

Although the title of this post says “Compiling” we actually are going to link a pre-compiled library binary with CGO.

Installation process

Install MinGw

If you have an older version of a working MinGw, I recommend installing the newest version. You can install it in a different folder and have both ones functioning, like I did.

Install rtmidi

pacman -S mingw-w64-x86_64-rtmidi

Install GCC and the toolchain

pacman -S mingw-w64-x86_64-gcc
pacman -S mingw-w64-x86_64-toolchain

Fix the PATH

Add the following directory to your path variable C:\msys64_midi\mingw64\bin, changing C:\msys64_midi to whatever installation path you used.

You must restart VsCode or any application that need to use this updated PATH variable

Configuring VsCode

VsCode currently does not support changing all the build flags needed to build this on the launch.json file, so we can debug the application directly in the IDE. Thus, the way I configured VsCode was creating a task to build the code, with the right build flags. Then, on the launch.json file, configure it to run that task and attach the debugger to the running process.

This way has some additional configuration but works well. Except for an issue when you click the “reload” button when debugging. The default behavior is that you can press this button with any file open and VsCode will recompile and re-run the initial file you started to debug. The issue is that, now, when you click reload, you must have the editor with the file you want to compile open, otherwise it will not re-build it.

.vscode/tasks.json

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "go: build (debug)",
            "type": "shell",
            "command": "go",
            "args": [
                "build",
                "-gcflags=all=-N -l",
                "-o",
                "${fileDirname}/__debug_bin.exe"
            ],
            "options": {
                "cwd": "${fileDirname}",
                "env": {
                    "CGO_ENABLED": "1",
                    "CC": "C:\\msys64_midi\\mingw64\\bin\\gcc.exe",
                    "CXX": "C:\\msys64_midi\\mingw64\\bin\\g++.exe",
                    "CGO_CFLAGS": "-IC:\\msys64_midi\\mingw64\\include",
                    "CGO_LDFLAGS": "-LC:\\msys64_midi\\mingw64\\lib"
                }
            },
            "presentation": {
                "reveal": "silent",
                "revealProblems": "onProblem",
                "close": false
            }
        },
        {
            "label": "go: build (static)", // CGO_LDFLAGS changed
            "type": "shell",
            "command": "go",
            "args": [
                "build",
                "-gcflags=all=-N -l",
                "-o",
                "${fileDirname}/__debug_bin_static.exe"
            ],
            "options": {
                "cwd": "${fileDirname}",
                "env": {
                    "CGO_ENABLED": "1",
                    "CC": "C:\\msys64_midi\\mingw64\\bin\\gcc.exe",
                    "CXX": "C:\\msys64_midi\\mingw64\\bin\\g++.exe",
                    "CGO_CFLAGS": "-IC:\\msys64_midi\\mingw64\\include",
                    "CGO_LDFLAGS": "'-LC:\\msys64_midi\\mingw64\\lib' '-static'"
                }
            },
            "presentation": {
                "reveal": "silent",
                "revealProblems": "onProblem",
                "close": false
            }
        }
    ]
}Code language: JSON / JSON with Comments (json)

This configuration has 2 tasks:

  • go: build (debug): With rtmidi dynamically linked, here use for debugging.
  • go: build (static): With rtmidi statically linked, which is better for sharing the final release.

You must change the paths to your install location.

The flags -gcflags=all=-N -l are mandatory for debugging.

.vscode/launch.json

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Package",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${fileDirname}"
        },
        {
            "name": "Attach to Process",
            "type": "go",
            "request": "launch",
            "mode": "exec",
            "program": "${fileDirname}/__debug_bin.exe",
            "preLaunchTask": "go: build (debug)",
        }
    ]
}Code language: JSON / JSON with Comments (json)

Testing

You should be able to build and run the reference code below, that list all your MIDI output devices.

package main

import (
	"fmt"
	"time"

	midi "gitlab.com/gomidi/midi/v2"
	_ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv" // autoregisters driver
)

func main() {
	defer midi.CloseDriver()
	time.Sleep(200 * time.Millisecond)

	fmt.Println("Available midi OUT ports: ")
	fmt.Println(midi.GetOutPorts().String())

	fmt.Println("Done")
}
Code language: Go (go)

Firslty, you may run the task and check its output to see if the build was successful.

If there was no error, you should be able to debug it.


Publicado

em

por

Comentários

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *