forked from Guichaguri/node-chiptune
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
218 lines (189 loc) · 6.7 KB
/
index.js
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
const stream = require('stream');
const native = require('libopenmpt');
module.exports = OpenMPT_Module;
function OpenMPT_Module(fileData) {
this.mod_ptr = openModule(fileData);
}
OpenMPT_Module.prototype = {
get repeat_count() {
return native._openmpt_module_get_repeat_count(this.mod_ptr);
},
set repeat_count(count) {
native._openmpt_module_set_repeat_count(this.mod_ptr, count);
},
get position_seconds() {
return native._openmpt_module_get_position_seconds(this.mod_ptr);
},
set_position_seconds: function(seconds) {
return native._openmpt_module_set_position_seconds(this.mod_ptr, seconds);
},
set_position_order_row: function(orderId, rowId) {
return native._openmpt_module_set_position_order_row(this.mod_ptr, orderId, rowId);
},
get duration_seconds() {
return native._openmpt_module_get_duration_seconds(this.mod_ptr);
},
get current_speed() {
return native._openmpt_module_get_current_speed(this.mod_ptr);
},
get current_tempo() {
return native._openmpt_module_get_current_tempo(this.mod_ptr);
},
get current_pattern() {
return native._openmpt_module_get_current_pattern(this.mod_ptr);
},
get current_row() {
return native._openmpt_module_get_current_row(this.mod_ptr);
},
get current_order() {
return native._openmpt_module_get_current_order(this.mod_ptr);
},
get num_channels() {
return native._openmpt_module_get_num_channels(this.mod_ptr);
},
get num_instruments() {
return native._openmpt_module_get_num_instruments(this.mod_ptr);
},
get num_orders() {
return native._openmpt_module_get_num_orders(this.mod_ptr);
},
get num_patterns() {
return native._openmpt_module_get_num_patterns(this.mod_ptr);
},
get num_samples() {
return native._openmpt_module_get_num_samples(this.mod_ptr);
},
get num_subsongs() {
return native._openmpt_module_get_num_subsongs(this.mod_ptr);
},
get_pattern_num_rows: function(patternId) {
return native._openmpt_module_get_pattern_num_rows(this.mod_ptr, patternId);
},
get_pattern_row_channel: function(patternId, rowId, channelId) {
const mod_ptr = this.mod_ptr;
const get_command = native._openmpt_module_get_pattern_row_channel_command.bind(native, mod_ptr, patternId, rowId, channelId)
const paramValue = get_command(5);
const data = {
note: get_command(0),
instrument: get_command(1),
volumeEffect: get_command(2),
effect: get_command(3),
volume: get_command(4),
parameter: (paramValue < 0)?(Math.abs(paramValue) + 0x80):paramValue, // emscripten tends to interpret high values as negative
string: native.Pointer_stringify(native._openmpt_module_format_pattern_row_channel(mod_ptr, patternId, rowId, channelId, 16, 0))
}
return data;
},
get_order_pattern: function(orderId) {
return native._openmpt_module_get_order_pattern(this.mod_ptr, orderId);
},
select_subsong: function(subsongId) {
return native._openmpt_module_select_subsong(this.mod_ptr, subsongId);
},
get metadata() {
const metadata = {};
const keys = native.Pointer_stringify(native._openmpt_module_get_metadata_keys(this.mod_ptr)).split(';');
for(var i = 0; i < keys.length; i++) {
var buf = native._malloc(keys[i].length + 1);
native.writeStringToMemory(keys[i], buf);
metadata[keys[i]] = native.Pointer_stringify(native._openmpt_module_get_metadata(this.mod_ptr, buf));
native._free(buf);
}
return metadata;
},
get ctls() {
const ctls = {};
const ctlList = native.Pointer_stringify(native._openmpt_module_get_ctls(this.mod_ptr)).split(';');
const mod_ptr = this.mod_ptr;
ctlList.forEach(function(ctlKey){
Object.defineProperty(ctls, ctlKey, {
configurable : false,
enumerable : true,
get : function(){
const keyBuf = native._malloc(ctlKey.length + 1);
native.writeStringToMemory(ctlKey, keyBuf);
const val = native.Pointer_stringify(native._openmpt_module_ctl_get(mod_ptr, keyBuf));
native._free(keyBuf);
return val;
},
set : function(ctlVal){
const keyBuf = native._malloc(ctlKey.length + 1);
const valBuf = native._malloc(ctlVal.length + 1);
native.writeStringToMemory(ctlKey, keyBuf);
native.writeStringToMemory(ctlVal, valBuf);
native._openmpt_module_ctl_set(mod_ptr, keyBuf, valBuf);
native._free(keyBuf);
native._free(valBuf);
}
})
})
return ctls;
}
}
class ModuleStream extends stream.Readable {
constructor(module, channels, samplerate, maxFramesPerChunk) {
if(channels != 1 && channels != 2 && channels != 4) {
throw new Error('Invalid number of channels');
}
const bytesPerFrame = 2 * channels;
const options = {
read: function() {
if(this.destroyed) {
this.push(null);
return;
}
const buf = readModule(module.mod_ptr, this.buf_ptr, samplerate, channels, maxFramesPerChunk, bytesPerFrame);
if(buf == null) {
native._free(this.buf_ptr);
}
this.push(buf);
}
}
super(options) // `this` only defined after this point
this.buf_ptr = initBuffer(maxFramesPerChunk, bytesPerFrame);
this.destroyed = false;
}
destroy() {
this.destroyed = true;
native._free(this.buf_ptr);
}
}
// Default values match those of node-speaker
OpenMPT_Module.prototype.openAsStream = function(channels = 2, samplerate = 44100, maxFramesPerChunk = 1024) {
return new ModuleStream(this, channels, samplerate, maxFramesPerChunk);
}
OpenMPT_Module.prototype.destroy = function () {
native._openmpt_module_destroy(this.mod_ptr);
this.mod_ptr = null;
delete this.mod_ptr;
}
function openModule(fileData) {
var array = new Int8Array(fileData);
var allocMem = native._malloc(array.byteLength);
native.HEAPU8.set(array, allocMem);
return native._openmpt_module_create_from_memory(allocMem, array.byteLength, 0, 0, 0);
}
function initBuffer(bytesPerFrame, maxFramesPerChunk) {
return native._malloc(bytesPerFrame * maxFramesPerChunk);
}
function readModule(mod_ptr, buf_ptr, samplerate, channels, maxFramesPerChunk, bytesPerFrame) {
var frames = 0;
switch(channels) {
case 1:
frames = native._openmpt_module_read_mono(mod_ptr, samplerate, maxFramesPerChunk, buf_ptr);
break;
case 2:
frames = native._openmpt_module_read_interleaved_stereo(mod_ptr, samplerate, maxFramesPerChunk, buf_ptr);
break;
case 4:
frames = native._openmpt_module_read_interleaved_quad(mod_ptr, samplerate, maxFramesPerChunk, buf_ptr);
break;
}
if(frames <= 0) return null;
var rawpcm = native.HEAPU8.subarray(buf_ptr, buf_ptr + bytesPerFrame * frames);
return new Buffer(rawpcm.buffer).slice(rawpcm.byteOffset, rawpcm.byteOffset + rawpcm.byteLength);
}
function cleanupModule(mod_ptr, buf_ptr) {
native._free(buf_ptr);
native._openmpt_module_destroy(mod_ptr);
}