Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MacTapper

A macOS menubar app that listens for physical taps and knocks on your MacBook and plays funny sounds.


How It Works

MacTapper uses the built-in microphone to detect sudden physical impacts on the MacBook body (knocks, taps, desk bangs). When a sharp acoustic spike is detected it plays a random sound from one of three categories: Vine Boom, Farts, or Memes.

Detection Pipeline

Mic input
  └── AVAudioEngine tap (441 frames @ 44100 Hz = ~100 windows/sec)
        └── RMS computed per window
              └── delta = |rms - prevRMS|
                    └── delta > threshold AND cooldown (350ms) elapsed?
                          └── onTapDetected() → play sound + flash icon

The key insight is that a physical knock produces a brief, sharp acoustic transient — a sudden spike in RMS that drops immediately back to the noise floor. Comparing consecutive RMS windows (delta) catches this spike reliably without reacting to sustained sounds like speech or music.

Why Not the Accelerometer?

CMMotionManager is API_UNAVAILABLE(macos) — Apple's motion framework is iOS/watchOS only. The Apple Silicon MacBook's hardware accelerometer (AppleSPUHIDInterface) exists but requires private Apple entitlements to receive events. Microphone analysis is the practical alternative.


macOS APIs Used

AppKit

  • NSApplication — app lifecycle, LSUIElement = true hides it from the Dock
  • NSStatusBar / NSStatusItem — menubar icon and menu
  • NSMenu / NSMenuItem — full dropdown menu with actions, state, represented objects
  • NSAlert — mic permission denied warning dialog
  • NSWorkspace — opens System Settings to the Microphone privacy pane
  • NSSound — fallback system sound playback if a sound file fails to load
  • Timer — icon flash reset (150 ms) and watchdog restart (every 2 s)

AVFoundation

  • AVAudioEngine — low-latency audio graph, used here purely for mic input
  • AVAudioEngine.inputNode — the microphone input node
  • installTap(onBus:bufferSize:format:) — installs a real-time callback that fires every 441 frames with raw PCM float data
  • AVAudioPCMBuffer / floatChannelData — raw float samples for RMS calculation
  • AVAudioPlayer — plays sound files from the resource bundle; multiple instances kept alive in activePlayers array to prevent deallocation mid-playback
  • AVCaptureDevice.requestAccess(for: .audio) — requests microphone permission at runtime

CoreAudio

  • AudioObjectGetPropertyData — enumerates all audio devices via kAudioHardwarePropertyDevices
  • kAudioObjectPropertyName — reads each device's display name to find the built-in microphone (name contains "Microphone")
  • AudioUnitSetProperty with kAudioOutputUnitProperty_CurrentDevice — pins AVAudioEngine's input node directly to the built-in mic's AudioDeviceID, preventing WhatsApp / Zoom / other apps from stealing the input and silencing the engine

Swift Package Manager

  • Package.swift with .process("Sounds") resource rule — SPM copies the entire Sounds/ directory into MacTapper_MacTapper.bundle at build time, flat (no subdirectory)
  • Bundle.module — the generated accessor for the SPM resource bundle; sound files are looked up with Bundle.module.url(forResource:withExtension:) (no subdirectory, because SPM flattens resources)

Project Structure

MacTapper/
├── Package.swift                   # SPM manifest — macOS 12+, AVFoundation + AppKit linked
├── Info.plist                      # LSUIElement=true, NSMicrophoneUsageDescription
├── run.sh                          # Build → bundle → kill old instance → open
└── Sources/MacTapper/
    ├── AppDelegate.swift           # Entire app (menubar, mic engine, sound playback)
    └── Sounds/                     # All sound assets (copied flat into resource bundle)
        ├── vine_boom.mp3
        ├── vine_boom2.aiff
        ├── fart1–3.aiff
        ├── wet_fart.aiff
        ├── oof.aiff
        ├── bruh.aiff
        ├── wow.aiff
        ├── no_no_no.aiff
        ├── among_us.aiff
        └── fahhhhh.aiff

Sound Categories

Category Files
Vine Boom vine_boom, vine_boom2
Farts fart1, fart2, fart3, wet_fart
Memes oof, bruh, among_us, no_no_no, wow, fahhhhh

Each tap picks a random enabled category, then a random file from that category.


Menubar Features

  • Tap counter — increments on every detected tap
  • Mic status — shows listening, reconnecting, denied, or error
  • Mute toggle — silences playback without stopping detection; icon switches to muted state
  • Per-category toggles — enable/disable Vine Boom, Farts, Memes independently
  • Sensitivity — Low / Medium / High, maps to RMS delta thresholds:
    • High: 0.005 (catches light finger taps)
    • Medium: 0.010 (solid knock)
    • Low: 0.018 (hard knock only)
  • Icon flash — icon switches to 💥 on tap, reverts after 150 ms

Resilience

Problem: When another app (WhatsApp, Zoom, FaceTime) holds the microphone exclusively, AVAudioEngine stops receiving audio and goes silent.

Solutions implemented:

  1. Built-in mic pinning — CoreAudio kAudioOutputUnitProperty_CurrentDevice forces the engine to use the physical built-in microphone device ID directly, bypassing the system's default input selection
  2. Watchdog timer — fires every 2 seconds; if audioEngine.isRunning is false, tears down and restarts the engine
  3. Config change handlerAVAudioEngineConfigurationChange notification triggers a rebuild of the engine when audio devices are added or removed (e.g. plugging in headphones)

Building and Running

Requires macOS 12+, Xcode Command Line Tools, and Swift 5.7+.

bash run.sh

This script:

  1. Runs swift build
  2. Creates MacTapper.app/Contents/MacOS|Resources structure
  3. Copies the binary, Info.plist, and the SPM resource bundle (MacTapper_MacTapper.bundle) into the app bundle
  4. Kills any existing MacTapper instance
  5. Runs open MacTapper.app

The app must be launched as a .app bundle via open. Running the raw binary directly bypasses LSUIElement and NSStatusBar silently produces nothing.

Grant microphone access when prompted (or via System Settings → Privacy & Security → Microphone).

About

macOS menubar app that plays funny sounds when you tap or knock on your MacBook

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages