diff --git a/audio-service/metadata.json b/audio-service/metadata.json index e83672c..0ac3d2c 100644 --- a/audio-service/metadata.json +++ b/audio-service/metadata.json @@ -18,16 +18,16 @@ "filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260220_193822.wav", "name": "Pee pee poo poo", "playbackType": "playStop", - "startTime": 27.756510985786615, - "volume": 1 + "startTime": 27.587412587412587, + "volume": 0.69 }, { - "endTime": 28.597210828548004, + "endTime": 27.516843118383072, "filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260220_200442.wav", "name": "Clip 20260220_200442", - "playbackType": "playStop", - "startTime": 26.1853978671042, - "volume": 1 + "playbackType": "playOverlap", + "startTime": 25.120307988450435, + "volume": 0.41 } ] } diff --git a/audio-service/src/__pycache__/audio_recorder.cpython-313.pyc b/audio-service/src/__pycache__/audio_recorder.cpython-313.pyc index 3fd36bd..61d41c8 100644 Binary files a/audio-service/src/__pycache__/audio_recorder.cpython-313.pyc and b/audio-service/src/__pycache__/audio_recorder.cpython-313.pyc differ diff --git a/audio-service/src/audio_recorder.py b/audio-service/src/audio_recorder.py index 05d38e2..b4db2ef 100644 --- a/audio-service/src/audio_recorder.py +++ b/audio-service/src/audio_recorder.py @@ -28,9 +28,9 @@ class AudioRecorder: self.channels = 2 self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32) self.recordings_dir = "recordings" + self.stream = sd.InputStream( samplerate=self.sample_rate, - channels=self.channels, callback=self.record_callback ) @@ -43,10 +43,9 @@ class AudioRecorder: self.stream.stop() self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32) - + print(f"AudioRecorder initialized with duration={self.duration}s, sample_rate={self.sample_rate}Hz, channels={self.channels}") self.stream = sd.InputStream( samplerate=self.sample_rate, - channels=self.channels, callback=self.record_callback ) diff --git a/audio-service/src/routes/__pycache__/recording.cpython-313.pyc b/audio-service/src/routes/__pycache__/recording.cpython-313.pyc index 6206238..d15b537 100644 Binary files a/audio-service/src/routes/__pycache__/recording.cpython-313.pyc and b/audio-service/src/routes/__pycache__/recording.cpython-313.pyc differ diff --git a/electron-ui/src/renderer/App.tsx b/electron-ui/src/renderer/App.tsx index aaaec0e..f9cfde0 100644 --- a/electron-ui/src/renderer/App.tsx +++ b/electron-ui/src/renderer/App.tsx @@ -2,6 +2,8 @@ import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; import { useEffect, useState } from 'react'; import { Provider } from 'react-redux'; import Dialog from '@mui/material/Dialog'; + +import { ThemeProvider, createTheme } from '@mui/material/styles'; import DialogTitle from '@mui/material/DialogTitle'; import DialogContent from '@mui/material/DialogContent'; import DialogActions from '@mui/material/DialogActions'; @@ -150,13 +152,25 @@ function MainPage() { } export default function App() { + const theme = createTheme({ + palette: { + primary: { + main: '#6e44ba', // plum + }, + secondary: { + main: '#4f3186', // plumDark + }, + }, + }); return ( - - - } /> - - + + + + } /> + + + ); } diff --git a/electron-ui/src/renderer/components/AudioTrimer.tsx b/electron-ui/src/renderer/components/AudioTrimer.tsx index 81487af..af5954b 100644 --- a/electron-ui/src/renderer/components/AudioTrimer.tsx +++ b/electron-ui/src/renderer/components/AudioTrimer.tsx @@ -9,6 +9,9 @@ import Dialog from '@mui/material/Dialog'; import DialogTitle from '@mui/material/DialogTitle'; import DialogContent from '@mui/material/DialogContent'; import DialogActions from '@mui/material/DialogActions'; +import Slider from '@mui/material/Slider'; +import ToggleButton from '@mui/material/ToggleButton'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; import { useWavesurfer } from '@wavesurfer/react'; import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.esm.js'; import ZoomPlugin from 'wavesurfer.js/dist/plugins/zoom.esm.js'; @@ -19,8 +22,10 @@ import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import DeleteIcon from '@mui/icons-material/Delete'; import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; -import { ClipMetadata } from '../../redux/types'; +import { ClipMetadata, PlaybackType } from '../../redux/types'; import { useAppSelector } from '../hooks'; +import PlayStopIcon from './playStopIcon'; +import PlayOverlapIcon from './playOverlapIcon'; export interface AudioTrimmerProps { metadata: ClipMetadata; @@ -43,6 +48,7 @@ export default function AudioTrimmer({ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false); const [nameInput, setNameInput] = useState(metadata.name); + const [volumeInput, setVolumeInput] = useState(metadata.volume ?? 1); const collectionNames = useAppSelector((state) => state.collections.map((col) => col.name), ); @@ -223,6 +229,7 @@ export default function AudioTrimmer({ } else { const allRegions = (plugins[0] as RegionsPlugin).getRegions(); if (allRegions.length > 0) { + wavesurfer.setVolume(metadata.volume ?? 1); wavesurfer.play(allRegions[0].start, allRegions[0].end); } else { wavesurfer.play(); @@ -404,12 +411,74 @@ export default function AudioTrimmer({
-
-
- - Clip: {formatTime(metadata.startTime ?? 0)} -{' '} - {formatTime(metadata.endTime ?? 0)} - +
+ + Clip: {formatTime(metadata.startTime ?? 0)} -{' '} + {formatTime(metadata.endTime ?? 0)} + +
+ setVolumeInput(newValue as number)} + onChangeCommitted={(e, newValue) => { + const newVolume = newValue as number; + console.log('Volume change:', newVolume); + if (onSave) onSave({ ...metadata, volume: newVolume }); + }} + color="secondary" + className="p-0 m-0" + /> + {/* { + const newVolume = parseFloat(e.target.value); + setVolumeInput(newVolume); + }} + onDragEnd={(e) => { + console.log('Volume change:'); + // const newVolume = parseFloat(e.target.value); + // if (onSave) onSave({ ...metadata, volume: newVolume }); + }} + className="mx-2 w-full accent-plum" + aria-label="Volume slider" + /> */} +
+
+ + { + if (onSave) + onSave({ + ...metadata, + playbackType: PlaybackType.PlayStop, + }); + }} + > + + + { + if (onSave) + onSave({ + ...metadata, + playbackType: PlaybackType.PlayOverlap, + }); + }} + > + + +
diff --git a/electron-ui/src/renderer/components/playOverlapIcon.tsx b/electron-ui/src/renderer/components/playOverlapIcon.tsx new file mode 100644 index 0000000..3592bd4 --- /dev/null +++ b/electron-ui/src/renderer/components/playOverlapIcon.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +export default function PlayOverlapIcon({ + size = 24, + color = 'currentColor', +}: { + size?: number; + color?: string; +}) { + return ( + + {/* Filled play arrow */} + + {/* Outlined play arrow (underneath and to the right) */} + + + ); +} diff --git a/electron-ui/src/renderer/components/playStopIcon.tsx b/electron-ui/src/renderer/components/playStopIcon.tsx new file mode 100644 index 0000000..7d66388 --- /dev/null +++ b/electron-ui/src/renderer/components/playStopIcon.tsx @@ -0,0 +1,23 @@ +export default function PlayStopIcon({ + size = 24, + color = 'currentColor', +}: { + size?: number; + color?: string; +}) { + return ( + + {/* Play Arrow */} + + {/* Stop Square */} + + + ); +}