spessasynth_lib 0.0.1
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.
- package/.idea/modules.xml +8 -0
- package/.idea/spessasynth_lib.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/copy_version.sh +38 -0
- package/index.js +73 -0
- package/package/@types/externals/stbvorbis_sync/stbvorbis_sync.min.d.ts +1 -0
- package/package/@types/index.d.ts +34 -0
- package/package/@types/midi_handler/midi_handler.d.ts +39 -0
- package/package/@types/midi_handler/web_midi_link.d.ts +12 -0
- package/package/@types/midi_parser/midi_data.d.ts +95 -0
- package/package/@types/midi_parser/midi_editor.d.ts +45 -0
- package/package/@types/midi_parser/midi_loader.d.ts +100 -0
- package/package/@types/midi_parser/midi_message.d.ts +154 -0
- package/package/@types/midi_parser/midi_writer.d.ts +6 -0
- package/package/@types/midi_parser/rmidi_writer.d.ts +9 -0
- package/package/@types/midi_parser/used_keys_loaded.d.ts +7 -0
- package/package/@types/sequencer/sequencer.d.ts +180 -0
- package/package/@types/sequencer/worklet_sequencer/sequencer_message.d.ts +28 -0
- package/package/@types/soundfont/read/generators.d.ts +98 -0
- package/package/@types/soundfont/read/instruments.d.ts +50 -0
- package/package/@types/soundfont/read/modulators.d.ts +73 -0
- package/package/@types/soundfont/read/presets.d.ts +87 -0
- package/package/@types/soundfont/read/riff_chunk.d.ts +31 -0
- package/package/@types/soundfont/read/samples.d.ts +134 -0
- package/package/@types/soundfont/read/zones.d.ts +141 -0
- package/package/@types/soundfont/soundfont.d.ts +76 -0
- package/package/@types/soundfont/write/ibag.d.ts +6 -0
- package/package/@types/soundfont/write/igen.d.ts +6 -0
- package/package/@types/soundfont/write/imod.d.ts +6 -0
- package/package/@types/soundfont/write/inst.d.ts +6 -0
- package/package/@types/soundfont/write/pbag.d.ts +6 -0
- package/package/@types/soundfont/write/pgen.d.ts +6 -0
- package/package/@types/soundfont/write/phdr.d.ts +6 -0
- package/package/@types/soundfont/write/pmod.d.ts +6 -0
- package/package/@types/soundfont/write/sdta.d.ts +11 -0
- package/package/@types/soundfont/write/shdr.d.ts +8 -0
- package/package/@types/soundfont/write/soundfont_trimmer.d.ts +6 -0
- package/package/@types/soundfont/write/write.d.ts +21 -0
- package/package/@types/synthetizer/audio_effects/effects_config.d.ts +29 -0
- package/package/@types/synthetizer/audio_effects/fancy_chorus.d.ts +93 -0
- package/package/@types/synthetizer/audio_effects/reverb.d.ts +7 -0
- package/package/@types/synthetizer/synth_event_handler.d.ts +161 -0
- package/package/@types/synthetizer/synthetizer.d.ts +294 -0
- package/package/@types/synthetizer/worklet_system/message_protocol/worklet_message.d.ts +89 -0
- package/package/@types/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.d.ts +134 -0
- package/package/@types/synthetizer/worklet_url.d.ts +5 -0
- package/package/@types/utils/buffer_to_wav.d.ts +8 -0
- package/package/@types/utils/byte_functions/big_endian.d.ts +13 -0
- package/package/@types/utils/byte_functions/little_endian.d.ts +35 -0
- package/package/@types/utils/byte_functions/string.d.ts +22 -0
- package/package/@types/utils/byte_functions/variable_length_quantity.d.ts +12 -0
- package/package/@types/utils/indexed_array.d.ts +21 -0
- package/package/@types/utils/loggin.d.ts +26 -0
- package/package/@types/utils/other.d.ts +32 -0
- package/package/LICENSE +26 -0
- package/package/README.md +84 -0
- package/package/externals/NOTICE +9 -0
- package/package/externals/libvorbis/@types/OggVorbisEncoder.d.ts +34 -0
- package/package/externals/libvorbis/OggVorbisEncoder.min.js +1 -0
- package/package/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
- package/package/externals/stbvorbis_sync/LICENSE +202 -0
- package/package/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
- package/package/index.js +73 -0
- package/package/midi_handler/README.md +3 -0
- package/package/midi_handler/midi_handler.js +118 -0
- package/package/midi_handler/web_midi_link.js +41 -0
- package/package/midi_parser/README.md +3 -0
- package/package/midi_parser/midi_data.js +121 -0
- package/package/midi_parser/midi_editor.js +557 -0
- package/package/midi_parser/midi_loader.js +502 -0
- package/package/midi_parser/midi_message.js +234 -0
- package/package/midi_parser/midi_writer.js +95 -0
- package/package/midi_parser/rmidi_writer.js +271 -0
- package/package/midi_parser/used_keys_loaded.js +172 -0
- package/package/package.json +43 -0
- package/package/sequencer/README.md +23 -0
- package/package/sequencer/sequencer.js +439 -0
- package/package/sequencer/worklet_sequencer/events.js +92 -0
- package/package/sequencer/worklet_sequencer/play.js +309 -0
- package/package/sequencer/worklet_sequencer/process_event.js +167 -0
- package/package/sequencer/worklet_sequencer/process_tick.js +85 -0
- package/package/sequencer/worklet_sequencer/sequencer_message.js +39 -0
- package/package/sequencer/worklet_sequencer/song_control.js +193 -0
- package/package/sequencer/worklet_sequencer/worklet_sequencer.js +218 -0
- package/package/soundfont/README.md +8 -0
- package/package/soundfont/read/generators.js +212 -0
- package/package/soundfont/read/instruments.js +125 -0
- package/package/soundfont/read/modulators.js +249 -0
- package/package/soundfont/read/presets.js +300 -0
- package/package/soundfont/read/riff_chunk.js +81 -0
- package/package/soundfont/read/samples.js +398 -0
- package/package/soundfont/read/zones.js +310 -0
- package/package/soundfont/soundfont.js +357 -0
- package/package/soundfont/write/ibag.js +39 -0
- package/package/soundfont/write/igen.js +75 -0
- package/package/soundfont/write/imod.js +46 -0
- package/package/soundfont/write/inst.js +34 -0
- package/package/soundfont/write/pbag.js +39 -0
- package/package/soundfont/write/pgen.js +77 -0
- package/package/soundfont/write/phdr.js +42 -0
- package/package/soundfont/write/pmod.js +46 -0
- package/package/soundfont/write/sdta.js +72 -0
- package/package/soundfont/write/shdr.js +54 -0
- package/package/soundfont/write/soundfont_trimmer.js +169 -0
- package/package/soundfont/write/write.js +180 -0
- package/package/synthetizer/README.md +6 -0
- package/package/synthetizer/audio_effects/effects_config.js +21 -0
- package/package/synthetizer/audio_effects/fancy_chorus.js +120 -0
- package/package/synthetizer/audio_effects/impulse_response_2.flac +0 -0
- package/package/synthetizer/audio_effects/reverb.js +24 -0
- package/package/synthetizer/synth_event_handler.js +156 -0
- package/package/synthetizer/synthetizer.js +766 -0
- package/package/synthetizer/worklet_processor.min.js +13 -0
- package/package/synthetizer/worklet_system/README.md +6 -0
- package/package/synthetizer/worklet_system/main_processor.js +363 -0
- package/package/synthetizer/worklet_system/message_protocol/handle_message.js +197 -0
- package/package/synthetizer/worklet_system/message_protocol/message_sending.js +74 -0
- package/package/synthetizer/worklet_system/message_protocol/worklet_message.js +121 -0
- package/package/synthetizer/worklet_system/minify_processor.sh +4 -0
- package/package/synthetizer/worklet_system/worklet_methods/controller_control.js +230 -0
- package/package/synthetizer/worklet_system/worklet_methods/data_entry.js +277 -0
- package/package/synthetizer/worklet_system/worklet_methods/note_off.js +109 -0
- package/package/synthetizer/worklet_system/worklet_methods/note_on.js +91 -0
- package/package/synthetizer/worklet_system/worklet_methods/program_control.js +183 -0
- package/package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +177 -0
- package/package/synthetizer/worklet_system/worklet_methods/snapshot.js +129 -0
- package/package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +272 -0
- package/package/synthetizer/worklet_system/worklet_methods/tuning_control.js +195 -0
- package/package/synthetizer/worklet_system/worklet_methods/vibrato_control.js +29 -0
- package/package/synthetizer/worklet_system/worklet_methods/voice_control.js +233 -0
- package/package/synthetizer/worklet_system/worklet_processor.js +9 -0
- package/package/synthetizer/worklet_system/worklet_utilities/lfo.js +23 -0
- package/package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +130 -0
- package/package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +73 -0
- package/package/synthetizer/worklet_system/worklet_utilities/modulator_curves.js +86 -0
- package/package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +81 -0
- package/package/synthetizer/worklet_system/worklet_utilities/unit_converter.js +66 -0
- package/package/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +265 -0
- package/package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +83 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +234 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +116 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +272 -0
- package/package/synthetizer/worklet_url.js +5 -0
- package/package/utils/README.md +4 -0
- package/package/utils/buffer_to_wav.js +101 -0
- package/package/utils/byte_functions/big_endian.js +28 -0
- package/package/utils/byte_functions/little_endian.js +74 -0
- package/package/utils/byte_functions/string.js +97 -0
- package/package/utils/byte_functions/variable_length_quantity.js +37 -0
- package/package/utils/encode_vorbis.js +30 -0
- package/package/utils/indexed_array.js +41 -0
- package/package/utils/loggin.js +79 -0
- package/package/utils/other.js +54 -0
- package/package.json +43 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { IndexedByteArray } from '../../utils/indexed_array.js'
|
|
2
|
+
import { readBytesAsUintLittleEndian, writeDword } from '../../utils/byte_functions/little_endian.js'
|
|
3
|
+
import { readBytesAsString, writeStringAsBytes } from '../../utils/byte_functions/string.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* riff_chunk.js
|
|
7
|
+
* reads a riff read and stores it as a class
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class RiffChunk
|
|
11
|
+
{
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new riff read
|
|
14
|
+
* @constructor
|
|
15
|
+
* @param header {string}
|
|
16
|
+
* @param size {number}
|
|
17
|
+
* @param data {IndexedByteArray}
|
|
18
|
+
*/
|
|
19
|
+
constructor(header, size, data) {
|
|
20
|
+
this.header = header;
|
|
21
|
+
this.size = size;
|
|
22
|
+
this.chunkData = data;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param dataArray {IndexedByteArray}
|
|
29
|
+
* @param readData {boolean}
|
|
30
|
+
* @param forceShift {boolean}
|
|
31
|
+
* @returns {RiffChunk}
|
|
32
|
+
*/
|
|
33
|
+
export function readRIFFChunk(dataArray, readData = true, forceShift = false) {
|
|
34
|
+
let header = readBytesAsString(dataArray, 4)
|
|
35
|
+
|
|
36
|
+
let size = readBytesAsUintLittleEndian(dataArray, 4)
|
|
37
|
+
let chunkData = undefined
|
|
38
|
+
if (readData)
|
|
39
|
+
{
|
|
40
|
+
chunkData = new IndexedByteArray(size)
|
|
41
|
+
chunkData.set(dataArray.slice(dataArray.currentIndex, dataArray.currentIndex + size))
|
|
42
|
+
}
|
|
43
|
+
if(readData || forceShift)
|
|
44
|
+
{
|
|
45
|
+
dataArray.currentIndex += size;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return new RiffChunk(header, size, chunkData)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param chunk {RiffChunk}
|
|
53
|
+
* @param prepend {IndexedByteArray}
|
|
54
|
+
* @returns {IndexedByteArray}
|
|
55
|
+
*/
|
|
56
|
+
export function writeRIFFChunk(chunk, prepend = undefined)
|
|
57
|
+
{
|
|
58
|
+
let size = 8 + chunk.size;
|
|
59
|
+
if(chunk.size % 2 !== 0)
|
|
60
|
+
{
|
|
61
|
+
size++;
|
|
62
|
+
}
|
|
63
|
+
if(prepend)
|
|
64
|
+
{
|
|
65
|
+
size += prepend.length;
|
|
66
|
+
}
|
|
67
|
+
const array = new IndexedByteArray(size);
|
|
68
|
+
// prepend data (for example type before the read)
|
|
69
|
+
if(prepend)
|
|
70
|
+
{
|
|
71
|
+
array.set(prepend, array.currentIndex);
|
|
72
|
+
array.currentIndex += prepend.length;
|
|
73
|
+
}
|
|
74
|
+
// write header
|
|
75
|
+
writeStringAsBytes(array, chunk.header);
|
|
76
|
+
// write size (excluding header and the size itself) and the prepend if specified
|
|
77
|
+
writeDword(array, size - 8 - (prepend?.length || 0));
|
|
78
|
+
// write data
|
|
79
|
+
array.set(chunk.chunkData, array.currentIndex);
|
|
80
|
+
return array;
|
|
81
|
+
}
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import {RiffChunk} from "./riff_chunk.js";
|
|
2
|
+
import {IndexedByteArray} from "../../utils/indexed_array.js";
|
|
3
|
+
import { readBytesAsUintLittleEndian, signedInt8} from "../../utils/byte_functions/little_endian.js";
|
|
4
|
+
import { stbvorbis } from '../../externals/stbvorbis_sync/stbvorbis_sync.min.js'
|
|
5
|
+
import { SpessaSynthWarn } from '../../utils/loggin.js'
|
|
6
|
+
import { readBytesAsString } from '../../utils/byte_functions/string.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* samples.js
|
|
10
|
+
* purpose: parses soundfont samples, resamples if needed.
|
|
11
|
+
* loads sample data, handles async loading of sf3 compressed samples
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export class BasicSample
|
|
15
|
+
{
|
|
16
|
+
/**
|
|
17
|
+
* The basic representation of a soundfont sample
|
|
18
|
+
* @param sampleName {string}
|
|
19
|
+
* @param sampleRate {number}
|
|
20
|
+
* @param samplePitch {number}
|
|
21
|
+
* @param samplePitchCorrection {number}
|
|
22
|
+
* @param sampleLink {number}
|
|
23
|
+
* @param sampleType {number}
|
|
24
|
+
* @param loopStart {number} relative to sample start
|
|
25
|
+
* @param loopEnd {number} relative to sample start
|
|
26
|
+
*/
|
|
27
|
+
constructor(
|
|
28
|
+
sampleName,
|
|
29
|
+
sampleRate,
|
|
30
|
+
samplePitch,
|
|
31
|
+
samplePitchCorrection,
|
|
32
|
+
sampleLink,
|
|
33
|
+
sampleType,
|
|
34
|
+
loopStart,
|
|
35
|
+
loopEnd
|
|
36
|
+
)
|
|
37
|
+
{
|
|
38
|
+
/**
|
|
39
|
+
* Sample's name
|
|
40
|
+
* @type {string}
|
|
41
|
+
*/
|
|
42
|
+
this.sampleName = sampleName;
|
|
43
|
+
/**
|
|
44
|
+
* Sample rate in Hz
|
|
45
|
+
* @type {number}
|
|
46
|
+
*/
|
|
47
|
+
this.sampleRate = sampleRate;
|
|
48
|
+
/**
|
|
49
|
+
* Original pitch of the sample as a MIDI note number
|
|
50
|
+
* @type {number}
|
|
51
|
+
*/
|
|
52
|
+
this.samplePitch = samplePitch;
|
|
53
|
+
/**
|
|
54
|
+
* Pitch correction, in cents. Can be negative
|
|
55
|
+
* @type {number}
|
|
56
|
+
*/
|
|
57
|
+
this.samplePitchCorrection = samplePitchCorrection;
|
|
58
|
+
/**
|
|
59
|
+
* Sample link, currently unused.
|
|
60
|
+
* @type {number}
|
|
61
|
+
*/
|
|
62
|
+
this.sampleLink = sampleLink;
|
|
63
|
+
/**
|
|
64
|
+
* Type of the sample, an enum
|
|
65
|
+
* @type {number}
|
|
66
|
+
*/
|
|
67
|
+
this.sampleType = sampleType;
|
|
68
|
+
/**
|
|
69
|
+
* Relative to start of the sample, bytes
|
|
70
|
+
* @type {number}
|
|
71
|
+
*/
|
|
72
|
+
this.sampleLoopStartIndex = loopStart;
|
|
73
|
+
/**
|
|
74
|
+
* Relative to start of the sample, in bytes
|
|
75
|
+
* @type {number}
|
|
76
|
+
*/
|
|
77
|
+
this.sampleLoopEndIndex = loopEnd;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Indicates if the sample is compressed
|
|
81
|
+
* @type {boolean}
|
|
82
|
+
*/
|
|
83
|
+
this.isCompressed = (sampleType & 0x10) > 0;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The compressed sample data if it was compressed by spessasynth
|
|
87
|
+
* @type {Uint8Array}
|
|
88
|
+
*/
|
|
89
|
+
this.compressedData = undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @returns {Uint8Array|IndexedByteArray}
|
|
94
|
+
*/
|
|
95
|
+
getRawData()
|
|
96
|
+
{
|
|
97
|
+
const e = new Error("Not implemented");
|
|
98
|
+
e.name = "NotImplementedError";
|
|
99
|
+
throw e;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param quality {number}
|
|
104
|
+
* @param encodeVorbis {EncodeVorbisFunction}
|
|
105
|
+
*/
|
|
106
|
+
compressSample(quality, encodeVorbis)
|
|
107
|
+
{
|
|
108
|
+
// no need to compress
|
|
109
|
+
if(this.isCompressed)
|
|
110
|
+
{
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// compress, always mono!
|
|
114
|
+
try {
|
|
115
|
+
this.compressedData = encodeVorbis([this.getAudioData()], 1, this.sampleRate, quality);
|
|
116
|
+
// flag as compressed
|
|
117
|
+
this.sampleType |= 0x10;
|
|
118
|
+
this.isCompressed = true;
|
|
119
|
+
}
|
|
120
|
+
catch (e)
|
|
121
|
+
{
|
|
122
|
+
SpessaSynthWarn(`Failed to compress ${this.sampleName}. Leaving as uncompressed!`);
|
|
123
|
+
this.isCompressed = false;
|
|
124
|
+
this.compressedData = undefined;
|
|
125
|
+
this.sampleType &= -17;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @returns {Float32Array}
|
|
132
|
+
*/
|
|
133
|
+
getAudioData()
|
|
134
|
+
{
|
|
135
|
+
const e = new Error("Not implemented");
|
|
136
|
+
e.name = "NotImplementedError";
|
|
137
|
+
throw e;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export class Sample extends BasicSample
|
|
142
|
+
{
|
|
143
|
+
/**
|
|
144
|
+
* Creates a sample
|
|
145
|
+
* @param sampleName {string}
|
|
146
|
+
* @param sampleStartIndex {number}
|
|
147
|
+
* @param sampleEndIndex {number}
|
|
148
|
+
* @param sampleLoopStartIndex {number}
|
|
149
|
+
* @param sampleLoopEndIndex {number}
|
|
150
|
+
* @param sampleRate {number}
|
|
151
|
+
* @param samplePitch {number}
|
|
152
|
+
* @param samplePitchCorrection {number}
|
|
153
|
+
* @param sampleLink {number}
|
|
154
|
+
* @param sampleType {number}
|
|
155
|
+
* @param smplArr {IndexedByteArray}
|
|
156
|
+
* @param sampleIndex {number} initial sample index when loading the sfont
|
|
157
|
+
*/
|
|
158
|
+
constructor(sampleName,
|
|
159
|
+
sampleStartIndex,
|
|
160
|
+
sampleEndIndex,
|
|
161
|
+
sampleLoopStartIndex,
|
|
162
|
+
sampleLoopEndIndex,
|
|
163
|
+
sampleRate,
|
|
164
|
+
samplePitch,
|
|
165
|
+
samplePitchCorrection,
|
|
166
|
+
sampleLink,
|
|
167
|
+
sampleType,
|
|
168
|
+
smplArr,
|
|
169
|
+
sampleIndex
|
|
170
|
+
)
|
|
171
|
+
{
|
|
172
|
+
super(
|
|
173
|
+
sampleName,
|
|
174
|
+
sampleRate,
|
|
175
|
+
samplePitch,
|
|
176
|
+
samplePitchCorrection,
|
|
177
|
+
sampleLink,
|
|
178
|
+
sampleType,
|
|
179
|
+
sampleLoopStartIndex - sampleStartIndex,
|
|
180
|
+
sampleLoopEndIndex - sampleStartIndex
|
|
181
|
+
);
|
|
182
|
+
this.sampleName = sampleName
|
|
183
|
+
// in bytes
|
|
184
|
+
this.sampleStartIndex = sampleStartIndex;
|
|
185
|
+
this.sampleEndIndex = sampleEndIndex;
|
|
186
|
+
this.isSampleLoaded = false;
|
|
187
|
+
this.sampleID = sampleIndex;
|
|
188
|
+
this.useCount = 0;
|
|
189
|
+
// in bytes
|
|
190
|
+
this.sampleLength = this.sampleEndIndex - this.sampleStartIndex;
|
|
191
|
+
this.sampleDataArray = smplArr;
|
|
192
|
+
this.sampleData = new Float32Array(0);
|
|
193
|
+
if(this.isCompressed)
|
|
194
|
+
{
|
|
195
|
+
// correct loop points
|
|
196
|
+
this.sampleLoopStartIndex += this.sampleStartIndex;
|
|
197
|
+
this.sampleLoopEndIndex += this.sampleStartIndex;
|
|
198
|
+
this.sampleLength = 99999999; // set to 999999 before we decode it
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get raw data, whether it's compressed or not as we simply write it to the file
|
|
205
|
+
* @return {Uint8Array}
|
|
206
|
+
*/
|
|
207
|
+
getRawData()
|
|
208
|
+
{
|
|
209
|
+
const smplArr = this.sampleDataArray;
|
|
210
|
+
if(this.isCompressed)
|
|
211
|
+
{
|
|
212
|
+
if(this.compressedData)
|
|
213
|
+
{
|
|
214
|
+
return this.compressedData;
|
|
215
|
+
}
|
|
216
|
+
const smplStart = smplArr.currentIndex;
|
|
217
|
+
return smplArr.slice(this.sampleStartIndex / 2 + smplStart, this.sampleEndIndex / 2 + smplStart);
|
|
218
|
+
}
|
|
219
|
+
else
|
|
220
|
+
{
|
|
221
|
+
const dataStartIndex = smplArr.currentIndex;
|
|
222
|
+
return smplArr.slice(dataStartIndex + this.sampleStartIndex, dataStartIndex + this.sampleEndIndex)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Decode binary vorbis into a float32 pcm
|
|
228
|
+
*/
|
|
229
|
+
decodeVorbis()
|
|
230
|
+
{
|
|
231
|
+
if (this.sampleLength < 1)
|
|
232
|
+
{
|
|
233
|
+
// eos, do not do anything
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// get the compressed byte stream
|
|
237
|
+
const smplArr = this.sampleDataArray;
|
|
238
|
+
const smplStart = smplArr.currentIndex;
|
|
239
|
+
const buff = smplArr.slice(this.sampleStartIndex / 2 + smplStart, this.sampleEndIndex / 2 + smplStart);
|
|
240
|
+
// reset array and being decoding
|
|
241
|
+
this.sampleData = new Float32Array(0);
|
|
242
|
+
/**
|
|
243
|
+
* @type {{data: Float32Array[], error: (string|null), sampleRate: number, eof: boolean}}
|
|
244
|
+
*/
|
|
245
|
+
const vorbis = stbvorbis.decode(buff.buffer);
|
|
246
|
+
this.sampleData = vorbis.data[0];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Loads the audio data and stores it for reuse
|
|
251
|
+
* @returns {Float32Array} The audioData
|
|
252
|
+
*/
|
|
253
|
+
getAudioData()
|
|
254
|
+
{
|
|
255
|
+
if (!this.isSampleLoaded)
|
|
256
|
+
{
|
|
257
|
+
// start loading data if not loaded
|
|
258
|
+
return this.loadBufferData();
|
|
259
|
+
}
|
|
260
|
+
return this.sampleData;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* @returns {Float32Array}
|
|
265
|
+
*/
|
|
266
|
+
loadUncompressedData()
|
|
267
|
+
{
|
|
268
|
+
if(this.isCompressed)
|
|
269
|
+
{
|
|
270
|
+
SpessaSynthWarn("Trying to load a compressed sample via loadUncompressedData()... aborting!");
|
|
271
|
+
return new Float32Array(0);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// read the sample data
|
|
275
|
+
let audioData = new Float32Array(this.sampleLength / 2);
|
|
276
|
+
const dataStartIndex = this.sampleDataArray.currentIndex;
|
|
277
|
+
let convertedSigned16 = new Int16Array(
|
|
278
|
+
this.sampleDataArray.slice(dataStartIndex + this.sampleStartIndex, dataStartIndex + this.sampleEndIndex)
|
|
279
|
+
.buffer
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// convert to float
|
|
283
|
+
for(let i = 0; i < convertedSigned16.length; i++)
|
|
284
|
+
{
|
|
285
|
+
audioData[i] = convertedSigned16[i] / 32768;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this.sampleData = audioData;
|
|
289
|
+
this.isSampleLoaded = true;
|
|
290
|
+
return audioData;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @returns {Float32Array}
|
|
295
|
+
*/
|
|
296
|
+
loadBufferData()
|
|
297
|
+
{
|
|
298
|
+
if (this.sampleLength < 1)
|
|
299
|
+
{
|
|
300
|
+
// eos, do not do anything
|
|
301
|
+
return new Float32Array(1);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if(this.isCompressed)
|
|
305
|
+
{
|
|
306
|
+
this.decodeVorbis();
|
|
307
|
+
this.isSampleLoaded = true;
|
|
308
|
+
return this.sampleData;
|
|
309
|
+
}
|
|
310
|
+
return this.loadUncompressedData();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Reads the generatorTranslator from the shdr read
|
|
316
|
+
* @param sampleHeadersChunk {RiffChunk}
|
|
317
|
+
* @param smplChunkData {IndexedByteArray}
|
|
318
|
+
* @returns {Sample[]}
|
|
319
|
+
*/
|
|
320
|
+
export function readSamples(sampleHeadersChunk, smplChunkData)
|
|
321
|
+
{
|
|
322
|
+
/**
|
|
323
|
+
* @type {Sample[]}
|
|
324
|
+
*/
|
|
325
|
+
let samples = [];
|
|
326
|
+
let index = 0;
|
|
327
|
+
while(sampleHeadersChunk.chunkData.length > sampleHeadersChunk.chunkData.currentIndex)
|
|
328
|
+
{
|
|
329
|
+
const sample = readSample(index, sampleHeadersChunk.chunkData, smplChunkData);
|
|
330
|
+
samples.push(sample);
|
|
331
|
+
index++;
|
|
332
|
+
}
|
|
333
|
+
// remove EOS
|
|
334
|
+
if (samples.length > 1)
|
|
335
|
+
{
|
|
336
|
+
samples.pop();
|
|
337
|
+
}
|
|
338
|
+
return samples;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Reads it into a sample
|
|
343
|
+
* @param index {number}
|
|
344
|
+
* @param sampleHeaderData {IndexedByteArray}
|
|
345
|
+
* @param smplArrayData {IndexedByteArray}
|
|
346
|
+
* @returns {Sample}
|
|
347
|
+
*/
|
|
348
|
+
function readSample(index, sampleHeaderData, smplArrayData) {
|
|
349
|
+
|
|
350
|
+
// read the sample name
|
|
351
|
+
let sampleName = readBytesAsString(sampleHeaderData, 20);
|
|
352
|
+
|
|
353
|
+
// read the sample start index
|
|
354
|
+
let sampleStartIndex = readBytesAsUintLittleEndian(sampleHeaderData, 4) * 2;
|
|
355
|
+
|
|
356
|
+
// read the sample end index
|
|
357
|
+
let sampleEndIndex = readBytesAsUintLittleEndian(sampleHeaderData, 4) * 2;
|
|
358
|
+
|
|
359
|
+
// read the sample looping start index
|
|
360
|
+
let sampleLoopStartIndex = readBytesAsUintLittleEndian(sampleHeaderData, 4) * 2;
|
|
361
|
+
|
|
362
|
+
// read the sample looping end index
|
|
363
|
+
let sampleLoopEndIndex = readBytesAsUintLittleEndian(sampleHeaderData, 4) * 2;
|
|
364
|
+
|
|
365
|
+
// read the sample rate
|
|
366
|
+
let sampleRate = readBytesAsUintLittleEndian(sampleHeaderData, 4);
|
|
367
|
+
|
|
368
|
+
// read the original sample pitch
|
|
369
|
+
let samplePitch = sampleHeaderData[sampleHeaderData.currentIndex++];
|
|
370
|
+
if(samplePitch === 255)
|
|
371
|
+
{
|
|
372
|
+
// if it's 255, then default to 60
|
|
373
|
+
samplePitch = 60;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// readt the sample pitch correction
|
|
377
|
+
let samplePitchCorrection = signedInt8(sampleHeaderData[sampleHeaderData.currentIndex++]);
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
// read the link to the other channel
|
|
381
|
+
let sampleLink = readBytesAsUintLittleEndian(sampleHeaderData, 2);
|
|
382
|
+
let sampleType = readBytesAsUintLittleEndian(sampleHeaderData, 2);
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
return new Sample(sampleName,
|
|
387
|
+
sampleStartIndex,
|
|
388
|
+
sampleEndIndex,
|
|
389
|
+
sampleLoopStartIndex,
|
|
390
|
+
sampleLoopEndIndex,
|
|
391
|
+
sampleRate,
|
|
392
|
+
samplePitch,
|
|
393
|
+
samplePitchCorrection,
|
|
394
|
+
sampleLink,
|
|
395
|
+
sampleType,
|
|
396
|
+
smplArrayData,
|
|
397
|
+
index);
|
|
398
|
+
}
|