/
bytecode.wiz
157 lines (107 loc) · 3.87 KB
/
bytecode.wiz
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright (c) 2023, Marcus Rowe <undisbeliever@gmail.com>.
// Distributed under The MIT License, see the LICENSE file for more details.
import "audio-driver";
let MAX_PAN = 128;
namespace bytecode {
let MAX_NESTED_LOOPS = 3;
let N_NOTE_OCTAVES = 8;
let N_PLAY_NOTE_INSTRUCTIONS = N_NOTE_OCTAVES * 12 * 2;
let FIRST_INSTRUCTION_WITH_ARGUMENT_OPCODE = N_PLAY_NOTE_INSTRUCTIONS;
let FIRST_NO_ARGUMENT_INSTRUCTION_OPCODE = FIRST_INSTRUCTION_WITH_ARGUMENT_OPCODE + bytecode.INSTRUCTIONS_WITH_ARGUMENTS.len * 2;
// Last instruction is the `disable_channel` instruction
// `disable_channel` will also send a KEY-OFF event.
let DISABLE_CHANNEL_BYTECODE = 0xfe;
// OPCODES < N_PLAY_NOTE_INSTRUCTIONS are a two-byte play note instruction.
//
// nnnnnnnO llllllll
// nnnnnnn = note to play (number of semitones above c0)
// O = key-off after note ends
// llllllll = note length
//
// NOTE: This instruction will wait an extra tick if the `key-off` bit is set.
// The remaining instructions have an opcode of `0xc0 + instruction_index * 2`
// Notes about rest/note length:
// * A rest/note length of 0 will pause for 256 ticks
// * A rest/note instruction will wait for an extra tick if the key-off bit is set.
// These bytecodes do have parameters
let INSTRUCTIONS_WITH_ARGUMENTS = [
// parameters: vvvvvvvv llllllll nnnnnnnO
// vvvvvvvv = i8 velocity (signed PITCH change per tick)
// llllllll = note length
// nnnnnnn = note to target (number of semitones above c0)
// O = key-off after note ends
//
// NOTE: portamento does not emit a KON event
portamento,
set_instrument,
// parameter = number of ticks to wait.
// NOTE: Does not send a keyoff event after resting.
rest,
// parameter = number of ticks to wait.
// Sends a keyoff event after resting (NOTE: waits an extra tick after keyoff).
rest_keyoff,
// parameter = subroutine ID (MAX: 127)
//
// NOTE: Only the lower 7 bits of the subroutine id are used.
// NOTE: There is no bounds checking
call_subroutine,
// parameter = u8 number of times to loop
start_loop_0,
start_loop_1,
start_loop_2,
// Breaks a loop early if the loop is on the last cycle
// Used to implement the `:` token in a MML loop.
//
// ie. Allows for `[ abc : de ]3` loops, which will play `abc de abc de abc`.
//
// parameter = number of bytes in the bytecode to skip if loop counter == 1.
skip_last_loop_0,
skip_last_loop_1,
skip_last_loop_2,
// parameter = u16 value to send to the ADSR register
set_adsr,
// parameter = u8 value to send to the GAIN register
// NOTE: Disables ADSR
set_gain,
// parameter = u8 volume (0-255)
set_volume,
// parameter = u8 volume to increment
inc_volume,
// parameter = u8 volume to decrement
dec_volume,
// parameter = pan (0-MAX_PAN, MAX_PAN/2 is centered)
set_pan,
// parameter = u8 pan to increment
inc_pan,
// parameter = u8 pan to decrement
dec_pan,
// parameter: pppppppp vvvvvvvv
// pppppppp = pan (0 - MAX_PAN, MAX_PAN/2 is centered)
// vvvvvvvv = volume
set_pan_and_volume,
];
// These bytecodes do not have parameters
let NO_ARGUMENT_INSTRUCTIONS = [
// End of channel bytecode.
//
// This instruction will either jump to the loop point or stop the channel
// (depending `MusicChannelHeader.loop`)
//
// This instruction is not recommended for sound effects.
// NOTE: `disable_channel` is slightly faster then `end`.
end,
// Return from a call instruction
return_from_subroutine,
// Jump to `start_loop_*` if `--loop_count != 0`
end_loop_0,
end_loop_1,
end_loop_2,
// padding
disable_channel,
disable_channel,
disable_channel,
disable_channel,
disable_channel,
disable_channel,
];
}