This file provides context for AI assistants working with this codebase.
Stay Tuned is an iOS Guitar Tuner written in SwiftUI. It features a modern glassmorphism UI with animated gradients, real-time pitch detection, and support for multiple instruments and tunings.
- Language: Swift 5.9+
- UI Framework: SwiftUI
- Audio: AVAudioEngine, AVAudioSession
- Signal Processing: Accelerate/vDSP for optimized audio processing
- Testing: Swift Testing framework
- Target: iOS 17.0+
- IDE: Xcode 15.0+
All new code must include unit tests. This prevents regressions and ensures code quality.
When building new features or modifying existing code:
- Write tests BEFORE or ALONGSIDE the implementation
- Test all business logic, calculations, and model methods
- Document critical configuration values in tests (like audio sensitivity)
- Run tests before committing:
Cmd+Uin Xcode
Test files are located in Stay TunedTests/:
ChromaticNoteTests.swift- Chromatic note detection from frequencyTuningTests.swift- Tuning and GuitarString modelsAudioTests.swift- Pitch detection and spectrum analyzer sensitivityTunerViewModelTests.swift- ViewModel business logicBeatDetectorTests.swift- Autocorrelation & BPM logic
The spectrum analyzer sensitivity was carefully tuned. These values are tested in AudioTests.swift:
| Parameter | Value | Purpose |
|---|---|---|
| dB floor | -70 | Lower = more sensitive to quiet sounds |
| dB range | 40 | Dynamic range for normalization |
| Sensitivity boost | 2.5x | Amplifies visual response |
Formula: normalizedDb = (db + 70) / 40 * 2.5
If you change these values, update the tests in SpectrumSensitivityTests to match.
docs/ # GitHub Pages - App Store required pages
├── index.html # Landing page with features
├── privacy.html # Privacy Policy
└── support.html # Support & FAQ
Stay Tuned/
├── Models/
│ ├── GuitarString.swift # String model (id, name, frequency, octave)
│ ├── Tuning.swift # Tuning model with closestString() and centsDeviation()
│ ├── TuningPresets.swift # All tuning definitions organized by instrument
│ └── Instrument.swift # Instrument model for multi-instrument support
├── Audio/
│ ├── AudioEngine.swift # AVAudioEngine wrapper for mic capture
│ └── PitchDetector.swift # YIN-based pitch detection algorithm
├── ViewModels/
│ └── TunerViewModel.swift # Main app state and audio processing logic
├── Views/
│ ├── TunerView.swift # Main tuner screen with Tools Menu integration
│ ├── StageModeView.swift # High-contrast, landscape-only performance view
│ ├── ToolsMenuView.swift # Consolidated menu for app tools (Settings, Stage Mode, etc.)
│ ├── HeadstockView.swift # Visual headstock with tappable tuning pegs
│ ├── TuningMeterView.swift # Animated needle meter with cents/Hz display
│ ├── TuningPickerView.swift # Tuning selection menu
│ ├── SettingsView.swift # Settings hub with navigation to sub-settings
│ └── ReferencePitchView.swift # Reference pitch adjustment (A=432-444Hz)
└── Store/
├── StoreManager.swift # StoreKit integration
└── TipProduct.swift # Tip jar product definitions
- Uses YIN-inspired algorithm with vDSP optimizations
- Sample accumulation buffer for low-frequency accuracy
- Difference function + cumulative mean normalized difference (CMNDF)
- Parabolic interpolation for sub-sample precision
- Frequency range: ~70Hz to ~450Hz (covers guitar range)
- Amplitude threshold: 0.008 (sensitive enough for high E string)
- Auto-detect mode: Automatically finds closest string to detected frequency
- Manual mode: User selects string by tapping peg
- In-tune tolerance: ±7 cents
- Sustained confirmation: 0.5 seconds in-tune to confirm a string
- Confirmed strings: Persist green state until user taps to retune
- All-tuned celebration: Quick flash + haptic when all strings confirmed
- Algorithm: Normalized Autocorrelation (172Hz resolution)
- Harmonic Correction: "Double-Time Check" favors 2x tempos (e.g., 120 vs 60 BPM) if detected strength > 50%
- Buffer: 3.5s circular buffer of RMS energy
- Range: 60-200 BPM
- AudioEngine captures mic input with small buffer (low latency)
- PitchDetector accumulates samples and detects pitch
- TunerViewModel processes frequency, calculates cents deviation
- UI updates at high frequency (30+ times/sec) with minimal smoothing
Organized in TuningPresets.swift:
- Guitar: Standard, Half Step Down, Whole Step Down, Drop D, DADGAD, Open tunings (G, D, C, E, A, B)
- Future: Banjo, Ukulele, Bass, etc.
- Purpose: High-contrast, interference-free view for live performance
- Entry: Accessed via Tools Menu or by rotating device to landscape
- Landscape Lock: View enforces landscape orientation by hiding close button
- Wake Lock: Disables idle timer to keep screen on during performance
- Responsive Layout: Dynamically scales UI elements based on available screen space using
GeometryReader
Settings are organized under SettingsView.swift, which acts as a settings hub. Each setting category has its own dedicated view that the user navigates to from the main settings screen.
Current Settings:
- Reference Pitch (
ReferencePitchView.swift) - Adjust A4 reference from 432-444Hz
Adding New Settings:
- Create a new view (e.g.,
AppearanceSettingsView.swift) - Add a
NavigationLinkrow inSettingsView.swiftunder the appropriate section - Use the existing
settingsSection()andsettingsRow()helper functions for consistency
Settings Data:
- Use
@AppStoragefor persistence (stored in UserDefaults) - Pass bindings from
TunerViewModelthroughSettingsViewto sub-views - Reference pitch is stored as
@AppStorage("referencePitch")
- Headstock: Martin-style acoustic guitar headstock
- Tuning pegs: Tappable, show checkmark when confirmed
- Meter needle: Compressed movement in ±in-tune zone for stability
- Hz display: Shows current and target frequency
- Haptic feedback: Success haptic on string confirmation and all-tuned
| Parameter | Value | Location |
|---|---|---|
| In-tune tolerance | ±7 cents | TunerViewModel.swift |
| Sustained duration | 0.5 seconds | TunerViewModel.swift |
| Amplitude threshold | 0.0018 | PitchDetector.swift |
| CMNDF threshold | 0.20 | PitchDetector.swift |
| Min frequency | 30 Hz | PitchDetector.swift |
| Max frequency | 4000 Hz | PitchDetector.swift |
| Reference pitch range | 432-444 Hz | ReferencePitchView.swift |
| Default reference pitch | 440 Hz (A4) | TunerViewModel.swift |
| Spectrum dB floor | -70 dB | SpectrumAnalyzerView.swift |
| Spectrum dB range | 40 | SpectrumAnalyzerView.swift |
| Spectrum sensitivity boost | 2.5x | SpectrumAnalyzerView.swift |
- Microphone usage description is set in build settings (not Info.plist)
- No custom Info.plist file - removed to avoid build conflicts
The /docs folder contains static web pages for GitHub Pages hosting. These are required for App Store submission.
| Page | Purpose | URL (when published) |
|---|---|---|
docs/index.html |
Main landing/documentation page | https://[username].github.io/stay-tuned/ |
docs/privacy.html |
Privacy Policy for App Store | https://[username].github.io/stay-tuned/privacy.html |
docs/support.html |
Support page with FAQ and contact | https://[username].github.io/stay-tuned/support.html |
When adding or removing features, update the docs pages:
docs/index.html- Update the features grid if adding new major featuresdocs/support.html- Update FAQ section for new features or changed behaviordocs/privacy.html- Update if data collection practices change (currently: none)
- Push the
/docsfolder to themainbranch - In GitHub repo settings → Pages → Set source to "Deploy from branch"
- Select
mainbranch and/docsfolder - Pages will be available at
https://[username].github.io/stay-tuned/
- Uses glassmorphism aesthetic matching the app's UI
- Fonts: DM Sans (body), Outfit (headings)
- Color scheme: Dark gradient with cyan/magenta/gold accents
- Responsive design for mobile and desktop
Located in TipJarView within ContentView.swift:
- Venmo:
@NickMacCarthy - Cash App:
$NickMacCarthy - PayPal:
nickmaccarthy
This app must adhere to Apple's App Store Review Guidelines. Key areas to watch:
When submitting to the App Store:
- All IAP products must be submitted alongside the app binary
- Each IAP requires a screenshot in App Store Connect
- IAPs must be marked "Ready to Submit" before app submission
Current IAP Products:
| Product ID | Price | Description |
|---|---|---|
nmac.TipCalculator.tip.service.good |
$0.99 | Good Service tip |
nmac.TipCalculator.tip.service.great |
$2.99 | Great Service tip |
nmac.TipCalculator.tip.service.amazing |
$4.99 | Amazing Service tip |
- iPhone screenshots must show iPhone UI (not iPad)
- iPad screenshots must show iPad UI (not iPhone in a frame)
- Screenshots must reflect actual app functionality
- Avoid marketing materials that don't show the app in use
Required Screenshot Sizes:
| Device | Size (pixels) |
|---|---|
| 6.7" iPhone | 1290 × 2796 |
| 6.5" iPhone | 1284 × 2778 |
| 12.9" iPad Pro | 2048 × 2732 |
| 13" iPad Air/Pro | 2064 × 2752 |
- All permission request flows use neutral language ("Continue", "Next")
- No skip/bypass buttons before system permission dialogs
- All IAP products have screenshots in App Store Connect
- IAP products are included in the submission
- iPhone screenshots taken on iPhone simulator
- iPad screenshots taken on iPad simulator (not iPhone in frame)
- All unit tests pass
- Version and build numbers updated