File size: 3,720 Bytes
d319f40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
801f0a7
d319f40
 
 
 
801f0a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d319f40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

// Utility to handle Base64 conversion
export const blobToBase64 = (blob: Blob): Promise<string> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onloadend = () => {
      if (typeof reader.result === 'string') {
        // Remove data URL prefix
        const base64 = reader.result.split(',')[1];
        resolve(base64);
      } else {
        reject(new Error('Failed to convert blob to base64'));
      }
    };
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
};

// Optimized Image Compression Utility
export const compressImage = (file: File, maxWidth = 1600, quality = 0.7): Promise<string> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = (event) => {
      const img = new Image();
      img.src = event.target?.result as string;
      img.onload = () => {
        const canvas = document.createElement('canvas');
        let width = img.width;
        let height = img.height;

        if (width > maxWidth) {
          height = Math.round((height * maxWidth) / width);
          width = maxWidth;
        }

        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        if (!ctx) {
            reject(new Error('Canvas context failed'));
            return;
        }
        ctx.drawImage(img, 0, 0, width, height);
        
        // Output as JPEG with quality reduction
        const dataUrl = canvas.toDataURL('image/jpeg', quality);
        // Remove prefix to get raw base64
        const base64 = dataUrl.split(',')[1];
        resolve(base64);
      };
      img.onerror = (err) => reject(err);
    };
    reader.onerror = (err) => reject(err);
  });
};

// --- RAW PCM Decoding Logic ---
export const base64ToUint8Array = (base64: string) => {
  const binaryString = window.atob(base64);
  const len = binaryString.length;
  const bytes = new Uint8Array(len);
  for (let i = 0; i < len; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes;
};

// Gemini TTS typically returns 24000Hz, single channel, Int16 PCM
export const decodePCM = (data: Uint8Array, ctx: AudioContext) => {
    const sampleRate = 24000;
    const int16Data = new Int16Array(data.buffer);
    const float32Data = new Float32Array(int16Data.length);
    
    // Convert Int16 to Float32 [-1.0, 1.0]
    for (let i = 0; i < int16Data.length; i++) {
        float32Data[i] = int16Data[i] / 32768.0;
    }

    const buffer = ctx.createBuffer(1, float32Data.length, sampleRate);
    buffer.copyToChannel(float32Data, 0);
    return buffer;
};

export const cleanTextForTTS = (text: string) => {
    let clean = text
        // 1. Remove Markdown headers, bold, italic markers, and blockquotes
        .replace(/([#*`~>_])/g, '') 
        // 2. Remove links [text](url) -> text
        .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
        // 4. Remove common Kaomoji patterns (Moved up)
        .replace(/\([^\u4e00-\u9fa5A-Za-z,\.,。]{1,8}\)/g, '');

    // 3. Remove Emojis (Safe way for older Safari)
    try {
        // Use constructor to avoid parse-time SyntaxError if unsupported
        // 'v' flag is better but 'u' is more supported. 
        const emojiRegex = new RegExp('\\p{Extended_Pictographic}', 'gu');
        clean = clean.replace(emojiRegex, '');
    } catch (e) {
        // Fallback for older browsers (approximate ranges)
        // This is a basic range for common emojis
        clean = clean.replace(/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}]/gu, '');
    }

    // 5. Consolidate whitespace
    return clean.replace(/\s+/g, ' ').trim();
};