a22c238dc4
A complete audio-reactive visualizer for psytrance music featuring: Audio Analysis (DSPEngine): - FFT spectrum analysis via Accelerate/vDSP - 64-band Mel spectrogram - Sub-bass energy extraction (<100Hz) - Automatic sidechain pump detection - Harmonic-to-Noise ratio (HNR) calculation - Peak/transient detection 8 Visualization Modes (Metal Shaders): 1. FFT Classic - Frequency spectrum bars with glow 2. Mel Spectrogram - Waterfall display 3. Sub-Bass - Pulsating rings 4. Sidechain Pump - Breathing zoom effect 5. Harmonic/Noise - Geometric vs chaotic particles 6. Mandelbrot - Audio-reactive fractal zoom 7. Tunnel Warp - Infinite tunnel with distortion 8. DMT Geometry - Sacred geometry patterns Features: - Selectable audio input device (BlackHole support) - Configurable buffer size (512/1024) - Reactivity slider for visual intensity - Auto-hiding control panel - Fullscreen support with keyboard shortcuts (1-8, F, ESC) - Persistent settings via UserDefaults - Psytrance-inspired neon/UV color palette
91 lines
2.2 KiB
Swift
91 lines
2.2 KiB
Swift
//
|
|
// AppSettings.swift
|
|
// PsytranceVisualizer
|
|
//
|
|
// Persistent application settings
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Application settings that are persisted between sessions
|
|
struct AppSettings: Codable {
|
|
/// Selected audio input device UID
|
|
var selectedAudioDeviceUID: String?
|
|
|
|
/// Audio buffer size (512 or 1024 samples)
|
|
var bufferSize: Int
|
|
|
|
/// Last used visualization mode (1-8)
|
|
var lastVisualizationMode: Int
|
|
|
|
/// Reactivity slider value (0.0 - 1.0)
|
|
var reactivity: Float
|
|
|
|
/// Whether app was in fullscreen mode
|
|
var isFullscreen: Bool
|
|
|
|
/// Last window frame (for restoration)
|
|
var windowFrame: CodableRect?
|
|
|
|
/// Volume/gain adjustment
|
|
var inputGain: Float
|
|
|
|
/// Whether to show FPS counter
|
|
var showFPS: Bool
|
|
|
|
/// Default settings
|
|
static var `default`: AppSettings {
|
|
AppSettings(
|
|
selectedAudioDeviceUID: nil,
|
|
bufferSize: 1024,
|
|
lastVisualizationMode: 1,
|
|
reactivity: 0.5,
|
|
isFullscreen: false,
|
|
windowFrame: nil,
|
|
inputGain: 1.0,
|
|
showFPS: false
|
|
)
|
|
}
|
|
|
|
/// Available buffer sizes
|
|
static let availableBufferSizes = [512, 1024]
|
|
|
|
/// Validates and clamps settings to valid ranges
|
|
mutating func validate() {
|
|
// Clamp buffer size to valid options
|
|
if !AppSettings.availableBufferSizes.contains(bufferSize) {
|
|
bufferSize = 1024
|
|
}
|
|
|
|
// Clamp visualization mode
|
|
if lastVisualizationMode < 1 || lastVisualizationMode > 8 {
|
|
lastVisualizationMode = 1
|
|
}
|
|
|
|
// Clamp reactivity
|
|
reactivity = max(0.0, min(1.0, reactivity))
|
|
|
|
// Clamp input gain
|
|
inputGain = max(0.0, min(2.0, inputGain))
|
|
}
|
|
}
|
|
|
|
/// Codable wrapper for CGRect
|
|
struct CodableRect: Codable {
|
|
var x: Double
|
|
var y: Double
|
|
var width: Double
|
|
var height: Double
|
|
|
|
init(from rect: CGRect) {
|
|
self.x = Double(rect.origin.x)
|
|
self.y = Double(rect.origin.y)
|
|
self.width = Double(rect.size.width)
|
|
self.height = Double(rect.size.height)
|
|
}
|
|
|
|
var cgRect: CGRect {
|
|
CGRect(x: x, y: y, width: width, height: height)
|
|
}
|
|
}
|