-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMIDI.FileHeader.js
More file actions
143 lines (128 loc) · 4.51 KB
/
Copy pathMIDI.FileHeader.js
File metadata and controls
143 lines (128 loc) · 4.51 KB
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
// MIDIFileHeader : Read and edit a MIDI header chunk in a given ArrayBuffer
function MIDIFileHeader(buffer) {
var a;
// No buffer creating him
if(!buffer) {
a = new Uint8Array(MIDIFileHeader.HEADER_LENGTH);
// Adding the header id (MThd)
a[0] = 0x4D; a[1] = 0x54; a[2] = 0x68; a[3] = 0x64;
// Adding the header chunk size
a[4] = 0x00; a[5] = 0x00; a[6] = 0x00; a[7] = 0x06;
// Adding the file format (1 here cause it's the most commonly used)
a[8] = 0x00; a[9] = 0x01;
// Adding the track count (1 cause it's a new file)
a[10] = 0x00; a[11] = 0x01;
// Adding the time division (192 ticks per beat)
a[12] = 0x00; a[13] = 0xC0;
// saving the buffer
this.datas = new DataView(a.buffer, 0, MIDIFileHeader.HEADER_LENGTH);
// Parsing the given buffer
} else {
if(!(buffer instanceof ArrayBuffer)) {
throw Error('Invalid buffer received.');
}
this.datas = new DataView(buffer, 0, MIDIFileHeader.HEADER_LENGTH);
// Reading MIDI header chunk
if(!(
'M' === String.fromCharCode(this.datas.getUint8(0)) &&
'T' === String.fromCharCode(this.datas.getUint8(1)) &&
'h' === String.fromCharCode(this.datas.getUint8(2)) &&
'd' === String.fromCharCode(this.datas.getUint8(3))
)) {
throw new Error('Invalid MIDIFileHeader : MThd prefix not found');
}
// Reading chunk length
if(6 !== this.datas.getUint32(4)) {
throw new Error('Invalid MIDIFileHeader : Chunk length must be 6');
}
}
}
// Static constants
MIDIFileHeader.HEADER_LENGTH = 14;
MIDIFileHeader.FRAMES_PER_SECONDS = 1;
MIDIFileHeader.TICKS_PER_BEAT = 2;
// MIDI file format
MIDIFileHeader.prototype.getFormat = function() {
var format = this.datas.getUint16(8);
if(0 !== format && 1 !== format && 2 !== format) {
throw new Error('Invalid MIDI file : MIDI format (' + format + '),' +
' format can be 0, 1 or 2 only.');
}
return format;
};
MIDIFileHeader.prototype.setFormat = function(format) {
if(0 !== format && 1 !== format && 2 !== format) {
throw new Error('Invalid MIDI format given (' + format + '),' +
' format can be 0, 1 or 2 only.');
}
this.datas.setUint16(8, format);
};
// Number of tracks
MIDIFileHeader.prototype.getTracksCount = function() {
return this.datas.getUint16(10);
};
MIDIFileHeader.prototype.setTracksCount = function(n) {
return this.datas.setUint16(10, n);
};
// Tick compute
MIDIFileHeader.prototype.getTickResolution = function(tempo) {
// Frames per seconds
if(this.datas.getUint16(12) & 0x8000) {
return 1000000 / (this.getSMPTEFrames() * this.getTicksPerFrame());
// Ticks per beat
}
// Default MIDI tempo is 120bpm, 500ms per beat
tempo = tempo || 500000;
return tempo / this.getTicksPerBeat();
};
// Time division type
MIDIFileHeader.prototype.getTimeDivision = function() {
if(this.datas.getUint16(12) & 0x8000) {
return MIDIFileHeader.FRAMES_PER_SECONDS;
}
return MIDIFileHeader.TICKS_PER_BEAT;
};
// Ticks per beat
MIDIFileHeader.prototype.getTicksPerBeat = function() {
var divisionWord = this.datas.getUint16(12);
if(divisionWord & 0x8000) {
throw new Error('Time division is not expressed as ticks per beat.');
}
return divisionWord;
};
MIDIFileHeader.prototype.setTicksPerBeat = function(ticksPerBeat) {
this.datas.setUint16(12, ticksPerBeat & 0x7FFF);
};
// Frames per seconds
MIDIFileHeader.prototype.getSMPTEFrames = function() {
var divisionWord = this.datas.getUint16(12);
var smpteFrames;
if(!(divisionWord & 0x8000)) {
throw new Error('Time division is not expressed as frames per seconds.');
}
smpteFrames = divisionWord & 0x7F00;
if(-1 === [24, 25, 29, 30].indexOf(smpteFrames)) {
throw new Error('Invalid SMPTE frames value (' + smpteFrames + ').');
}
return 29 === smpteFrames ? 29.97 : smpteFrames;
};
MIDIFileHeader.prototype.getTicksPerFrame = function() {
var divisionWord = this.datas.getUint16(12);
if(!(divisionWord & 0x8000)) {
throw new Error('Time division is not expressed as frames per seconds.');
}
return divisionWord & 0x00FF;
};
MIDIFileHeader.prototype.setSMTPEDivision = function(smpteFrames, ticksPerFrame) {
if(29.97 === smpteFrames) {
smpteFrames = 29;
}
if(-1 === [24, 25, 29, 30].indexOf(smpteFrames)) {
throw new Error('Invalid SMPTE frames value given (' + smpteFrames + ').');
}
if(0 > ticksPerFrame || 0xFF < ticksPerFrame) {
throw new Error('Invalid ticks per frame value given (' + smpteFrames + ').');
}
this.datas.setUint8(12, 0x80 | smpteFrames);
this.datas.setUint8(13, ticksPerFrame);
};