fully functional runtime stuff. Need settings then new features
This commit is contained in:
@ -18,16 +18,16 @@
|
|||||||
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260220_193822.wav",
|
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260220_193822.wav",
|
||||||
"name": "Pee pee poo poo",
|
"name": "Pee pee poo poo",
|
||||||
"playbackType": "playStop",
|
"playbackType": "playStop",
|
||||||
"startTime": 27.756510985786615,
|
"startTime": 27.587412587412587,
|
||||||
"volume": 1
|
"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",
|
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260220_200442.wav",
|
||||||
"name": "Clip 20260220_200442",
|
"name": "Clip 20260220_200442",
|
||||||
"playbackType": "playStop",
|
"playbackType": "playOverlap",
|
||||||
"startTime": 26.1853978671042,
|
"startTime": 25.120307988450435,
|
||||||
"volume": 1
|
"volume": 0.41
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -28,9 +28,9 @@ class AudioRecorder:
|
|||||||
self.channels = 2
|
self.channels = 2
|
||||||
self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
|
self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
|
||||||
self.recordings_dir = "recordings"
|
self.recordings_dir = "recordings"
|
||||||
|
|
||||||
self.stream = sd.InputStream(
|
self.stream = sd.InputStream(
|
||||||
samplerate=self.sample_rate,
|
samplerate=self.sample_rate,
|
||||||
channels=self.channels,
|
|
||||||
callback=self.record_callback
|
callback=self.record_callback
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,10 +43,9 @@ class AudioRecorder:
|
|||||||
self.stream.stop()
|
self.stream.stop()
|
||||||
|
|
||||||
self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
|
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(
|
self.stream = sd.InputStream(
|
||||||
samplerate=self.sample_rate,
|
samplerate=self.sample_rate,
|
||||||
channels=self.channels,
|
|
||||||
callback=self.record_callback
|
callback=self.record_callback
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -2,6 +2,8 @@ import { MemoryRouter as Router, Routes, Route } from 'react-router-dom';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import Dialog from '@mui/material/Dialog';
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
|
||||||
|
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import DialogActions from '@mui/material/DialogActions';
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
@ -150,13 +152,25 @@ function MainPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
const theme = createTheme({
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#6e44ba', // plum
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#4f3186', // plumDark
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<MainPage />} />
|
<Route path="/" element={<MainPage />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
|
</ThemeProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,9 @@ import Dialog from '@mui/material/Dialog';
|
|||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import DialogActions from '@mui/material/DialogActions';
|
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 { useWavesurfer } from '@wavesurfer/react';
|
||||||
import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.esm.js';
|
import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.esm.js';
|
||||||
import ZoomPlugin from 'wavesurfer.js/dist/plugins/zoom.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 DeleteIcon from '@mui/icons-material/Delete';
|
||||||
import { useSortable } from '@dnd-kit/sortable';
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import { ClipMetadata } from '../../redux/types';
|
import { ClipMetadata, PlaybackType } from '../../redux/types';
|
||||||
import { useAppSelector } from '../hooks';
|
import { useAppSelector } from '../hooks';
|
||||||
|
import PlayStopIcon from './playStopIcon';
|
||||||
|
import PlayOverlapIcon from './playOverlapIcon';
|
||||||
|
|
||||||
export interface AudioTrimmerProps {
|
export interface AudioTrimmerProps {
|
||||||
metadata: ClipMetadata;
|
metadata: ClipMetadata;
|
||||||
@ -43,6 +48,7 @@ export default function AudioTrimmer({
|
|||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||||
const [nameInput, setNameInput] = useState<string>(metadata.name);
|
const [nameInput, setNameInput] = useState<string>(metadata.name);
|
||||||
|
const [volumeInput, setVolumeInput] = useState<number>(metadata.volume ?? 1);
|
||||||
const collectionNames = useAppSelector((state) =>
|
const collectionNames = useAppSelector((state) =>
|
||||||
state.collections.map((col) => col.name),
|
state.collections.map((col) => col.name),
|
||||||
);
|
);
|
||||||
@ -223,6 +229,7 @@ export default function AudioTrimmer({
|
|||||||
} else {
|
} else {
|
||||||
const allRegions = (plugins[0] as RegionsPlugin).getRegions();
|
const allRegions = (plugins[0] as RegionsPlugin).getRegions();
|
||||||
if (allRegions.length > 0) {
|
if (allRegions.length > 0) {
|
||||||
|
wavesurfer.setVolume(metadata.volume ?? 1);
|
||||||
wavesurfer.play(allRegions[0].start, allRegions[0].end);
|
wavesurfer.play(allRegions[0].start, allRegions[0].end);
|
||||||
} else {
|
} else {
|
||||||
wavesurfer.play();
|
wavesurfer.play();
|
||||||
@ -404,12 +411,74 @@ export default function AudioTrimmer({
|
|||||||
<div className="m-1 wavesurfer-scroll-container">
|
<div className="m-1 wavesurfer-scroll-container">
|
||||||
<div ref={containerRef} className="wavesurfer-inner" />
|
<div ref={containerRef} className="wavesurfer-inner" />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid justify-items-stretch grid-cols-2 text-neutral-500">
|
<div className="flex justify-between mt-2">
|
||||||
<div className="m-1 flex justify-start">
|
<span className="w-1/5 flex-none text-sm text-neutral-500 self-center">
|
||||||
<text className="text-sm ">
|
|
||||||
Clip: {formatTime(metadata.startTime ?? 0)} -{' '}
|
Clip: {formatTime(metadata.startTime ?? 0)} -{' '}
|
||||||
{formatTime(metadata.endTime ?? 0)}
|
{formatTime(metadata.endTime ?? 0)}
|
||||||
</text>
|
</span>
|
||||||
|
<div className="w-3/5 flex-1 flex justify-center items-center">
|
||||||
|
<Slider
|
||||||
|
value={volumeInput}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
onChange={(e, newValue) => 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"
|
||||||
|
/>
|
||||||
|
{/* <input
|
||||||
|
type="range"
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
value={volumeInput}
|
||||||
|
onChange={(e) => {
|
||||||
|
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"
|
||||||
|
/> */}
|
||||||
|
</div>
|
||||||
|
<div className="w-1/5 flex justify-end text-sm text-neutral-500">
|
||||||
|
<ToggleButtonGroup value={metadata.playbackType}>
|
||||||
|
<ToggleButton
|
||||||
|
value="playStop"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
if (onSave)
|
||||||
|
onSave({
|
||||||
|
...metadata,
|
||||||
|
playbackType: PlaybackType.PlayStop,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlayStopIcon />
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton
|
||||||
|
value="playOverlap"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
if (onSave)
|
||||||
|
onSave({
|
||||||
|
...metadata,
|
||||||
|
playbackType: PlaybackType.PlayOverlap,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlayOverlapIcon />
|
||||||
|
</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
29
electron-ui/src/renderer/components/playOverlapIcon.tsx
Normal file
29
electron-ui/src/renderer/components/playOverlapIcon.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function PlayOverlapIcon({
|
||||||
|
size = 24,
|
||||||
|
color = 'currentColor',
|
||||||
|
}: {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 32 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
{/* Filled play arrow */}
|
||||||
|
<polygon points="4,4 4,20 16,12" fill={color} />
|
||||||
|
{/* Outlined play arrow (underneath and to the right) */}
|
||||||
|
<polygon
|
||||||
|
points="12,4 12,20 24,12"
|
||||||
|
fill="none"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth={1}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
electron-ui/src/renderer/components/playStopIcon.tsx
Normal file
23
electron-ui/src/renderer/components/playStopIcon.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export default function PlayStopIcon({
|
||||||
|
size = 24,
|
||||||
|
color = 'currentColor',
|
||||||
|
}: {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 48 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
aria-label="Play/Stop Icon"
|
||||||
|
>
|
||||||
|
{/* Play Arrow */}
|
||||||
|
<polygon points="4,4 20,12 4,20" fill={color} />
|
||||||
|
{/* Stop Square */}
|
||||||
|
<rect x="28" y="4" width="16" height="16" rx="2" fill={color} />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user