그동안 쌓여 있던 미커밋 파일들을 정리. 소스·자산만 올리고, 개인 데이터와 런타임 상태는 gitignore로 차단. 추가된 자산: - web-ui/audiomass, web-ui/wavacity — 자체 호스팅 오디오 에디터 - web-ui/drums — 드럼 샘플 - wol-gate — WOL 프록시 컨테이너(Dockerfile + server.js) - glm-1m-fix — GLM 1M 컨텍스트 설치 스크립트(linux/windows) - RENODE_FIXES_SUMMARY*.md, CCTV정보_서울특별시.csv, .claude/plans 수정: - edge_tts_synth.py: rate 인자 추가(말하기 속도 조절) - .smallclaw/config.json: google(gemini) provider 등록 — 키는 vault 참조 - assets/SmallClaw*.png 삭제분 반영 gitignore (중요): - .smallclaw/users/*/workspace/ 전체 차단. 기존 패턴이 memory/uploads와 일부 확장자만 막아서 detective/(고소장·통화녹취·의사소견서·가족관계증명서), generated-media/, music/, pptx/ 등 앱 생성 개인 데이터 403MB가 노출돼 있었음 - .smallclaw/active-sessions.json 차단 — 살아있는 bearer 토큰과 admin 역할이 평문으로 들어있어 커밋 시 게이트웨이 관리자 자격증명이 그대로 공개됨 - google-usage.json, config.json.bak-*, workspace/memory·weather-maps· task_result_*, voice/ 등 런타임 상태도 함께 차단 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
119 lines
3.2 KiB
JavaScript
119 lines
3.2 KiB
JavaScript
function interleave(L, R, Arr) {
|
|
var len = L.length + R.length;
|
|
var out = new Arr(len);
|
|
var i = 0, j = 0, n = L.length;
|
|
while (j < n) {
|
|
out[i++] = L[j];
|
|
out[i++] = R[j];
|
|
++j;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function writeString(view, offset, string) {
|
|
for (var i = 0; i < string.length; i++) {
|
|
view.setUint8(offset + i, string.charCodeAt(i));
|
|
}
|
|
}
|
|
|
|
function encodeWAV(samples, numChannels, sampleRate, bitDepth) {
|
|
var bytesPerSample = bitDepth / 8;
|
|
var dataLen = samples.length * bytesPerSample;
|
|
var isFloat = bitDepth === 32;
|
|
var fmtSize = isFloat ? 18 : 16;
|
|
var factSize = isFloat ? 12 : 0;
|
|
var totalSize = 12 + 8 + fmtSize + factSize + 8 + dataLen;
|
|
var buffer = new ArrayBuffer(totalSize);
|
|
var view = new DataView(buffer);
|
|
|
|
writeString(view, 0, 'RIFF');
|
|
view.setUint32(4, totalSize - 8, true);
|
|
writeString(view, 8, 'WAVE');
|
|
|
|
var p = 12;
|
|
writeString(view, p, 'fmt ');
|
|
view.setUint32(p + 4, fmtSize, true);
|
|
view.setUint16(p + 8, isFloat ? 3 : 1, true);
|
|
view.setUint16(p + 10, numChannels, true);
|
|
view.setUint32(p + 12, sampleRate, true);
|
|
view.setUint32(p + 16, sampleRate * numChannels * bytesPerSample, true);
|
|
view.setUint16(p + 20, numChannels * bytesPerSample, true);
|
|
view.setUint16(p + 22, bitDepth, true);
|
|
if (isFloat) view.setUint16(p + 24, 0, true);
|
|
p += 8 + fmtSize;
|
|
|
|
if (isFloat) {
|
|
writeString(view, p, 'fact');
|
|
view.setUint32(p + 4, 4, true);
|
|
view.setUint32(p + 8, samples.length / numChannels, true);
|
|
p += 12;
|
|
}
|
|
|
|
writeString(view, p, 'data');
|
|
view.setUint32(p + 4, dataLen, true);
|
|
p += 8;
|
|
|
|
if (bitDepth === 16) {
|
|
for (var i = 0; i < samples.length; ++i, p += 2)
|
|
view.setInt16(p, samples[i], true);
|
|
} else if (bitDepth === 24) {
|
|
for (var i = 0; i < samples.length; ++i, p += 3) {
|
|
var v = samples[i];
|
|
view.setUint8(p, v & 0xff);
|
|
view.setUint8(p + 1, (v >> 8) & 0xff);
|
|
view.setUint8(p + 2, (v >> 16) & 0xff);
|
|
}
|
|
} else {
|
|
for (var i = 0; i < samples.length; ++i, p += 4)
|
|
view.setFloat32(p, samples[i], true);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
var sample_rate = 44100;
|
|
var kbps = 128;
|
|
var channels = 1;
|
|
var bit_depth = 16;
|
|
|
|
var left_buf = null;
|
|
var right_buf = null;
|
|
var first_buffer = true;
|
|
|
|
onmessage = function( ev ) {
|
|
if (!ev.data) return ;
|
|
|
|
if (ev.data.sample_rate) {
|
|
sample_rate = ev.data.sample_rate / 1;
|
|
kbps = ev.data.kbps / 1;
|
|
channels = ev.data.channels / 1;
|
|
bit_depth = (ev.data.bit_depth / 1) || 16;
|
|
|
|
return ;
|
|
}
|
|
|
|
if (first_buffer) {
|
|
left_buf = ev.data;
|
|
first_buffer = false;
|
|
|
|
if (channels > 1) return ;
|
|
}
|
|
else if (channels > 1) {
|
|
right_buf = ev.data;
|
|
}
|
|
|
|
var Arr = bit_depth === 32 ? Float32Array :
|
|
bit_depth === 24 ? Int32Array :
|
|
Int16Array;
|
|
|
|
var L = new Arr (left_buf);
|
|
var R = right_buf ? new Arr (right_buf) : null;
|
|
var interleaved = R ? interleave (L, R, Arr) : L;
|
|
|
|
var encoded = encodeWAV(interleaved, channels, sample_rate, bit_depth);
|
|
var audioBlob = new Blob([encoded], { type: 'audio/wav' });
|
|
|
|
postMessage( audioBlob );
|
|
}
|