n65 0.5.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +28 -0
  4. data/.gitignore +1 -1
  5. data/.rubocop.yml +125 -0
  6. data/Gemfile +3 -1
  7. data/README.md +3 -20
  8. data/Rakefile +15 -1
  9. data/bin/n65 +2 -0
  10. data/data/opcodes.yaml +39 -39
  11. data/everdrive_transfer/everdrive.rb +175 -0
  12. data/examples/pulse_chord.asm +1 -1
  13. data/examples/scales.asm +182 -0
  14. data/lib/n65/directives/ascii.rb +4 -19
  15. data/lib/n65/directives/bytes.rb +20 -35
  16. data/lib/n65/directives/dw.rb +22 -36
  17. data/lib/n65/directives/enter_scope.rb +14 -30
  18. data/lib/n65/directives/exit_scope.rb +7 -17
  19. data/lib/n65/directives/inc.rb +14 -30
  20. data/lib/n65/directives/incbin.rb +6 -24
  21. data/lib/n65/directives/ines_header.rb +66 -27
  22. data/lib/n65/directives/label.rb +8 -21
  23. data/lib/n65/directives/org.rb +9 -19
  24. data/lib/n65/directives/segment.rb +5 -19
  25. data/lib/n65/directives/space.rb +6 -18
  26. data/lib/n65/front_end.rb +36 -39
  27. data/lib/n65/instruction.rb +123 -159
  28. data/lib/n65/instruction_base.rb +6 -18
  29. data/lib/n65/memory_space.rb +51 -71
  30. data/lib/n65/opcodes.rb +3 -5
  31. data/lib/n65/parser.rb +20 -38
  32. data/lib/n65/regexes.rb +20 -21
  33. data/lib/n65/symbol_table.rb +58 -89
  34. data/lib/n65/version.rb +3 -1
  35. data/lib/n65.rb +120 -121
  36. data/n65.gemspec +17 -12
  37. data/nes_lib/nes.sym +2 -2
  38. data/spec/.rubocop.yml +4 -0
  39. data/spec/assembler_spec.rb +84 -0
  40. data/spec/lib/n65/memory_space_spec.rb +147 -0
  41. data/spec/lib/n65/symbol_table_spec.rb +291 -0
  42. data/utils/opcode_table_to_yaml.rb +65 -67
  43. data.tar.gz.sig +0 -0
  44. metadata +84 -41
  45. metadata.gz.sig +0 -0
  46. data/examples/music_driver.asm +0 -202
  47. data/test/test_memory_space.rb +0 -82
  48. data/test/test_symbol_table.rb +0 -238
  49. data/utils/midi/Makefile +0 -3
  50. data/utils/midi/c_scale.mid +0 -0
  51. data/utils/midi/convert +0 -0
  52. data/utils/midi/guitar.mid +0 -0
  53. data/utils/midi/include/event.h +0 -93
  54. data/utils/midi/include/file.h +0 -57
  55. data/utils/midi/include/helpers.h +0 -14
  56. data/utils/midi/include/track.h +0 -45
  57. data/utils/midi/lil_melody.mid +0 -0
  58. data/utils/midi/mi_feabhra.mid +0 -0
  59. data/utils/midi/midi_to_nes.rb +0 -204
  60. data/utils/midi/source/convert.cpp +0 -16
  61. data/utils/midi/source/event.cpp +0 -96
  62. data/utils/midi/source/file.cpp +0 -37
  63. data/utils/midi/source/helpers.cpp +0 -46
  64. data/utils/midi/source/track.cpp +0 -37
