From 2ce6d1e2447892ca0fd51d6b22a20cebb1ae5011 Mon Sep 17 00:00:00 2001 From: James Twose Date: Sat, 14 Mar 2026 20:29:48 +0000 Subject: [PATCH 1/5] adding mic selection in the settings --- package-lock.json | 72 +++++++++++++++++++++++++++++++-- package.json | 2 +- src/App.jsx | 3 ++ src/components/AudioCapture.jsx | 38 ++++++++++++++++- src/components/Settings.jsx | 51 +++++++++++++++++++++-- src/services/audioService.js | 12 +++++- 6 files changed, 166 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index fbc5e8c..8975003 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "whattheflat", - "version": "0.1.0", + "name": "jambuddy", + "version": "0.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "whattheflat", - "version": "0.1.0", + "name": "jambuddy", + "version": "0.6.0", "dependencies": { "audiomotion-analyzer": "^4.5.4", "pitchy": "^4.1.0", @@ -2189,6 +2189,70 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.8.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.8.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", diff --git a/package.json b/package.json index 4dc7417..73e7d49 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "whattheflat", + "name": "jambuddy", "description": "A jam session companion app that provides a chromatic tuner, chord progressions, and more.", "version": "0.6.0", "private": true, diff --git a/src/App.jsx b/src/App.jsx index 87d7ea8..e9a3ab8 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -24,6 +24,8 @@ const DEFAULTS = { // Audio input minClarity: 0.80, minVolume: 0.01, + // Selected device (null = system default) + audioDeviceId: null, } function loadStored(key, fallback) { @@ -508,6 +510,7 @@ export default function App() { isListening={isListening} minClarity={config.minClarity} minVolume={config.minVolume} + audioDeviceId={config.audioDeviceId} onPermissionError={() => { setMicError(true) setIsListening(false) diff --git a/src/components/AudioCapture.jsx b/src/components/AudioCapture.jsx index c3b67ae..14141de 100644 --- a/src/components/AudioCapture.jsx +++ b/src/components/AudioCapture.jsx @@ -81,7 +81,7 @@ function detectBassPC(freqData, sampleRate, fftSize) { return ((bestMidi % 12) + 12) % 12 } -export default function AudioCapture({ onNote, onChroma, onOnset, onWaveform, isListening, minClarity = 0.80, minVolume = 0.01, onPermissionError }) { +export default function AudioCapture({ onNote, onChroma, onOnset, onWaveform, isListening, minClarity = 0.80, minVolume = 0.01, onPermissionError, audioDeviceId = null }) { const audioCtxRef = useRef(null) const timeBufRef = useRef(null) const freqBufRef = useRef(null) @@ -121,8 +121,34 @@ export default function AudioCapture({ onNote, onChroma, onOnset, onWaveform, is stop() let stream try { - stream = await navigator.mediaDevices.getUserMedia({ audio: true }) + // Helpful debug: list available media devices before requesting permission + try { + if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) { + const devices = await navigator.mediaDevices.enumerateDevices() + const audioIns = devices.filter(d => d.kind === 'audioinput') + console.log('Audio inputs available:', audioIns) + } + } catch (e) { + console.warn('enumerateDevices failed', e) + } + + const constraints = audioDeviceId + ? { audio: { deviceId: { exact: audioDeviceId } } } + : { audio: true } + + console.log('Requesting getUserMedia with constraints:', constraints) + stream = await navigator.mediaDevices.getUserMedia(constraints) } catch (err) { + // If permission denied or other error, surface extra diagnostics when possible + console.warn('getUserMedia failed', err) + try { + if (navigator.permissions && navigator.permissions.query) { + const p = await navigator.permissions.query({ name: 'microphone' }) + console.log('microphone permission state:', p.state) + } + } catch (e) { + // ignore; not all environments support Permissions API for microphone + } onPermissionErrorRef.current?.(err) return } @@ -135,6 +161,14 @@ export default function AudioCapture({ onNote, onChroma, onOnset, onWaveform, is audioCtxRef.current = ctx const source = ctx.createMediaStreamSource(stream) + // Debug: log the acquired audio tracks and labels/deviceIds + try { + const tracks = stream.getAudioTracks() + console.log('Acquired audio tracks:', tracks.map(t => ({ label: t.label, id: t.id, enabled: t.enabled, muted: t.muted }))) + } catch (e) { + console.warn('Could not inspect stream tracks', e) + } + // Small analyser — pitch detection needs fast time-domain data const pa = ctx.createAnalyser() pa.fftSize = PITCH_FFT diff --git a/src/components/Settings.jsx b/src/components/Settings.jsx index 34f714f..f109aa2 100644 --- a/src/components/Settings.jsx +++ b/src/components/Settings.jsx @@ -1,3 +1,5 @@ +import { useRef, useEffect, useState } from 'react' + const SETTINGS = [ { section: 'Chord Detection', @@ -70,12 +72,10 @@ const SETTINGS = [ }, ] -import { useRef } from 'react' - export default function Settings({ config, onChange, onClose, onReset, monoColor, onMonoColorChange }) { // Snapshot on mount so Cancel can restore - const savedConfig = useRef(config) - const savedMono = useRef(monoColor) + const savedConfig = useRef(config) + const savedMono = useRef(monoColor) function handleCancel() { Object.entries(savedConfig.current).forEach(([k, v]) => onChange(k, v)) @@ -83,6 +83,41 @@ export default function Settings({ config, onChange, onClose, onReset, monoColor onClose() } + function DeviceSelector({ config, onChange }) { + const [devices, setDevices] = useState([]) + + async function refresh() { + try { + if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) return + const list = await navigator.mediaDevices.enumerateDevices() + setDevices(list.filter(d => d.kind === 'audioinput')) + } catch (e) { + console.warn('enumerateDevices failed', e) + } + } + + useEffect(() => { refresh() }, []) + + return ( +
+
+ + +
+
If device labels are empty, grant microphone permission first and hit Refresh.
+
+ ) + } + return (
@@ -175,6 +210,14 @@ export default function Settings({ config, onChange, onClose, onReset, monoColor
))} + + {/* Audio device selector */} +
+

