Sound programming with Go (super introductory level)

What I did in Nim language, I will try it in Go language. https://qiita.com/mk2/items/bc41f9dfee6669083dbb

Play the "do" sound

Use mmsystem.h via cgo. It's Go code, but it's almost like C.

package main

// #cgo LDFLAGS: -lwinmm
// #include <stdlib.h>
// #include <windows.h>
// #include <mmsystem.h>
import "C"
import (
	"bufio"
	"fmt"
	"math"
	"os"
	"unsafe"
)

const (
	SRATE    = 44410
	PI       = 3.14159286
	B_TIME   = 1.0
	F0       = 440.0
	AMP      = 40.0
	DATA_LEN = int(SRATE * B_TIME)
)

func main() {
	var (
		hWave C.HWAVEOUT
		whdr  C.WAVEHDR
		wfe   C.WAVEFORMATEX
	)

	bWave := (*[DATA_LEN]byte)(C.malloc(C.ulonglong(DATA_LEN)))

	for cnt := 0; cnt < DATA_LEN; cnt++ {
		bWave[cnt] = byte(AMP * math.Sin(float64(2.0*PI*F0*float32(cnt)/SRATE)))
	}

	wfe.wFormatTag = C.WAVE_FORMAT_PCM
	wfe.nChannels = 1
	wfe.nSamplesPerSec = SRATE
	wfe.nAvgBytesPerSec = SRATE
	wfe.wBitsPerSample = 8
	wfe.nBlockAlign = wfe.nChannels * wfe.wBitsPerSample / 8

	C.waveOutOpen(&hWave, C.WAVE_MAPPER, &wfe, 0, 0, C.CALLBACK_NULL)

	whdr.lpData = C.LPSTR(unsafe.Pointer(bWave))
	whdr.dwBufferLength = C.ulong(DATA_LEN)
	whdr.dwFlags = C.WHDR_BEGINLOOP | C.WHDR_ENDLOOP
	whdr.dwLoops = 1

	C.waveOutPrepareHeader(hWave, &whdr, C.uint(unsafe.Sizeof(C.WAVEHDR{})))
	C.waveOutWrite(hWave, &whdr, C.uint(unsafe.Sizeof(C.WAVEHDR{})))

	reader := bufio.NewReader(os.Stdin)
	fmt.Println("Press Enter to exit...")
	reader.ReadString('\n')
}

Create a MIDI file (using Open MIDI Project)

Using MIDI Data Library provided by Open MIDI Project via cgo I will. DLLs and header files are the same as those used in the Nim language.

This is also almost like C.

package main

// #cgo windows LDFLAGS: -L. -lMIDIData
// #include "MIDIData.h"
import "C"

func main() {
	var midiData = C.MIDIData_Create(C.MIDIDATA_FORMAT0, 1, C.MIDIDATA_TPQNBASE, 120)
	var midiTrack = C.MIDIData_GetFirstTrack(midiData)
	C.MIDITrack_InsertTrackNameA(midiTrack, 0, C.CString("doremi"))
	C.MIDITrack_InsertTempo(midiTrack, 0, 60000000/120)
	C.MIDITrack_InsertProgramChange(midiTrack, 0, 0, 1)

	C.MIDITrack_InsertNote(midiTrack, 0, 0, 60, 100, 120)
	C.MIDITrack_InsertNote(midiTrack, 120, 0, 62, 100, 120)
	C.MIDITrack_InsertNote(midiTrack, 240, 0, 64, 100, 120)

	C.MIDITrack_InsertEndofTrack(midiTrack, 360)
	C.MIDIData_SaveAsSMFA(midiData, C.CString("doremi.midi"))
	C.MIDIData_Delete(midiData)
}

Create a MIDI file (using gomidi)

Create a MIDI file using gomidi without using cgo.

At first, I was doing the generated MIDI tick with 120 as in the example I did with Open MIDI Project, but when I thought that it was too short and strange, gomidi defaulted to 960 TPQN (how much 1 tick is) It turns out that it seems to generate a MIDI file with a value that determines whether it will be in seconds?).

I was wondering if I could set it to 120TPQN as in the case of Open MIDI Program, but I didn't know the setting, so I changed it so that the same file can be generated with 960TPQN.

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"gitlab.com/gomidi/midi/writer"
)

func main() {
	dir, _ := os.Getwd()
	f := filepath.Join(dir, "smf-test.mid")

	if _, err := os.Stat(f); os.IsExist(err) {
		os.Remove(f)
	}

	err := writer.WriteSMF(f, 1, func(wr *writer.SMF) error {
		writer.TrackSequenceName(wr, "doremi")
		writer.TempoBPM(wr, 120)
		writer.ProgramChange(wr, 1)

		writer.NoteOn(wr, 60, 100)
		wr.SetDelta(960)
		writer.NoteOff(wr, 60)

		writer.NoteOn(wr, 62, 100)
		wr.SetDelta(960)
		writer.NoteOff(wr, 62)

		writer.NoteOn(wr, 64, 100)
		wr.SetDelta(960)
		writer.NoteOff(wr, 64)
		writer.EndOfTrack(wr)
		return nil
	})

	if err != nil {
		fmt.Printf("could not write SMF file %v\n", f)
		return
	}

}

Source code

https://github.com/mk2/go-sound-study

Recommended Posts

Sound programming with Go (super introductory level)
Asynchronous programming with libev # 2
3. 3. AI programming with Python
Python programming with Atom
Competitive programming with python
Shader programming with pyOpenGL
Linear Programming with PuLP
Programming with Python Flask
Asynchronous programming with libev # 3