@@ -1,202 +0,0 @@
1
- ;------------------------------------------------------------------------------
2
- ; An NES music engine that understands the binary stream outputted from my
3
- ; MIDI converter :)
4
- ;;;;
5
- ; Create an iNES header
6
- .ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
7
-
8
-
9
- ;;;;
10
- ; Include all the symbols in the nes library
11
- .inc <nes.sym>
12
-
13
-
14
- ;;;;
15
- ; Let's put a data structure to control the sound engine in the zero page
16
- .org $0000
17
- .scope sound_engine
18
- ; Where we are reading from ROM
19
- .space stream_read_ptr_lo 1
20
- .space stream_read_ptr_hi 1
21
-
22
- ; Where we are writing in the APU
23
- .space stream_write_ptr_lo 1
24
- .space stream_write_ptr_hi 1
25
- .space delta 1
26
- .
27
-
28
-
29
- ;;;;
30
- ; Open the prog section bank 0
31
- .segment prog 0
32
-
33
-
34
- ;;;;
35
- ; Setup the interrupt vectors
36
- .org $FFFA
37
- .dw vblank
38
- .dw main
39
- .dw irq
40
-
41
-
42
- ;;;;
43
- ; Here is our code entry point, which we'll call main.
44
- .org $C000
45
- .scope main
46
- ; Disable interrupts and decimal flag
47
- sei
48
- cld
49
-
50
- ; Wait for 2 vblanks
51
- wait_vb1:
52
- lda nes.ppu.status
53
- bpl wait_vb1
54
- wait_vb2:
55
- lda nes.ppu.status
56
- bpl wait_vb2
57
-
58
- ; Now we want to initialize the hardware to a known state
59
- lda #%00
60
- ldx #$00
61
- clear_segments:
62
- sta $0, x
63
- sta $100, x
64
- sta $200, x
65
- sta $300, x
66
- sta $400, x
67
- sta $500, x
68
- sta $600, x
69
- sta $700, x
70
- inx
71
- bne clear_segments
72
-
73
- ; Reset the stack pointer
74
- ldx #$FF
75
- txs
76
-
77
- ; Disable all graphics and vblank nmi
78
- lda #$00
79
- sta nes.ppu.control
80
- sta nes.ppu.mask
81
-
82
- jsr init_sound
83
-
84
- ; Resume interrupts and NMI and loop here forever
85
- lda #%10000000
86
- sta nes.ppu.control
87
- cli
88
- forever:
89
- jmp forever
90
- .
91
-
92
-
93
- ;;;;
94
- ; Initialize the APU to enable Pulse1
95
- .scope init_sound
96
- lda #$00
97
- ldy #$00
98
- clear_apu:
99
- sta nes.apu, y
100
- iny
101
- cpy #$10
102
- bne clear_apu
103
-
104
- lda #>music_buffer
105
- ldx #<music_buffer
106
- sta sound_engine.stream_read_ptr_hi
107
- stx sound_engine.stream_read_ptr_lo
108
-
109
- lda #$40
110
- ldx #$00
111
- sta sound_engine.stream_write_ptr_hi
112
- stx sound_engine.stream_write_ptr_lo
113
-
114
- lda #$01
115
- sta sound_engine.delta
116
-
117
- lda #$01
118
- sta nes.apu.channel_enable
119
- rts
120
- .
121
-
122
-
123
- ;;;;
124
- ; VBlank reads our music buffer
125
- .scope vblank
126
- ; Backup our registers
127
- pha
128
- txa
129
- pha
130
- tya
131
- pha
132
-
133
- jsr sound_engine_driver
134
-
135
- ; Restore the registers
136
- pla
137
- tay
138
- pla
139
- tax
140
- pla
141
- rti
142
- .
143
-
144
- ;;;;
145
- ; Sound driver that updates the APU registers
146
- ; via a stream of APU write commands
147
- .scope sound_engine_driver
148
- dec sound_engine.delta
149
- bne done
150
-
151
- read_event:
152
- ; Load the new delta from the stream
153
- ldy #$00
154
- lda (sound_engine.stream_read_ptr_lo), Y
155
- sta sound_engine.delta
156
-
157
- ; Read pulse1 control register value
158
- ldy #$01
159
- lda (sound_engine.stream_read_ptr_lo), Y
160
- sta nes.apu.pulse1.control
161
-
162
- ; Read the value for pulse1.ft
163
- ldy #$02
164
- lda (sound_engine.stream_read_ptr_lo), Y
165
- sta nes.apu.pulse1.ft
166
-
167
-
168
- ; Read the value for pulse1.ct
169
- ldy #$03
170
- lda (sound_engine.stream_read_ptr_lo), Y
171
- sta nes.apu.pulse1.ct
172
-
173
- ; Advance the 16-bit stream pointer by number of bytes read
174
- lda sound_engine.stream_read_ptr_lo
175
- clc
176
- adc #$04
177
- sta sound_engine.stream_read_ptr_lo
178
- bcc done
179
- inc sound_engine.stream_read_ptr_hi
180
-
181
- ; If the very next event is 0 delta away, do it now too
182
- ; Read the value for pulse1.ct
183
- ldy #$04
184
- lda (sound_engine.stream_read_ptr_lo), Y
185
- beq read_event
186
-
187
- done:
188
- rts
189
- .
190
-
191
-
192
- ;;;;
193
- ; IRQ Does nothing
194
- .scope irq
195
- rti
196
- .
197
-
198
- ;;;;
199
- ; Include the music buffer stream
200
- .org $D000
201
- music_buffer:
202
- .incbin "data.mus"
@@ -1,82 +0,0 @@
1
- gem 'minitest'
2
- require 'minitest/autorun'
3
- require 'minitest/unit'
4
-
5
- require_relative '../lib/n65/memory_space.rb'
6
-
7
-
8
- class TestMemorySpace < MiniTest::Test
9
- include N65
10
-
11
- def _test_create_prog_rom
12
- ## First just try to read alll of it
13
- space = MemorySpace.create_prog_rom
14
- contents = space.read(0x8000, 0x4000)
15
- assert_equal(contents.size, 0x4000)
16
- assert(contents.all?{|byte| byte.zero?})
17
-
18
- ## It is mirrored so this should also work
19
- space = MemorySpace.create_prog_rom
20
- contents = space.read(0xC000, 0x4000)
21
- assert_equal(contents.size, 0x4000)
22
- assert(contents.all?{|byte| byte.zero?})
23
- end
24
-
25
-
26
- def _test_writing
27
- ## Write some bytes into prog 2 area
28
- space = MemorySpace.create_prog_rom
29
- space.write(0xC000, "hi there".bytes)
30
-
31
- ## Read them back..
32
- contents = space.read(0xC000, 8)
33
- assert_equal('hi there', contents.pack('C*'))
34
-
35
- ## Should be mirrored in prog 1
36
- contents = space.read(0x8000, 8)
37
- assert_equal('hi there', contents.pack('C*'))
38
- end
39
-
40
-
41
- def _test_reading_out_of_bounds
42
- space = MemorySpace.create_prog_rom
43
- assert_raises(MemorySpace::AccessOutsideProgRom) do
44
- space.read(0x200, 10)
45
- end
46
-
47
- ## But that is valid char rom area, so no explody
48
- space = MemorySpace.create_char_rom
49
- space.read(0x200, 10)
50
-
51
- ## But something like this should explode
52
- space = MemorySpace.create_char_rom
53
- assert_raises(MemorySpace::AccessOutsideCharRom) do
54
- space.read(0x8001, 10)
55
- end
56
- end
57
-
58
-
59
- ####
60
- ## There seem to be problems writing bytes right to
61
- ## the end of the memory map, specifically where the
62
- ## vector table is in prog rom, so let's test that.
63
- def test_writing_to_end
64
- space = MemorySpace.create_prog_rom
65
- bytes = [0xDE, 0xAD]
66
-
67
- ## Write the NMI address to FFFA
68
- space.write(0xFFFA, bytes)
69
-
70
- ## Write the entry point to FFFC
71
- space.write(0xFFFC, bytes)
72
-
73
- ## Write the irq to FFFE, and this fails, saying
74
- ## I'm trying to write to $10000 for some reason.
75
- space.write(0xFFFE, bytes)
76
-
77
- ## Write to the very first
78
- space.write(0x8000, bytes)
79
- end
80
-
81
- end
82
-
@@ -1,238 +0,0 @@
1
- gem 'minitest'
2
- require 'minitest/autorun'
3
- require 'minitest/unit'
4
-
5
- require_relative '../lib/n65/symbol_table.rb'
6
- require_relative '../lib/n65.rb'
7
-
8
-
9
- class TestSymbolTable < MiniTest::Test
10
- include N65
11
-
12
- ####
13
- ## Test that we can make simple global symbols
14
- def test_define_global_symbols
15
- st = SymbolTable.new
16
- st.define_symbol('dog', 'woof')
17
- assert_equal('woof', st.resolve_symbol('dog'))
18
- end
19
-
20
-
21
- ####
22
- ## Test entering into a sub scope, and setting and retrieving values
23
- def test_enter_scope
24
- st = SymbolTable.new
25
- st.enter_scope('animals')
26
- st.define_symbol('dog', 'woof')
27
- assert_equal('woof', st.resolve_symbol('dog'))
28
- end
29
-
30
-
31
- ####
32
- ## Access something from an outer scope without dot syntax
33
- def test_outer_scope
34
- st = SymbolTable.new
35
- st.enter_scope('outer')
36
- st.define_symbol('dog', 'woof')
37
- st.enter_scope('inner')
38
- st.define_symbol('pig', 'oink')
39
- assert_equal('woof', st.resolve_symbol('dog'))
40
- end
41
-
42
-
43
- ####
44
- ## Access something from an outer scope without dot syntax
45
- def test_shadow
46
- st = SymbolTable.new
47
- st.enter_scope('outer')
48
- st.define_symbol('dog', 'woof')
49
- st.enter_scope('inner')
50
- st.define_symbol('dog', 'bark')
51
- assert_equal('bark', st.resolve_symbol('dog'))
52
- assert_equal('woof', st.resolve_symbol('outer.dog'))
53
- st.exit_scope
54
- st.exit_scope
55
- assert_equal('bark', st.resolve_symbol('outer.inner.dog'))
56
- end
57
-
58
-
59
- ####
60
- ## Test exiting a sub scope, and seeing that the variable is unavailable by simple name
61
- def test_exit_scope
62
- st = SymbolTable.new
63
- st.enter_scope('animals')
64
- st.define_symbol('dog', 'woof')
65
- assert_equal('woof', st.resolve_symbol('dog'))
66
-
67
- st.exit_scope
68
-
69
- assert_raises(SymbolTable::UndefinedSymbol) do
70
- assert_equal('woof', st.resolve_symbol('dog'))
71
- end
72
- end
73
-
74
-
75
- ####
76
- ## Test exiting a sub scope, and being able to access a symbol through a full path
77
- def test_exit_scope
78
- st = SymbolTable.new
79
- st.enter_scope('animals')
80
- st.define_symbol('dog', 'woof')
81
- assert_equal('woof', st.resolve_symbol('dog'))
82
-
83
- st.exit_scope
84
-
85
- assert_equal('woof', st.resolve_symbol('animals.dog'))
86
- end
87
-
88
-
89
- ####
90
- ## Have two symbols that are the same but are in different scopes
91
- def test_two_scopes_same_symbol
92
- st = SymbolTable.new
93
- st.define_symbol('dog', 'woof')
94
- assert_equal('woof', st.resolve_symbol('dog'))
95
-
96
- st.enter_scope('animals')
97
-
98
- st.define_symbol('dog', 'woofwoof')
99
- assert_equal('woofwoof', st.resolve_symbol('dog'))
100
-
101
- st.exit_scope
102
-
103
- assert_equal('woof', st.resolve_symbol('dog'))
104
- assert_equal('woofwoof', st.resolve_symbol('animals.dog'))
105
- end
106
-
107
-
108
- ####
109
- ## How do you get stuff out of the global scope when you are in
110
- ## a sub scope?
111
- def test_access_global_scope
112
- st = SymbolTable.new
113
- st.define_symbol('dog', 'woof')
114
- assert_equal('woof', st.resolve_symbol('dog'))
115
-
116
- st.enter_scope('animals')
117
- st.define_symbol('pig', 'oink')
118
- assert_equal('oink', st.resolve_symbol('pig'))
119
-
120
- ## Ok, now I want to access global.dog basically from the previous scope
121
- assert_equal('woof', st.resolve_symbol('global.dog'))
122
- end
123
-
124
-
125
- ####
126
- ## Now I want to just test making an anonymous scope
127
- def test_anonymous_scope
128
- st = SymbolTable.new
129
- st.define_symbol('dog', 'woof')
130
- assert_equal('woof', st.resolve_symbol('dog'))
131
-
132
- st.enter_scope
133
- st.define_symbol('pig', 'oink')
134
- assert_equal('oink', st.resolve_symbol('pig'))
135
-
136
- ## Ok, now I want to access global.dog basically from the previous scope
137
- assert_equal('woof', st.resolve_symbol('global.dog'))
138
-
139
- ## Now exit the anonymous scope and get dog
140
- st.exit_scope
141
- assert_equal('woof', st.resolve_symbol('global.dog'))
142
- assert_equal('woof', st.resolve_symbol('dog'))
143
- end
144
-
145
-
146
- ####
147
- ## Now I want to test that I cannot exist the outer-most
148
- ## global scope by mistake
149
- def test_cant_exit_global
150
- st = SymbolTable.new
151
- assert_raises(SymbolTable::CantExitScope) do
152
- st.exit_scope
153
- end
154
- end
155
-
156
-
157
- ####
158
- ## I would like the name of the scope to take on the
159
- ## value of the program counter at that location.
160
- def test_scope_as_symbol
161
- program = <<-ASM
162
- .ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
163
- .org $8000
164
- .segment prog 0
165
- .scope main
166
- sei
167
- cld
168
- lda \#$00
169
- jmp main
170
- .
171
- jmp main
172
- jmp global.main
173
- ASM
174
-
175
- #### There really should be an evaluate string method
176
- assembler = Assembler.new
177
- program.split(/\n/).each do |line|
178
- assembler.assemble_one_line(line)
179
- end
180
- assembler.fulfill_promises
181
- assert_equal(0x8000, assembler.symbol_table.resolve_symbol('global.main'))
182
- end
183
-
184
-
185
- ####
186
- ## Fix a bug where we can't see a forward declared symbol in a scope
187
- def test_foward_declaration_in_scope
188
- program = <<-ASM
189
- ;;;;
190
- ; Create an iNES header
191
- .ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
192
-
193
- ;;;;
194
- ; Try to expose a problem we have with scopes
195
- ; We don't seem to be able to branch to a forward
196
- ; declared symbol within a scope
197
- .org $8000
198
- .scope main
199
- sei
200
- cld
201
- lda #\$00
202
- bne forward_symbol
203
- nop
204
- nop
205
- nop
206
- forward_symbol:
207
- rts
208
- .
209
- ASM
210
-
211
- #### There really should be an evaluate string method
212
- assembler = Assembler.new
213
- program.split(/\n/).each do |line|
214
- assembler.assemble_one_line(line)
215
- end
216
- puts YAML.dump(assembler.symbol_table)
217
- assembler.fulfill_promises
218
-
219
- #### The forward symbol should have been resolved to +3, and the ROM should look like this:
220
- correct_rom = [0x4e, 0x45, 0x53, 0x1a, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
221
- 0x78, # SEI
222
- 0xd8, # CLD
223
- 0xa9, 0x0, # LDA immediate 0
224
- 0xd0, 0x3, # BNE +3
225
- 0xea, # NOP
226
- 0xea, # NOP
227
- 0xea, # NOP
228
- 0x60 # RTS forward_symbol
229
- ]
230
-
231
- #### Grab the first 26 bytes of the rom and make sure they assemble to the above
232
- emitted_rom = assembler.emit_binary_rom.bytes[0...26]
233
- assert_equal(correct_rom, emitted_rom)
234
- #### Yup it is fixed now.
235
- end
236
-
237
- end
238
-
data/utils/midi/Makefile DELETED
@@ -1,3 +0,0 @@
1
-
2
- build: convert
3
- clang++ source/*.cpp -I include -o convert
Binary file
data/utils/midi/convert DELETED
Binary file
Binary file
@@ -1,93 +0,0 @@
1
- #ifndef MIDI_EVENT_H
2
- #define MIDI_EVENT_H
3
-
4
- #include <stdio.h>
5
- #include <stdlib.h>
6
- #include "helpers.h"
7
-
8
- namespace Midi {
9
-
10
- enum MidiMessageType {Midi = 0, MidiNoteOff = 0x8, MidiNoteOn, MidiPolyAftertouch,
11
- MidiControlChange, MidiProgramChange, MidiChannelAftertouch,
12
- MidiPitchWheel, MidiMetaEvent = 0xFF, MidiSysex1 = 0xF0, MidiSysex2 = 0xF7};
13
-
14
-
15
- enum MidiMetaType {MetaSequenceNumber = 0x0, MetaTextEvent = 0x1, MetaCopyright = 0x2,
16
- MetaTrackName = 0x3, MetaInstrumentName = 0x4, MetaLyricText = 0x5,
17
- MetaMarkerText = 0x6, MetaCuePoint = 0x7, MetaChannelPrefixAssignment = 0x20,
18
- MetaEndOfTrack = 0x2F, MetaTempoSetting = 0x51, MetaSMPTEOffset = 0x54,
19
- MetaTimeSignature = 0x58, MetaKeySignature = 0x59, MetaSequenceSpecific = 0x7F};
20
-
21
- /***
22
- * Spec:
23
- * MidiTrackEvent = <delta_time (Variable)> + <MidiEvent> | <MetaEvent> | <SysexEvent>
24
- * MidiEvent = <MidiMessageType (4 bits)> + <channel (4 bits)> + <parameter1 (1 byte)> <parameter2 (1 byte)>
25
- * MetaEvent = 0xFF + <MidiMetaType> + <size (Variable)> + <data>
26
- * SysexEvent = 0xF0 + <data> + 0xF7 -or-
27
- * SysexEvent = 0xF7 + <data> 0xF7
28
- ***/
29
-
30
- class Event {
31
- private:
32
- unsigned int m_delta;
33
- unsigned int m_size;
34
- unsigned int m_data_size;
35
- unsigned char m_status;
36
- unsigned char m_meta_type;
37
- unsigned char m_parameter1;
38
- unsigned char m_parameter2;
39
- void *m_data;
40
-
41
- public:
42
- Event();
43
- Event(int delta, int status, int parameter1, int parameter2);
44
- void init_midi(int delta, int status, int parameter1, int parameter2);
45
- void init_from_file(FILE *fp, unsigned char last_status);
46
- ~Event();
47
-
48
- // Some easy inline functions
49
- static bool is_status_byte(unsigned char byte) { return ((byte & 0x80) >> 7) == 1; }
50
- unsigned int delta() const { return m_delta; }
51
- unsigned char status() const { return m_status; }
52
- unsigned int bytes_read() const { return m_size; }
53
- unsigned char parameter1() const { return m_parameter1; }
54
- unsigned char parameter2() const { return m_parameter2; }
55
- int message_type() const { return (m_status &0xF0) >> 4; }
56
- int channel() const { return (m_status &0xF); }
57
- void *sysex_data() const { return m_data; }
58
-
59
-
60
- void print_yaml(){
61
- printf(" - :delta: 0x%X\n", m_delta);
62
- printf(" :status: 0x%X\n", m_status);
63
-
64
- switch(m_status){
65
- case 0xFF:
66
- printf(" :meta_type: 0x%X\n", m_meta_type);
67
- printf(" :meta_data_size: 0x%X\n", m_data_size);
68
- break;
69
- case 0xF0:
70
- case 0xF7:
71
- printf(" :sysex_data_size: %d\n", m_data_size);
72
- break;
73
- default:
74
- printf(" :parameter1: 0x%X\n", m_parameter1);
75
- printf(" :parameter2: 0x%X\n", m_parameter2);
76
- }
77
- /* Printing out the actual data as a string confuses the YAML parser
78
- * So let's just not do that.
79
- if(m_data){
80
- printf(" :data: \"");
81
-
82
- for(int i = 0; i < m_data_size; i++){
83
- printf("%c", ((unsigned char*)m_data)[i]);
84
- }
85
- printf("\"\n");
86
- }
87
- */
88
- }
89
- };
90
-
91
- }
92
-
93
- #endif
@@ -1,57 +0,0 @@
1
- #ifndef MIDI_FILE_H
2
- #define MIDI_FILE_H
3
-
4
- #include <vector>
5
- #include "track.h"
6
-
7
- namespace Midi {
8
-
9
- /***
10
- * Spec:
11
- * MidiFile = <midi_header_t> + <MidiTrack> [+ <MidiTrack> ...]
12
- *
13
- * midi_header_t = "MThd" + <size (4 bytes)> + <format (2 bytes)> + <track_count (2 bytes)> + <ticks_per_quarter_note (2 bytes)>
14
- *
15
- ***/
16
-
17
- typedef struct midi_header_t {
18
- char cookie[4];
19
- unsigned int size;
20
- unsigned short format;
21
- unsigned short track_count;
22
- unsigned short ticks_per_quarter_note;
23
- } __attribute__((packed)) midi_header_t;
24
-
25
-
26
- class File {
27
- private:
28
- midi_header_t m_header;
29
-
30
- public:
31
- File(void);
32
- ~File(void);
33
- std::vector<Track *> m_tracks;
34
- void init_from_file(const char *filename);
35
-
36
- void print_yaml(){
37
- printf("---\n");
38
- printf(":midi_file:\n");
39
- printf(" :header: Mthd\n");
40
- printf(" :size: %u\n", m_header.size);
41
- printf(" :format: %u\n", m_header.format);
42
- printf(" :track_count: %u\n", m_header.track_count);
43
- printf(" :ticks_per_quarter_note: %u\n", m_header.ticks_per_quarter_note);
44
- printf(" :tracks:\n");
45
-
46
- for(int i = 0; i < m_tracks.size(); i++){
47
- if(i == 0){
48
- m_tracks[i]->print_yaml();
49
- }else{
50
- m_tracks[i]->print_yaml();
51
- }
52
- }
53
- }
54
- };
55
-
56
- }
57
- #endif
@@ -1,14 +0,0 @@
1
- #ifndef MIDI_HELPERS_H
2
- #define MIDI_HELPERS_H
3
-
4
- #include <stdio.h>
5
- #include <stdlib.h>
6
-
7
- namespace Midi {
8
-
9
- unsigned int read_variable_length(FILE *fp, unsigned int *value_size);
10
- short swap_endian_16(short big_endian);
11
- int swap_endian_32(int big_endian);
12
-
13
- }
14
- #endif