Skip to content

Commit 26e9d29

Browse files
committed
stuff
1 parent 0be82fb commit 26e9d29

12 files changed

Lines changed: 2944 additions & 184 deletions

File tree

Build/src/index.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,13 @@ <h2>Playlists</h2>
3838
<ul id="tracks"></ul>
3939
</div>
4040
<!-- Visualizer -->
41-
<div id="visualizerContainer" style="width: 100%; height: 128px; background: #000;">
41+
<!-- <div id="visualizerContainer" style="width: 100%; height: 128px; background: #000;">
4242
<div id="visualizer" style="width: 100%; height: 100%;"></div>
43+
</div> -->
44+
<div class="visualizer-section">
45+
<div id="visualizer-container" style="width: 100%; height: 128px; background: #000;"></div>
4346
</div>
47+
4448
<!-- Grouped Player Controls -->
4549
<div id="playerControls">
4650
<!-- Current Track Art -->

Build/src/main.ts

Lines changed: 172 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,215 @@
11
import { create } from 'zustand';
22
import { initPlayer } from './player';
33
import { initSettings } from './settings';
4-
import { initVisualizer, VisualizerSettings, VisualizerType } from './visualizer';
54
import { initPlaylists } from './playlists';
65
import { initTracks } from './tracks';
76
import { initUI } from './ui';
8-
9-
interface VisualizerInstance {
10-
setVisualizerType: (type: VisualizerType) => void;
11-
updateSettings: (settings: Partial<VisualizerSettings>) => void;
12-
}
7+
import { VisualizerManager } from './visualizerManager';
138

149
export interface AppState {
1510
currentTrack: string | null;
1611
isPlaying: boolean;
1712
theme: string;
18-
visualizer: VisualizerInstance | null;
1913
setCurrentTrack: (track: string | null) => void;
2014
setIsPlaying: (playing: boolean) => void;
2115
setTheme: (theme: string) => void;
22-
setVisualizer: (visualizer: VisualizerInstance) => void;
2316
}
2417

2518
const useStore = create<AppState>((set) => ({
2619
currentTrack: null,
2720
isPlaying: false,
2821
theme: 'default',
29-
visualizer: null,
3022
setCurrentTrack: (track) => set({ currentTrack: track }),
3123
setIsPlaying: (playing) => set({ isPlaying: playing }),
3224
setTheme: (theme) => set({ theme }),
33-
setVisualizer: (visualizer) => set({ visualizer }),
3425
}));
3526

