aboutsummaryrefslogtreecommitdiff
path: root/examples/golang-mid2wav/mid2wav.go
blob: d8fc0595ef65ccb93d8ebfb73265bd75fc55c5d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package main

import (
    "flag"
    "log"
    "unsafe"
)

// #cgo LDFLAGS: -lADLMIDI -Lbin
// #cgo CFLAGS:
// #include <stdio.h>
// #include <stdlib.h>
// #include <adlmidi.h>
// typedef struct wav_header {
//     // RIFF Header
//     char riff_header[4]; // Contains "RIFF"
//     int wav_size; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8
//     char wave_header[4]; // Contains "WAVE"
//     // Format Header
//     char fmt_header[4]; // Contains "fmt " (includes trailing space)
//     int fmt_chunk_size; // Should be 16 for PCM
//     short audio_format; // Should be 1 for PCM. 3 for IEEE Float
//     short num_channels;
//     int sample_rate;
//     int byte_rate; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample
//     short sample_alignment; // num_channels * Bytes Per Sample
//     short bit_depth; // Number of bits per sample
//     // Data
//     char data_header[4]; // Contains "data"
//     int data_bytes; // Number of bytes in data. Number of samples * num_channels * sample byte size
//     // uint8_t bytes[]; // Remainder of wave file is bytes
// } wav_header;
import "C"

func main() {
    inputPtr := flag.String("i", "", "input midi file")
    outputPtr := flag.String("o", "", "output wave file")
    bank := flag.Int("b", 59, "bank")
    chips := flag.Int("c", 2, "chips")

    flag.Parse()

    if *inputPtr == "" {
	    log.Fatal("no input midi file")
    }

    if *outputPtr == "" {
	    log.Fatal("no output file")
    }

    // Convert into raw C-String
    cInputFile := C.CString(*inputPtr)

    // Initializing the instance of libADLMIDI
    cAdlMidiPlayer := C.adl_init(48000)
    // Set number of parallel chips
    C.adl_setNumChips(cAdlMidiPlayer, C.int(*chips))
    // Disable looping (it's not needed for WAV output)
    C.adl_setLoopEnabled(cAdlMidiPlayer, 0)
    // Setting one of embedded banks
    C.adl_setBank(cAdlMidiPlayer, C.int(*bank))
    // Open an external MIDI file to play it
    C.adl_openFile(cAdlMidiPlayer, cInputFile)
    // Remove the file path buffer
    C.free(unsafe.Pointer(cInputFile))

    // Create the audio buffer that will be used to store taken audio chunks and write them into WAV file
    cBuffer := C.malloc(2048 * 2)

    cOutputFileName := C.CString(*outputPtr)
    cFileMode := C.CString("wb")

    cFILE := C.fopen(cOutputFileName, cFileMode)
    C.free(unsafe.Pointer(cOutputFileName))
    C.free(unsafe.Pointer(cFileMode))

    var cWavHeader C.wav_header
    cWavHeader.riff_header[0] = 'R'
    cWavHeader.riff_header[1] = 'I'
    cWavHeader.riff_header[2] = 'F'
    cWavHeader.riff_header[3] = 'F'
    cWavHeader.wav_size = 0
    cWavHeader.wave_header[0] = 'W'
    cWavHeader.wave_header[1] = 'A'
    cWavHeader.wave_header[2] = 'V'
    cWavHeader.wave_header[3] = 'E'
    cWavHeader.fmt_header[0] = 'f'
    cWavHeader.fmt_header[1] = 'm'
    cWavHeader.fmt_header[2] = 't'
    cWavHeader.fmt_header[3] = ' '
    cWavHeader.fmt_chunk_size = 16
    cWavHeader.audio_format = 1
    cWavHeader.num_channels = 2
    cWavHeader.sample_rate = 48000
    cWavHeader.byte_rate = 192000
    cWavHeader.sample_alignment = 4
    cWavHeader.bit_depth = 16
    cWavHeader.data_header[0] = 'd'
    cWavHeader.data_header[1] = 'a'
    cWavHeader.data_header[2] = 't'
    cWavHeader.data_header[3] = 'a'
    cWavHeader.data_bytes = 0

    C.fwrite(unsafe.Pointer(&cWavHeader), 44, 1, cFILE)

    finished := false
    dataBytes := 0

    for !finished {
        count := C.adl_play(cAdlMidiPlayer, 2048, (*C.int16_t)(cBuffer))
        if C.adl_atEnd(cAdlMidiPlayer) == 1 {
            finished = true
        }
        if count > 0 {
            dataBytes += int(count) * 4
            C.fwrite(cBuffer, 1, C.size_t(count * 2), cFILE)
        }
    }
    C.adl_close(cAdlMidiPlayer)

    cWavHeader.wav_size = C.int(36 + dataBytes)
    cWavHeader.data_bytes = C.int(dataBytes)

    C.fseek(cFILE, 0, C.SEEK_SET)
    C.fwrite(unsafe.Pointer(&cWavHeader), 44, 1, cFILE)

    C.fclose(cFILE)
    C.free(cBuffer)
}