| import { RiffChunk } from "../basic_soundfont/riff_chunk.js"; |
| import { IndexedByteArray } from "../../utils/indexed_array.js"; |
| import { readLittleEndian, signedInt8 } from "../../utils/byte_functions/little_endian.js"; |
| import { stbvorbis } from "../../externals/stbvorbis_sync/stbvorbis_sync.min.js"; |
| import { SpessaSynthWarn } from "../../utils/loggin.js"; |
| import { readBytesAsString } from "../../utils/byte_functions/string.js"; |
| import { BasicSample } from "../basic_soundfont/basic_sample.js"; |
|
|
| export class SoundFontSample extends BasicSample |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| constructor( |
| sampleName, |
| sampleStartIndex, |
| sampleEndIndex, |
| sampleLoopStartIndex, |
| sampleLoopEndIndex, |
| sampleRate, |
| samplePitch, |
| samplePitchCorrection, |
| sampleLink, |
| sampleType, |
| smplArr, |
| sampleIndex, |
| isDataRaw |
| ) |
| { |
| super( |
| sampleName, |
| sampleRate, |
| samplePitch, |
| samplePitchCorrection, |
| sampleLink, |
| sampleType, |
| sampleLoopStartIndex - (sampleStartIndex / 2), |
| sampleLoopEndIndex - (sampleStartIndex / 2) |
| ); |
| this.sampleName = sampleName; |
| |
| this.sampleStartIndex = sampleStartIndex; |
| this.sampleEndIndex = sampleEndIndex; |
| this.isSampleLoaded = false; |
| this.sampleID = sampleIndex; |
| |
| this.sampleLength = this.sampleEndIndex - this.sampleStartIndex; |
| this.sampleDataArray = smplArr; |
| this.sampleData = new Float32Array(0); |
| if (this.isCompressed) |
| { |
| |
| this.sampleLoopStartIndex += this.sampleStartIndex / 2; |
| this.sampleLoopEndIndex += this.sampleStartIndex / 2; |
| this.sampleLength = 99999999; |
| } |
| this.isDataRaw = isDataRaw; |
| } |
| |
| |
| |
| |
| |
| getRawData() |
| { |
| const smplArr = this.sampleDataArray; |
| if (this.isCompressed) |
| { |
| if (this.compressedData) |
| { |
| return this.compressedData; |
| } |
| const smplStart = smplArr.currentIndex; |
| return smplArr.slice(this.sampleStartIndex / 2 + smplStart, this.sampleEndIndex / 2 + smplStart); |
| } |
| else |
| { |
| if (!this.isDataRaw) |
| { |
| |
| super.getRawData(); |
| } |
| const dataStartIndex = smplArr.currentIndex; |
| return smplArr.slice(dataStartIndex + this.sampleStartIndex, dataStartIndex + this.sampleEndIndex); |
| } |
| } |
| |
| |
| |
| |
| decodeVorbis() |
| { |
| if (this.sampleLength < 1) |
| { |
| |
| return; |
| } |
| |
| const smplArr = this.sampleDataArray; |
| const smplStart = smplArr.currentIndex; |
| const buff = smplArr.slice(this.sampleStartIndex / 2 + smplStart, this.sampleEndIndex / 2 + smplStart); |
| |
| this.sampleData = new Float32Array(0); |
| try |
| { |
| |
| |
| |
| const vorbis = stbvorbis.decode(buff.buffer); |
| this.sampleData = vorbis.data[0]; |
| if (this.sampleData === undefined) |
| { |
| SpessaSynthWarn(`Error decoding sample ${this.sampleName}: Vorbis decode returned undefined.`); |
| } |
| } |
| catch (e) |
| { |
| |
| SpessaSynthWarn(`Error decoding sample ${this.sampleName}: ${e}`); |
| this.sampleData = new Float32Array(this.sampleLoopEndIndex + 1); |
| } |
| } |
| |
| |
| |
| |
| |
| getAudioData() |
| { |
| if (!this.isSampleLoaded) |
| { |
| |
| if (this.sampleLength < 1) |
| { |
| SpessaSynthWarn(`Invalid sample ${this.sampleName}! Invalid length: ${this.sampleLength}`); |
| return new Float32Array(1); |
| } |
| |
| if (this.isCompressed) |
| { |
| |
| this.decodeVorbis(); |
| this.isSampleLoaded = true; |
| return this.sampleData; |
| } |
| else if (!this.isDataRaw) |
| { |
| return this.getUncompressedReadyData(); |
| } |
| return this.loadUncompressedData(); |
| } |
| return this.sampleData; |
| } |
| |
| |
| |
| |
| loadUncompressedData() |
| { |
| if (this.isCompressed) |
| { |
| SpessaSynthWarn("Trying to load a compressed sample via loadUncompressedData()... aborting!"); |
| return new Float32Array(0); |
| } |
| |
| |
| let audioData = new Float32Array(this.sampleLength / 2); |
| const dataStartIndex = this.sampleDataArray.currentIndex; |
| let convertedSigned16 = new Int16Array( |
| this.sampleDataArray.slice(dataStartIndex + this.sampleStartIndex, dataStartIndex + this.sampleEndIndex) |
| .buffer |
| ); |
| |
| |
| for (let i = 0; i < convertedSigned16.length; i++) |
| { |
| audioData[i] = convertedSigned16[i] / 32768; |
| } |
| |
| this.sampleData = audioData; |
| this.isSampleLoaded = true; |
| return audioData; |
| } |
| |
| |
| |
| |
| getUncompressedReadyData() |
| { |
| |
| |
| |
| |
| let audioData = this.sampleDataArray.slice(this.sampleStartIndex / 2, this.sampleEndIndex / 2); |
| this.sampleData = audioData; |
| this.isSampleLoaded = true; |
| return audioData; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function readSamples(sampleHeadersChunk, smplChunkData, isSmplDataRaw = true) |
| { |
| |
| |
| |
| let samples = []; |
| let index = 0; |
| while (sampleHeadersChunk.chunkData.length > sampleHeadersChunk.chunkData.currentIndex) |
| { |
| const sample = readSample(index, sampleHeadersChunk.chunkData, smplChunkData, isSmplDataRaw); |
| samples.push(sample); |
| index++; |
| } |
| |
| if (samples.length > 1) |
| { |
| samples.pop(); |
| } |
| return samples; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| function readSample(index, sampleHeaderData, smplArrayData, isDataRaw) |
| { |
| |
| |
| let sampleName = readBytesAsString(sampleHeaderData, 20); |
| |
| |
| let sampleStartIndex = readLittleEndian(sampleHeaderData, 4) * 2; |
| |
| |
| let sampleEndIndex = readLittleEndian(sampleHeaderData, 4) * 2; |
| |
| |
| let sampleLoopStartIndex = readLittleEndian(sampleHeaderData, 4); |
| |
| |
| let sampleLoopEndIndex = readLittleEndian(sampleHeaderData, 4); |
| |
| |
| let sampleRate = readLittleEndian(sampleHeaderData, 4); |
| |
| |
| let samplePitch = sampleHeaderData[sampleHeaderData.currentIndex++]; |
| if (samplePitch === 255) |
| { |
| |
| samplePitch = 60; |
| } |
| |
| |
| let samplePitchCorrection = signedInt8(sampleHeaderData[sampleHeaderData.currentIndex++]); |
| |
| |
| |
| let sampleLink = readLittleEndian(sampleHeaderData, 2); |
| let sampleType = readLittleEndian(sampleHeaderData, 2); |
| |
| |
| return new SoundFontSample( |
| sampleName, |
| sampleStartIndex, |
| sampleEndIndex, |
| sampleLoopStartIndex, |
| sampleLoopEndIndex, |
| sampleRate, |
| samplePitch, |
| samplePitchCorrection, |
| sampleLink, |
| sampleType, |
| smplArrayData, |
| index, |
| isDataRaw |
| ); |
| } |