Nim言語でやったのを、Go言語でもやってみようと思います。
https://qiita.com/mk2/items/bc41f9dfee6669083dbb
「ド」の音を再生する
cgo経由でmmsystem.hを利用します。Goのコードですが、ほとんど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("Enterを押したら終了します...")
reader.ReadString('\n')
}
MIDIファイルを作成する(おーぷんMIDIぷろじぇくと利用)
cgo経由でおーぷんMIDIぷろじぇくとが提供している、MIDIDataライブラリを利用します。DLLやヘッダファイルなどはNim言語のときのものを流用します。
これもほぼ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)
}
MIDIファイルを作成する(gomidi利用)
cgoを使わずに、gomidiを利用してMIDIファイルを作成します。
最初、生成するMIDIのtickをおーぷんMIDIぷろじぇくとでやった例と同じように120でやっていたのですが、短すぎておかしいなーと思っていたところ、gomidiはデフォルト960TPQN(1tickがどのくらいの秒数になるかを決める値?)でMIDIファイルを生成するらしいということがわかりました。
おーぷんMIDIぷろじぇくとの例と同じように120TPQNにできないかと思ったのですが、イマイチ設定がわからなかったので、いったん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
}
}
ソースコード