Skip to main content
Glama

MCP MIDI Bridge

by tezza1971
App.tsx9.28 kB
import React, { useState, useEffect } from 'react'; import { Tabs, TabsContent, TabsList, TabsTrigger } from './components/ui/Tabs'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './components/ui/Card'; import { Button } from './components/ui/Button'; import StatusBar from './components/StatusBar'; import SongDisplay from './components/SongDisplay'; import MidiImport from './components/MidiImport'; import JsonInput from './components/JsonInput'; import SettingsModal from './components/SettingsModal'; import AboutModal from './components/AboutModal'; import { NoteSequence, AppConfig, SongInfo } from './types'; import { Play, Square, Settings, Info, Music, Upload, FileText, List } from 'lucide-react'; function App() { const [currentSong, setCurrentSong] = useState<NoteSequence | null>(null); const [isPlaying, setIsPlaying] = useState(false); const [showSettings, setShowSettings] = useState(false); const [showAbout, setShowAbout] = useState(false); const [config, setConfig] = useState<AppConfig>({ mcpPort: 8002, pythonPort: 5000, lastUsedSong: null }); const [songList, setSongList] = useState<SongInfo[]>([]); const [isServerRunning, setIsServerRunning] = useState(false); useEffect(() => { // Load initial data when component mounts loadSongList(); loadLatestSong(); checkServerStatus(); }, []); const loadSongList = async () => { try { const songs = await window.electronAPI.getSongList(); setSongList(songs); } catch (error) { console.error('Failed to load song list:', error); } }; const loadLatestSong = async () => { try { const song = await window.electronAPI.getLatestSong(); if (song) { setCurrentSong(song); } } catch (error) { console.error('Failed to load latest song:', error); } }; const checkServerStatus = async () => { try { const status = await window.electronAPI.getMcpStatus(); setIsServerRunning(status.running); } catch (error) { console.error('Failed to check server status:', error); } }; const handlePlaySong = async () => { if (!currentSong) return; setIsPlaying(true); try { await window.electronAPI.playMidi(currentSong); } catch (error) { console.error('Failed to play song:', error); } finally { setIsPlaying(false); } }; const handleStopSong = async () => { try { await window.electronAPI.stopMidi(); setIsPlaying(false); } catch (error) { console.error('Failed to stop song:', error); } }; const handleSongSelection = (song: SongInfo) => { // Load the selected song console.log('Loading song:', song); }; const handleNoteSequenceLoad = (noteSequence: NoteSequence) => { setCurrentSong(noteSequence); }; const handleConfigChange = async (newConfig: Partial<AppConfig>) => { try { await window.electronAPI.updateConfig(newConfig); setConfig(prev => ({ ...prev, ...newConfig })); } catch (error) { console.error('Failed to update config:', error); } }; const handleMidiImport = (filePath: string) => { console.log('Importing MIDI file:', filePath); // TODO: Implement MIDI file import }; return ( <div className="min-h-screen bg-background"> <div className="container mx-auto p-6 space-y-6"> {/* Header */} <div className="flex items-center justify-between"> <div> <h1 className="text-3xl font-bold tracking-tight">MCP MIDI Bridge</h1> <p className="text-muted-foreground"> Bridge between LLM music generation and DAWs via virtual MIDI </p> </div> <div className="flex items-center gap-2"> <Button variant="outline" size="icon" onClick={() => setShowSettings(true)}> <Settings className="h-4 w-4" /> </Button> <Button variant="outline" size="icon" onClick={() => setShowAbout(true)}> <Info className="h-4 w-4" /> </Button> </div> </div> {/* Status Bar */} <StatusBar apiStatus={{ running: isServerRunning, port: config.mcpPort, }} midiOutputs={[]} /> {/* Main Content */} <Tabs defaultValue="player" className="space-y-6"> <TabsList className="grid w-full grid-cols-4"> <TabsTrigger value="player" className="flex items-center gap-2"> <Music className="h-4 w-4" /> Player </TabsTrigger> <TabsTrigger value="json-input" className="flex items-center gap-2"> <FileText className="h-4 w-4" /> JSON Input </TabsTrigger> <TabsTrigger value="midi-import" className="flex items-center gap-2"> <Upload className="h-4 w-4" /> MIDI Import </TabsTrigger> <TabsTrigger value="song-list" className="flex items-center gap-2"> <List className="h-4 w-4" /> Song List </TabsTrigger> </TabsList> <TabsContent value="player" className="space-y-6"> {/* Current Song Display */} <SongDisplay currentSong={currentSong} isPlaying={isPlaying} onPlay={handlePlaySong} onExport={() => { console.log('Exporting song...'); }} playbackProgress={0} midiInstruments={{}} midiDrums={{}} /> {/* Playback Controls */} <Card> <CardHeader> <CardTitle>Playback Controls</CardTitle> <CardDescription> Control MIDI playback and manage the current song </CardDescription> </CardHeader> <CardContent> <div className="flex items-center gap-4"> <Button onClick={handlePlaySong} disabled={!currentSong || isPlaying} className="flex items-center gap-2" > <Play className="h-4 w-4" /> Play </Button> <Button onClick={handleStopSong} disabled={!isPlaying} variant="outline" className="flex items-center gap-2" > <Square className="h-4 w-4" /> Stop </Button> {currentSong && ( <div className="text-sm text-muted-foreground"> {currentSong.notes?.length || 0} notes • {currentSong.totalTime?.toFixed(2) || 0}s </div> )} </div> </CardContent> </Card> </TabsContent> <TabsContent value="json-input"> <JsonInput onNoteSequenceLoad={handleNoteSequenceLoad} /> </TabsContent> <TabsContent value="midi-import"> <MidiImport onImport={handleMidiImport} /> </TabsContent> <TabsContent value="song-list" className="space-y-6"> <Card> <CardHeader> <CardTitle>Saved Songs</CardTitle> <CardDescription> Browse and manage your saved NoteSequence files </CardDescription> </CardHeader> <CardContent> {songList.length === 0 ? ( <p className="text-muted-foreground text-center py-8"> No songs saved yet. Load a NoteSequence to get started. </p> ) : ( <div className="space-y-2"> {songList.map((song, index) => ( <div key={index} className="flex items-center justify-between p-3 border rounded-lg hover:bg-accent cursor-pointer" onClick={() => handleSongSelection(song)} > <div> <div className="font-medium">{song.filename}</div> <div className="text-sm text-muted-foreground"> {new Date(song.created).toLocaleString()} </div> </div> <Button variant="outline" size="sm"> Load </Button> </div> ))} </div> )} </CardContent> </Card> </TabsContent> </Tabs> {/* Modals */} {showSettings && ( <SettingsModal config={config} onClose={() => setShowSettings(false)} onSave={handleConfigChange} /> )} {showAbout && ( <AboutModal onClose={() => setShowAbout(false)} /> )} </div> </div> ); } export default App;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tezza1971/mcp-midi'

If you have feedback or need assistance with the MCP directory API, please join our Discord server