File size: 5,907 Bytes
100a6dd
 
 
d086f83
100a6dd
 
 
 
 
 
 
d086f83
 
100a6dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d086f83
 
100a6dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d086f83
 
 
 
100a6dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Sound service for chess UI

// Define sound types
type SoundType = 'move' | 'capture' | 'check' | 'castle' | 'game-end' | 'promote';

// Map of sound files
const SOUND_FILES: Record<SoundType, string> = {
  'move': '/assets/sounds/move.mp3',
  'capture': '/assets/sounds/capture.mp3',
  'check': '/assets/sounds/check.mp3',
  'castle': '/assets/sounds/castle.mp3',
  'game-end': '/assets/sounds/game-end.mp3',
  'promote': '/assets/sounds/move.mp3'  // Reusing move sound for promote
};

// Audio objects for preloading
const audioCache: Record<SoundType, HTMLAudioElement> = {} as Record<SoundType, HTMLAudioElement>;

// Sound enabled flag (can be toggled by user)
let soundEnabled = true;

// Create fallback sounds using the Web Audio API if sound files are not available
const audioContext = typeof AudioContext !== 'undefined' ? new AudioContext() : null;

// Function to create a simple beep sound
const createBeepSound = (frequency: number, duration: number): AudioBuffer | null => {
  if (!audioContext) return null;
  
  const sampleRate = audioContext.sampleRate;
  const buffer = audioContext.createBuffer(1, sampleRate * duration, sampleRate);
  const data = buffer.getChannelData(0);
  
  for (let i = 0; i < buffer.length; i++) {
    data[i] = Math.sin(2 * Math.PI * frequency * i / sampleRate) * 
              (i < buffer.length * 0.1 ? i / (buffer.length * 0.1) : 
               i > buffer.length * 0.9 ? (buffer.length - i) / (buffer.length * 0.1) : 1);
  }
  
  return buffer;
};

// Create fallback sounds
const fallbackSounds: Record<SoundType, AudioBuffer | null> = {
  'move': audioContext ? createBeepSound(440, 0.1) : null,  // A4 note
  'capture': audioContext ? createBeepSound(330, 0.2) : null, // E4 note
  'check': audioContext ? createBeepSound(587, 0.3) : null,  // D5 note
  'castle': audioContext ? createBeepSound(261, 0.3) : null, // C4 note
  'game-end': audioContext ? createBeepSound(196, 0.5) : null, // G3 note
  'promote': audioContext ? createBeepSound(523, 0.2) : null  // C5 note
};

/**

 * Preload all sound files

 */
export const preloadSounds = (): void => {
  Object.entries(SOUND_FILES).forEach(([key, file]) => {
    try {
      const audio = new Audio();
      audio.src = file;
      audio.preload = 'auto';
      audioCache[key as SoundType] = audio;
    } catch (error) {
      console.error(`Failed to preload sound: ${file}`, error);
    }
  });
};

/**

 * Play a sound effect

 * @param type The type of sound to play

 */
export const playSound = (type: SoundType): void => {
  if (!soundEnabled) return;
  
  try {
    // Try to play the audio file
    const audio = audioCache[type];
    if (audio) {
      audio.currentTime = 0;
      audio.play().catch(err => {
        console.warn('Error playing sound file, using fallback:', err);
        playFallbackSound(type);
      });
    } else {
      // Try to create a new audio element
      const newAudio = new Audio(SOUND_FILES[type]);
      newAudio.play().catch(err => {
        console.warn('Error playing sound file, using fallback:', err);
        playFallbackSound(type);
      });
    }
  } catch (error) {
    console.error(`Failed to play sound: ${type}`, error);
    playFallbackSound(type);
  }
};

/**

 * Play a fallback sound using Web Audio API

 * @param type The type of sound to play

 */
const playFallbackSound = (type: SoundType): void => {
  if (!audioContext) return;
  
  try {
    const buffer = fallbackSounds[type];
    if (buffer) {
      const source = audioContext.createBufferSource();
      source.buffer = buffer;
      source.connect(audioContext.destination);
      source.start();
    } else {
      // Create a simple beep as last resort
      const oscillator = audioContext.createOscillator();
      const gainNode = audioContext.createGain();
      
      oscillator.connect(gainNode);
      gainNode.connect(audioContext.destination);
      
      // Set properties based on sound type
      switch (type) {
        case 'move':
          oscillator.frequency.value = 440; // A4
          gainNode.gain.value = 0.1;
          break;
        case 'capture':
          oscillator.frequency.value = 330; // E4
          gainNode.gain.value = 0.2;
          break;
        case 'check':
          oscillator.frequency.value = 587; // D5
          gainNode.gain.value = 0.3;
          break;
        case 'castle':
          oscillator.frequency.value = 261; // C4
          gainNode.gain.value = 0.2;
          break;
        case 'game-end':
          oscillator.frequency.value = 196; // G3
          gainNode.gain.value = 0.3;
          break;
        case 'promote':
          oscillator.frequency.value = 523; // C5
          gainNode.gain.value = 0.2;
          break;
      }
      
      // Start and stop the oscillator
      oscillator.start();
      oscillator.stop(audioContext.currentTime + 0.2);
    }
  } catch (error) {
    console.error('Failed to play fallback sound:', error);
  }
};

/**

 * Play a sound if sound is enabled

 * @param type The type of sound to play

 */
export const playSoundIfEnabled = (type: SoundType): void => {
  if (soundEnabled) {
    playSound(type);
  }
};

/**

 * Toggle sound on/off

 * @param enabled Whether sound should be enabled

 */
export const toggleSound = (enabled?: boolean): boolean => {
  if (enabled !== undefined) {
    soundEnabled = enabled;
  } else {
    soundEnabled = !soundEnabled;
  }
  return soundEnabled;
};

/**

 * Set sound enabled state

 * @param enabled Whether sound should be enabled

 */
export const setSoundEnabled = (enabled: boolean): void => {
  soundEnabled = enabled;
};

/**

 * Check if sound is enabled

 */
export const isSoundEnabled = (): boolean => {
  return soundEnabled;
};