+ Microphone +

+ +
diff --git a/src/services/audioService.js b/src/services/audioService.js index 12db1ec..e1c8532 100644 --- a/src/services/audioService.js +++ b/src/services/audioService.js @@ -78,7 +78,17 @@ export function useAudioTuner() { const startListening = async () => { if (isListening) return try { - const stream = await navigator.mediaDevices.getUserMedia({ audio: true }) + // Read saved device selection from config (if present) + let deviceId = null + try { + const cfg = JSON.parse(localStorage.getItem('wtf_config') || '{}') + deviceId = cfg.audioDeviceId || null + } catch (e) { + // ignore + } + const constraints = deviceId ? { audio: { deviceId: { exact: deviceId } } } : { audio: true } + console.log('Tuner requesting getUserMedia with', constraints) + const stream = await navigator.mediaDevices.getUserMedia(constraints) const ctx = new (window.AudioContext || window.webkitAudioContext)() const analyser = ctx.createAnalyser() analyser.fftSize = 4096 From a1048b0e55fe36ca021149b432ffa3f874f03f06 Mon Sep 17 00:00:00 2001 From: James Twose Date: Sat, 14 Mar 2026 20:43:18 +0000 Subject: [PATCH 2/5] attempt 1 at redoing the release yaml --- .github/workflows/release.yml | 67 ++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5eb39a9..0d8dcb0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,33 +9,66 @@ permissions: contents: write jobs: - build-windows: - runs-on: windows-latest + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: windows-latest + command: npm run electron:build:win + artifact: release/*.exe + env: + CSC_IDENTITY_AUTO_DISCOVERY: false + WIN_CSC_LINK: '' + - os: macos-latest + command: npm run electron:build:mac -- --universal + artifact: release/*.dmg + env: + CSC_IDENTITY_AUTO_DISCOVERY: false + - os: ubuntu-latest + command: npm run electron:build:linux + artifact: release/*.AppImage + env: + CSC_IDENTITY_AUTO_DISCOVERY: false steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm install - - run: npm run electron:build:win + - name: Build (Windows) + if: matrix.os == 'windows-latest' + run: ${{ matrix.command }} env: CSC_IDENTITY_AUTO_DISCOVERY: false WIN_CSC_LINK: '' - - uses: softprops/action-gh-release@v2 - with: - files: releases/*.exe - build-mac: - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20 - - run: npm install - - run: npm run electron:build:mac + - name: Build (mac/linux) + if: matrix.os != 'windows-latest' + run: ${{ matrix.command }} env: CSC_IDENTITY_AUTO_DISCOVERY: false - - uses: softprops/action-gh-release@v2 + - name: Upload build artifacts + uses: actions/upload-artifact@v4 with: - files: releases/*.dmg + name: ${{ matrix.os }}-artifacts + path: ${{ matrix.artifact }} + if-no-files-found: ignore + + publish: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Download all build artifacts + uses: actions/download-artifact@v4 + - name: Aggregate artifacts + run: | + mkdir -p release_all + find . -type f \( -name "*.dmg" -o -name "*.AppImage" -o -name "*.exe" -o -name "*.deb" \) -exec cp {} release_all/ \; + - name: List aggregated artifacts + run: ls -la release_all || true + - name: Create GitHub Release with all artifacts + uses: softprops/action-gh-release@v2 + with: + files: release_all/* From 3ddf85412322677c3da21b6a7e9593cc3ea210f1 Mon Sep 17 00:00:00 2001 From: James Twose Date: Sat, 14 Mar 2026 20:47:05 +0000 Subject: [PATCH 3/5] adding more build info to the README and bumping version --- README.md | 36 ++++++++++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3571627..117cf03 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,42 @@ npm run electron:build:linux Output is placed in `frontend/release/`. +## Releasing / Tagging + +To create a GitHub release and trigger the CI build pipeline, create an annotated tag and push it to origin. The release workflow runs on tags matching `v*` (for example `v0.6.1`). + +Local tagging example: + +```bash +# update package.json version first if desired +git tag -a v0.6.1 -m "Release v0.6.1" +git push origin v0.6.1 +``` + +What the GitHub Action does (`.github/workflows/release.yml`): + +- Listens for pushed tags `v*` and runs a matrix build across Windows, macOS and Linux. +- macOS is built as a universal binary (`--universal`) so a single DMG supports both Intel and Apple Silicon. +- Each matrix job builds the installer using `electron-builder`, uploads its artifacts, and a final `publish` job aggregates all artifacts into one GitHub release. + +If you prefer to run builds locally before tagging, use the npm scripts in the repository root: + +```bash +# Windows NSIS +npm run electron:build:win + +# macOS DMG (universal) +npm run electron:build:mac -- --universal + +# Linux AppImage +npm run electron:build:linux +``` + +CI notes / troubleshooting +- The workflow uploads artifacts from `release/` into the release. Ensure `package.json` build `directories.output` matches the workflow's expected `release/` folder. +- If mac packaging for x64 on ARM-hosted runners fails, switch to `--universal` (already configured) or build x64 on an Intel runner. +- To test the workflow locally, consider using `nektos/act` or push a temporary tag like `vtest`. + ## Design Tokens All colors are defined in `frontend/tailwind.config.js` and can be referenced by name in any component. diff --git a/package-lock.json b/package-lock.json index 8975003..af0ce13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jambuddy", - "version": "0.6.0", + "version": "0.6.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jambuddy", - "version": "0.6.0", + "version": "0.6.1", "dependencies": { "audiomotion-analyzer": "^4.5.4", "pitchy": "^4.1.0", diff --git a/package.json b/package.json index 73e7d49..a9cea82 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jambuddy", "description": "A jam session companion app that provides a chromatic tuner, chord progressions, and more.", - "version": "0.6.0", + "version": "0.6.1", "private": true, "type": "module", "main": "electron/main.cjs", From 2efc78569443a26bf31877f6cec6e1095a6ef604 Mon Sep 17 00:00:00 2001 From: James Twose Date: Sat, 14 Mar 2026 20:56:02 +0000 Subject: [PATCH 4/5] adding more build info to the README and bumping version - v2 --- .github/workflows/release.yml | 75 ++++++++++++++++++----------------- package-lock.json | 4 +- package.json | 4 +- 3 files changed, 43 insertions(+), 40 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0d8dcb0..a7371c0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,63 +12,66 @@ jobs: build: runs-on: ${{ matrix.os }} strategy: + fail-fast: false # Ensures one OS failing doesn't kill the others matrix: include: - os: windows-latest command: npm run electron:build:win - artifact: release/*.exe - env: - CSC_IDENTITY_AUTO_DISCOVERY: false - WIN_CSC_LINK: '' + artifact_pattern: "release/*.exe" - os: macos-latest - command: npm run electron:build:mac -- --universal - artifact: release/*.dmg - env: - CSC_IDENTITY_AUTO_DISCOVERY: false + # Added --universal here if you want to support both Intel and Apple Silicon + command: npm run electron:build:mac -- --universal + artifact_pattern: "release/*.dmg" - os: ubuntu-latest command: npm run electron:build:linux - artifact: release/*.AppImage - env: - CSC_IDENTITY_AUTO_DISCOVERY: false + artifact_pattern: "release/*.AppImage" + + env: + # This fixes the "GitHub Personal Access Token is not set" error + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Prevents errors related to missing code-signing certificates + CSC_IDENTITY_AUTO_DISCOVERY: false + steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 20 - - run: npm install - - name: Build (Windows) - if: matrix.os == 'windows-latest' - run: ${{ matrix.command }} - env: - CSC_IDENTITY_AUTO_DISCOVERY: false - WIN_CSC_LINK: '' + cache: 'npm' - - name: Build (mac/linux) - if: matrix.os != 'windows-latest' + - name: Install dependencies + run: npm install + + - name: Build Application run: ${{ matrix.command }} - env: - CSC_IDENTITY_AUTO_DISCOVERY: false - - name: Upload build artifacts + + - name: Upload Artifacts uses: actions/upload-artifact@v4 with: - name: ${{ matrix.os }}-artifacts - path: ${{ matrix.artifact }} - if-no-files-found: ignore + name: artifacts-${{ matrix.os }} + path: ${{ matrix.artifact_pattern }} + if-no-files-found: error publish: needs: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Download all build artifacts + - name: Download all artifacts uses: actions/download-artifact@v4 - - name: Aggregate artifacts - run: | - mkdir -p release_all - find . -type f \( -name "*.dmg" -o -name "*.AppImage" -o -name "*.exe" -o -name "*.deb" \) -exec cp {} release_all/ \; - - name: List aggregated artifacts - run: ls -la release_all || true - - name: Create GitHub Release with all artifacts + with: + # This downloads all "artifacts-*" into a folder named 'all-outputs' + path: all-outputs + merge-multiple: true + + - name: List files for debugging + run: ls -R all-outputs + + - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: - files: release_all/* + # Point directly to the folder where all OS builds are merged + files: all-outputs/* + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index af0ce13..b0380ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jambuddy", - "version": "0.6.1", + "version": "0.6.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jambuddy", - "version": "0.6.1", + "version": "0.6.2", "dependencies": { "audiomotion-analyzer": "^4.5.4", "pitchy": "^4.1.0", diff --git a/package.json b/package.json index a9cea82..98cd73e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jambuddy", "description": "A jam session companion app that provides a chromatic tuner, chord progressions, and more.", - "version": "0.6.1", + "version": "0.6.2", "private": true, "type": "module", "main": "electron/main.cjs", @@ -13,7 +13,7 @@ "electron:build": "vite build && electron-builder", "electron:build:win": "vite build && electron-builder --win --publish never", "electron:build:mac": "vite build && electron-builder --mac --publish never", - "electron:build:linux": "vite build && electron-builder --linux" + "electron:build:linux": "vite build && electron-builder --linux --publish never" }, "dependencies": { "audiomotion-analyzer": "^4.5.4", From 7d5ad306b40d1b6c10bbb9ce5002f72136c28446 Mon Sep 17 00:00:00 2001 From: James Twose Date: Sat, 14 Mar 2026 21:03:31 +0000 Subject: [PATCH 5/5] updating the build productName to JamBuddy --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 98cd73e..fae472b 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,8 @@ "wait-on": "^9.0.4" }, "build": { - "appId": "com.whattheflat.app", - "productName": "WhatTheFlat", + "appId": "com.jambuddy.app", + "productName": "JamBuddy", "files": [ "dist/**/*", "electron/**/*",