| 176 | } |
| 177 | |
| 178 | async function _fetchPcm(stem, chunkIdx) { |
| 179 | const { url, header } = stem; |
| 180 | const { dataOffset, dataSize, bytesPerFrame, sampleRate } = header; |
| 181 | const chunkBytes = Math.floor(CHUNK_SEC * sampleRate) * bytesPerFrame; |
| 182 | const byteStart = dataOffset + chunkIdx * chunkBytes; |
| 183 | if (byteStart >= dataOffset + dataSize) return null; // past end of file |
| 184 | const byteEnd = Math.min(byteStart + chunkBytes, dataOffset + dataSize) - 1; |
| 185 | const res = await fetch(url, { headers: { Range: `bytes=${byteStart}-${byteEnd}` } }); |
| 186 | if (!res.ok && res.status !== 206) throw new Error(`Range fetch ${res.status}`); |
| 187 | return res.arrayBuffer(); |
| 188 | } |
| 189 | |
| 190 | // Returns a Promise<Map<name, AudioBuffer>>. Deduplicates: if a fetch for |
| 191 | // chunkIdx is already in flight, returns the same promise. |