diff --git a/audio-service/settings.json b/audio-service/settings.json index 693356b..3e9bf0b 100644 --- a/audio-service/settings.json +++ b/audio-service/settings.json @@ -1,10 +1,16 @@ { "input_device": { - "default_samplerate": 44100.0, - "index": 1, - "max_input_channels": 8, - "name": "VM Mic mix (VB-Audio Voicemeete" + "index": 49, + "name": "Microphone (Logi C615 HD WebCam)", + "max_input_channels": 1, + "default_samplerate": 48000.0 }, "save_path": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings", - "recording_length": 30 + "recording_length": 30, + "output_device": { + "index": 40, + "name": "Speakers (Realtek(R) Audio)", + "max_output_channels": 2, + "default_samplerate": 48000.0 + } } \ No newline at end of file diff --git a/audio-service/src/__pycache__/audio_recorder.cpython-313.pyc b/audio-service/src/__pycache__/audio_recorder.cpython-313.pyc index 61d41c8..e036ec8 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/__pycache__/settings.cpython-313.pyc b/audio-service/src/__pycache__/settings.cpython-313.pyc index a5a10d6..ace0d6d 100644 Binary files a/audio-service/src/__pycache__/settings.cpython-313.pyc and b/audio-service/src/__pycache__/settings.cpython-313.pyc differ diff --git a/audio-service/src/__pycache__/windows_audio.cpython-313.pyc b/audio-service/src/__pycache__/windows_audio.cpython-313.pyc index 7aa364e..dfd54e2 100644 Binary files a/audio-service/src/__pycache__/windows_audio.cpython-313.pyc and b/audio-service/src/__pycache__/windows_audio.cpython-313.pyc differ diff --git a/audio-service/src/audio_recorder.py b/audio-service/src/audio_recorder.py index b4db2ef..243271b 100644 --- a/audio-service/src/audio_recorder.py +++ b/audio-service/src/audio_recorder.py @@ -30,7 +30,6 @@ class AudioRecorder: self.recordings_dir = "recordings" self.stream = sd.InputStream( - samplerate=self.sample_rate, callback=self.record_callback ) @@ -45,7 +44,6 @@ class AudioRecorder: 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, callback=self.record_callback ) diff --git a/audio-service/src/routes/__pycache__/device.cpython-313.pyc b/audio-service/src/routes/__pycache__/device.cpython-313.pyc index c20c299..7a8e875 100644 Binary files a/audio-service/src/routes/__pycache__/device.cpython-313.pyc and b/audio-service/src/routes/__pycache__/device.cpython-313.pyc differ diff --git a/audio-service/src/routes/device.py b/audio-service/src/routes/device.py index 1f9bdd2..b9e7974 100644 --- a/audio-service/src/routes/device.py +++ b/audio-service/src/routes/device.py @@ -19,13 +19,13 @@ recorder = AudioRecorder() # except Exception as e: # return jsonify({'status': 'error', 'message': str(e)}), 400 -@device_bp.route('/device/get', methods=['GET']) -def get_audio_device(): - try: - device_info = audio_manager.get_default_device('input') - return jsonify({'status': 'success', 'device_info': device_info}) - except Exception as e: - return jsonify({'status': 'error', 'message': str(e)}), 400 +# @device_bp.route('/device/get', methods=['GET']) +# def get_audio_device(): +# try: +# device_info = audio_manager.get_default_device('input') +# return jsonify({'status': 'success', 'device_info': device_info}) +# except Exception as e: +# return jsonify({'status': 'error', 'message': str(e)}), 400 @device_bp.route('/device/list', methods=['GET']) def list_audio_devices(): diff --git a/audio-service/src/settings.py b/audio-service/src/settings.py index 1092d80..56672e4 100644 --- a/audio-service/src/settings.py +++ b/audio-service/src/settings.py @@ -20,21 +20,44 @@ class SettingsManager: else: self.settings = { "input_device": None, + "output_device": None, "save_path": os.path.join(os.getcwd(), "recordings"), "recording_length": 15 } audio_manager = WindowsAudioManager() - devices = audio_manager.list_audio_devices('input') - print(f"Available input devices: {self.settings}") - input = self.settings["input_device"] + input_devices = audio_manager.list_audio_devices('input') + output_devices = audio_manager.list_audio_devices('output') + # print("Available input devices:") + # for i, dev in enumerate(input_devices): + # print(i, dev['name']) + # print("Available output devices:") + # for i, dev in enumerate(output_devices): + # print(i, dev['name']) + # print(f"Available input devices: {input_devices}") + # print(f"Available output devices: {output_devices}") + input = None + output = None + + if("input_device" in self.settings): + input = self.settings["input_device"] + if("output_device" in self.settings): + output = self.settings["output_device"] #see if input device is in "devices", if not set to the first index - if input is not None and any(d['name'] == input["name"] for d in devices): + if input is not None and any(d['name'] == input["name"] for d in input_devices): print(f"Using saved input device index: {input}") else: - input = devices[0] if devices else None + input = input_devices[0] if input_devices else None self.settings["input_device"] = input + + #see if output device is in "devices", if not set to the first index + if output is not None and any(d['name'] == output["name"] for d in output_devices): + print(f"Using saved output device index: {output}") + else: + output = output_devices[0] if output_devices else None + self.settings["output_device"] = output + self.save_settings() diff --git a/audio-service/src/windows_audio.py b/audio-service/src/windows_audio.py index 1099d04..e9a574a 100644 --- a/audio-service/src/windows_audio.py +++ b/audio-service/src/windows_audio.py @@ -18,7 +18,19 @@ class WindowsAudioManager: """ Initialize Windows audio device and volume management. """ - self.devices = sd.query_devices() + host_apis = sd.query_hostapis() + wasapi_device_indexes = None + for api in host_apis: + if api['name'].lower() == 'Windows WASAPI'.lower(): + wasapi_device_indexes = api['devices'] + break + # print(f"Host APIs: {host_apis}") + print(f"WASAPI Device Indexes: {wasapi_device_indexes}") + wasapi_device_indexes = set(wasapi_device_indexes) if wasapi_device_indexes is not None else set() + self.devices = [dev for dev in sd.query_devices() if dev['index'] in wasapi_device_indexes] + # self.devices = sd.query_devices() + print(f"devices: {self.devices}") + self.default_input = sd.default.device[0] self.default_output = sd.default.device[1] @@ -34,7 +46,7 @@ class WindowsAudioManager: { 'index': dev['index'], 'name': dev['name'], - 'max_input_channels': dev['max_input_channels'], + 'channels': dev['max_input_channels'], 'default_samplerate': dev['default_samplerate'] } for dev in self.devices if dev['max_input_channels'] > 0 @@ -44,7 +56,7 @@ class WindowsAudioManager: { 'index': dev['index'], 'name': dev['name'], - 'max_output_channels': dev['max_output_channels'], + 'channels': dev['max_output_channels'], 'default_samplerate': dev['default_samplerate'] } for dev in self.devices if dev['max_output_channels'] > 0 diff --git a/electron-ui/src/main/service.ts b/electron-ui/src/main/service.ts new file mode 100644 index 0000000..e69de29 diff --git a/electron-ui/src/renderer/App.tsx b/electron-ui/src/renderer/App.tsx index 545b7f0..e3d8501 100644 --- a/electron-ui/src/renderer/App.tsx +++ b/electron-ui/src/renderer/App.tsx @@ -14,6 +14,7 @@ import { useAppDispatch, useAppSelector } from './hooks'; import { store } from '../redux/main'; import { useNavigate } from 'react-router-dom'; import SettingsPage from './Settings'; +import { apiFetch } from './api'; function MainPage() { const dispatch = useAppDispatch(); @@ -30,7 +31,7 @@ function MainPage() { useEffect(() => { const fetchMetadata = async () => { try { - const response = await fetch('http://localhost:5010/meta'); + const response = await apiFetch('meta'); const data = await response.json(); dispatch({ type: 'metadata/setAllData', payload: data }); } catch (error) { diff --git a/electron-ui/src/renderer/Settings.tsx b/electron-ui/src/renderer/Settings.tsx index c1576ec..4f2cfec 100644 --- a/electron-ui/src/renderer/Settings.tsx +++ b/electron-ui/src/renderer/Settings.tsx @@ -5,38 +5,57 @@ import './App.css'; import TextField from '@mui/material/TextField'; import Select from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; +import { apiFetch } from './api'; type AudioDevice = { - id: string; - label: string; + index: number; + name: string; + default_sample_rate: number; + channels: number; }; type Settings = { - httpPort: string; - inputDevice: AudioDevice; - outputDevice: AudioDevice; - recordingLength: number; - outputFolder: string; + http_port: number; + input_device: AudioDevice; + output_device: AudioDevice; + recording_length: number; + save_path: string; }; const defaultSettings: Settings = { - httpPort: '', - inputDevice: { id: '', label: '' }, - outputDevice: { id: '', label: '' }, - recordingLength: 0, - outputFolder: '', + http_port: 0, + input_device: { index: 0, name: '', default_sample_rate: 0, channels: 0 }, + output_device: { index: 0, name: '', default_sample_rate: 0, channels: 0 }, + recording_length: 0, + save_path: '', }; -const fetchAudioDevices = async (): Promise => { +async function fetchAudioDevices( + type: 'input' | 'output', +): Promise { // Replace with actual backend call // Example: return window.api.getAudioDevices(); - return [ - { id: 'default-in', label: 'Default Input' }, - { id: 'mic-1', label: 'Microphone 1' }, - { id: 'default-out', label: 'Default Output' }, - { id: 'spk-1', label: 'Speakers 1' }, - ]; -}; + return apiFetch(`device/list?device_type=${type}`) + .then((res) => res.json()) + .then((data) => data.devices as AudioDevice[]) + .catch((error) => { + console.error('Error fetching audio devices:', error); + return []; + }); +} + +async function fetchSettings(): Promise { + // Replace with actual backend call + // Example: return window.api.getAudioDevices(); + console.log('Fetching settings from backend...'); + return apiFetch('settings') + .then((res) => res.json()) + .then((data) => data.settings as Settings) + .catch((error) => { + console.error('Error fetching settings:', error); + return defaultSettings; + }); +} const sendSettingsToBackend = (settings: Settings) => { // Replace with actual backend call @@ -51,11 +70,24 @@ export default function SettingsPage() { const navigate = useNavigate(); useEffect(() => { - fetchAudioDevices() + fetchSettings() + .then((fetchedSettings) => { + console.log('Fetched settings:', fetchedSettings); + setSettings(fetchedSettings); + return null; + }) + .then(() => { + return fetchAudioDevices('input'); + }) .then((devices) => { - // For demo, split devices by id - setInputDevices(devices.filter((d) => d.id.includes('in'))); - setOutputDevices(devices.filter((d) => d.id.includes('out'))); + setInputDevices(devices); + // console.log('Input devices:', devices); + return fetchAudioDevices('output'); + }) + .then((devices) => { + setOutputDevices(devices); + + // console.log('Output devices:', devices); return devices; }) .catch((error) => { @@ -78,16 +110,16 @@ export default function SettingsPage() { const handleFolderChange = async () => { // Replace with actual folder picker // Example: const folder = await window.api.selectFolder(); - const folder = window.prompt( - 'Enter output folder path:', - settings.outputFolder, - ); - if (folder !== null) { - setSettings((prev) => ({ - ...prev, - outputFolder: folder, - })); - } + // const folder = window.prompt( + // 'Enter output folder path:', + // settings.outputFolder, + // ); + // if (folder !== null) { + // setSettings((prev) => ({ + // ...prev, + // outputFolder: folder, + // })); + // } }; return ( @@ -109,9 +141,15 @@ export default function SettingsPage() { variant="standard" type="text" name="httpPort" - value={settings.httpPort} + value={settings.http_port} + onBlur={() => console.log('port blur')} onChange={(e) => { - setSettings((prev) => ({ ...prev, httpPort: e.target.value })); + if (!Number.isNaN(Number(e.target.value))) { + setSettings((prev) => ({ + ...prev, + http_port: Number(e.target.value), + })); + } }} className="ml-2 text-white w-[150px]" /> @@ -121,15 +159,24 @@ export default function SettingsPage() { @@ -139,18 +186,23 @@ export default function SettingsPage() { @@ -161,12 +213,14 @@ export default function SettingsPage() { variant="standard" type="text" name="recordingLength" - value={settings.recordingLength} + value={settings.recording_length} onChange={(e) => { - setSettings((prev) => ({ - ...prev, - recordingLength: e.target.value, - })); + if (!Number.isNaN(Number(e.target.value))) { + setSettings((prev) => ({ + ...prev, + recording_length: Number(e.target.value), + })); + } }} className="ml-2 w-[150px]" /> @@ -177,8 +231,8 @@ export default function SettingsPage() {