Files
homeclaw/web-ui/audiomass/wav.js
T
kimandClaude Opus 5 e85ebe6608 v4.3.20: 미커밋 자산 일괄 정리 + 유저 워크스페이스/세션토큰 gitignore
그동안 쌓여 있던 미커밋 파일들을 정리. 소스·자산만 올리고, 개인 데이터와
런타임 상태는 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>
2026-07-29 14:55:37 +09:00

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 );
}