36-
async function initApp() {
37-
// Initialize visualizer first
38-
const visualizerInstance = initVisualizer(useStore);
39-
if (visualizerInstance) {
40-
useStore.getState().setVisualizer({
41-
setVisualizerType: visualizerInstance.setVisualizerType,
42-
updateSettings: visualizerInstance.updateSettings
43-
});
27+
// Enhanced visualizer wrapper to work with Howl.js
28+
class HowlVisualizerAdapter {
29+
private visualizerManager: any = null; // VisualizerManager instance
30+
private howlInstance: any = null;
31+
private audioContext: AudioContext | null = null;
32+
private analyser: AnalyserNode | null = null;
33+
private source: MediaElementAudioSourceNode | null = null;
34+
35+
constructor(canvasContainer: HTMLElement) {
36+
// We'll initialize the visualizer manager when we get a Howl instance
37+
this.setupCanvasContainer(canvasContainer);
4438
}
45-
46-
// Initialize other components
39+
40+
private setupCanvasContainer(container: HTMLElement) {
41+
// Ensure container has proper styling for visualizer
42+
if (!container.style.position) {
43+
container.style.position = 'relative';
44+
}
45+
}
46+
47+
public setHowlInstance(howl: any) {
48+
this.howlInstance = howl;
49+
this.setupVisualizerConnection();
50+
}
51+
52+
public setupVisualizerConnection() {
53+
if (!this.howlInstance) return;
54+
55+
try {
56+
// Get the underlying HTML audio element from Howl
57+
const audioElement = this.getAudioElementFromHowl();
58+
59+
if (audioElement && !this.visualizerManager) {
60+
const canvasContainer = document.getElementById('visualizer-container');
61+
if (canvasContainer) {
62+
// Now we can create the VisualizerManager with the audio element
63+
this.visualizerManager = new VisualizerManager(audioElement, canvasContainer);
64+
console.log('Visualizer manager would be created here with audio element');
65+
66+
// For now, let's set up Web Audio API connection manually
67+
// this.setupWebAudioConnection(audioElement);
68+
}
69+
}
70+
} catch (error) {
71+
console.error('Error setting up visualizer connection:', error);
72+
}
73+
}
74+
75+
private getAudioElementFromHowl(): HTMLAudioElement | null {
76+
try {
77+
// Access the internal HTML audio element from Howl
78+
if (this.howlInstance && this.howlInstance._sounds && this.howlInstance._sounds[0]) {
79+
return this.howlInstance._sounds[0]._node;
80+
}
81+
} catch (error) {
82+
console.error('Error accessing audio element from Howl:', error);
83+
}
84+
return null;
85+
}
86+
87+
private setupWebAudioConnection(audioElement: HTMLAudioElement) {
88+
try {
89+
// Use Howler's existing audio context if available
90+
this.audioContext = (window as any).Howler?.ctx || new (window.AudioContext || (window as any).webkitAudioContext)();
91+
92+
if (!this.analyser) {
93+
if (!this.audioContext) {
94+
console.error('AudioContext is not available');
95+
return;
96+
}
97+
this.analyser = this.audioContext.createAnalyser();
98+
this.analyser.fftSize = 2048;
99+
100+
// Create source from audio element
101+
this.source = this.audioContext.createMediaElementSource(audioElement);
102+
this.source.connect(this.analyser);
103+
this.analyser.connect(this.audioContext.destination);
104+
105+
console.log('Web Audio API connection established for visualizer');
106+
}
107+
} catch (error) {
108+
console.error('Error setting up Web Audio API:', error);
109+
}
110+
}
111+
112+
public setVisualizerType(type: string) {
113+
if (this.visualizerManager) {
114+
this.visualizerManager.addVisualizer(type);
115+
} else {
116+
console.warn('Visualizer manager not initialized yet');
117+
}
118+
}
119+
120+
public updateSettings(settings: any) {
121+
// Handle visualizer settings updates
122+
console.log('Updating visualizer settings:', settings);
123+
}
124+
125+
public getAnalyserNode(): AnalyserNode | null {
126+
return this.analyser;
127+
}
128+
129+
public getAudioContext(): AudioContext | null {
130+
return this.audioContext;
131+
}
132+
133+
public destroy() {
134+
if (this.visualizerManager) {
135+
// Clean up visualizer manager
136+
this.visualizerManager = null;
137+
}
138+
139+
if (this.source) {
140+
this.source.disconnect();
141+
this.source = null;
142+
}
143+
144+
if (this.analyser) {
145+
this.analyser.disconnect();
146+
this.analyser = null;
147+
}
148+
}
149+
}
150+
151+
async function initApp() {
152+
// Initialize other components first
47153
initUI(useStore);
48154
initSettings(useStore);
49155
initPlaylists(useStore);
50156
initTracks(useStore);
51157

52-
// Initialize player after visualizer
158+
// Initialize player
53159
const playerInstance = initPlayer(useStore);
54160

55-
// Connect player and visualizer if both exist
56-
if (playerInstance && visualizerInstance && typeof playerInstance.setVisualizerInstance === 'function') {
57-
playerInstance.setVisualizerInstance(visualizerInstance);
161+
// Initialize visualizer adapter
162+
const visualizerContainer = document.getElementById('visualizer-container');
163+
if (visualizerContainer && playerInstance) {
164+
const visualizerAdapter = new HowlVisualizerAdapter(visualizerContainer);
165+
166+
// Connect the visualizer adapter to the player
167+
if (typeof playerInstance.setVisualizerInstance === 'function') {
168+
playerInstance.setVisualizerInstance(visualizerAdapter);
169+
console.log('Visualizer adapter connected to audio player');
170+
}
171+
172+
// Set up visualizer controls
173+
setupVisualizerControls(visualizerAdapter);
58174
}
59175

60176
setupAddMusicButton();
61177

178+
// Keyboard controls
62179
document.addEventListener('keydown', (e) => {
63-
if (e.code === 'Space') {
180+
if (e.code === 'Space' && e.target === document.body) {
64181
e.preventDefault();
65-
const store = useStore.getState();
66-
store.setIsPlaying(!store.isPlaying);
182+
const playPauseBtn = document.getElementById('playPauseBtn');
183+
if (playPauseBtn) {
184+
playPauseBtn.click();
185+
}
67186
}
68187
});
188+
189+
// Handle window resize for visualizer
190+
window.addEventListener('resize', () => {
191+
// If you have a visualizer manager, call its resize method
192+
// visualizerManager?.resizeCanvas();
193+
});
194+
}
195+
196+
function setupVisualizerControls(visualizerAdapter: HowlVisualizerAdapter) {
197+
// Example: Add buttons to switch visualizer types
198+
const visualizerButtons = document.querySelectorAll('[data-visualizer-type]');
199+
200+
visualizerButtons.forEach(button => {
201+
button.addEventListener('click', (e) => {
202+
const target = e.target as HTMLElement;
203+
const type = target.dataset.visualizerType;
204+
if (type) {
205+
visualizerAdapter.setVisualizerType(type);
206+
207+
// Update button states
208+
visualizerButtons.forEach(btn => btn.classList.remove('active'));
209+
target.classList.add('active');
210+
}
211+
});
212+
});
69213
}
70214

71215
// Directory picker + file input fallback for Add Music
@@ -78,26 +222,20 @@ function setupAddMusicButton() {
78222
uploadBtn.addEventListener('click', async () => {
79223
if ('showDirectoryPicker' in window) {
80224
try {
81-
// @ts-ignore
82225
const dirHandle = await (window as any).showDirectoryPicker();
83-
// Dispatch a custom event or call a handler to process the directory
84226
const event = new CustomEvent('music-directory-selected', { detail: dirHandle });
85227
window.dispatchEvent(event);
86228
} catch (e) {
87-
// User cancelled or error, fallback to file input
88229
fileInput.click();
89230
}
90231
} else {
91232
console.warn('Directory picker not supported, falling back to file input');
92-
// Fallback for browsers that don't support directory picker
93-
// Ensure file input accepts multiple files and audio types
94233
fileInput.setAttribute('multiple', 'true');
95234
fileInput.setAttribute('accept', 'audio/*');
96-
// Trigger file input click
97235
fileInput.click();
98236
}
99237
});
100238
}
101239

102-
// Call this in initApp
240+
// Initialize the app
103241
initApp();

Build/src/player.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import {
1313
declare global {
1414
interface Window {
1515
Howl: any;
16-
Howler: any;
16+
Howler?: any | {
17+
ctx?: AudioContext;
18+
};
1719
}
1820
}
1921

@@ -463,6 +465,13 @@ class AudioPlayer {
463465
return this.howl;
464466
}
465467

468+
public getAudioElement() {
469+
if (this.howl && this.howl._sounds && this.howl._sounds[0]) {
470+
return this.howl._sounds[0]._node; // Returns the HTML audio element
471+
}
472+
return null;
473+
}
474+
466475
// Public method to cleanup when component unmounts
467476
destroy() {
468477
if (this.unsubscribe) {

0 commit comments

Comments
 (0)