pitch_kit
⨠Features
A modern and easy-to-use Flutter package for real-time chord and note detection straight from the device microphone, with a pure Dart DSP core:
- đ¸ Note Detection â Detect individual notes (E, A, D, G, B, âĻ) with the YIN pitch-detection algorithm for accurate, octave-error-resistant results.
- đļ Chord Detection â Recognize chords such as
Am,Bb,C,Em7, andAsus2using chroma analysis and template matching. - đī¸ Tuning in Cents â Every detected note reports how flat or sharp it is, ready to drive a tuner needle.
- đģ Multi-Instrument Ready â Built-in
InstrumentProfilepresets (Guitar, Ukulele, Bass), or supply your own custom tuning. - đ Noise Handling â DC-offset removal, a high-pass filter, an energy gate, and temporal smoothing reduce background interference.
- ⥠Real-Time & Efficient â A from-scratch FFT and Stream-based pipeline, with a bounded pitch-detection search, keep detection fast and responsive.
- đ Flutter Widget Support â A single widget handles the microphone permission (including the permanently-denied â Settings flow) and streams results back to you.
đ Getting started
Requirements
| Requirement | Version |
|---|---|
| Dart SDK | âĨ 3.0.0 |
| Flutter SDK | âĨ 3.10.0 |
| Android â min SDK | 28 |
| Android â target SDK | 37 |
| iOS â min deployment | 15.0 |
Tested on Flutter 3.47.2 / Dart 3.13.2.
Add the package to your pubspec.yaml:
dependencies:
pitch_kit: ^0.0.1
Or install it from the command line:
flutter pub add pitch_kit
This package depends on record for microphone capture and permission_handler for the runtime permission flow. Both are pulled in automatically.
đą Platform Setup
pitch_kit needs microphone access. Setup differs by platform.
Android
No action needed. pitch_kit already declares android.permission.RECORD_AUDIO in its manifest, so it merges into your app automatically.
iOS
1. Add the usage description to ios/Runner/Info.plist. Without it, iOS terminates the app the instant it accesses the microphone:
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to detect notes and chords from your instrument.</string>
2. permission_handler compiles every permission unless you opt out. In ios/Podfile, enable only the microphone macro inside the post_install block:
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
'PERMISSION_MICROPHONE=1',
'PERMISSION_EVENTS=0',
'PERMISSION_EVENTS_FULL_ACCESS=0',
'PERMISSION_REMINDERS=0',
'PERMISSION_CONTACTS=0',
'PERMISSION_CAMERA=0',
'PERMISSION_PHOTOS=0',
'PERMISSION_NOTIFICATIONS=0',
'PERMISSION_MEDIA_LIBRARY=0',
'PERMISSION_SENSORS=0',
'PERMISSION_BLUETOOTH=0',
'PERMISSION_APP_TRACKING_TRANSPARENCY=0',
'PERMISSION_CRITICAL_ALERTS=0',
'PERMISSION_ASSISTANT=0',
'PERMISSION_LOCATION=0',
'PERMISSION_SPEECH_RECOGNIZER=0',
]
end
end
end
đĄ Usage
The example below shows a minimal integration. See the example project for the full app.
đ¸ GuitarTunerListener
Wrap any widget with GuitarTunerListener. It requests the microphone permission, starts detection,
and streams a typed TuningResult back through the onResult callback. Detection stops
automatically when the widget is disposed.
| Parameters | Description |
|---|---|
onResult |
This parameter is required and it's the callback invoked with each detection result (NoteTuningResult, ChordTuningResult, or SilenceTuningResult) |
child |
This parameter is required and it's the widget rendered while the tuner runs in the background |
profile |
This parameter is the InstrumentProfile to tune for, with default value InstrumentProfile.guitar (built-in: .guitar, .ukulele, .bass) |
titleText |
This parameter is the title text of the permission popup with default value "Microphone needed" |
rationaleText |
This parameter is the text shown when the permission can still be requested |
permanentlyDeniedText |
This parameter is the text shown when the permission is permanently denied (the button then opens Settings) |
allowText |
This parameter is the confirm button label when the permission can still be requested with default value "Allow" |
openSettingsText |
This parameter is the confirm button label when the permission is permanently denied with default value "Open Settings" |
dismissText |
This parameter is the dismiss button label with default value "Not now" |
đļ TuningResult
TuningResult is a sealed class with three cases you can handle exhaustively:
| Type | Description |
|---|---|
NoteTuningResult |
A single detected note: name (e.g. "E", "A#"), freq (Hz), and cents (deviation, -50..+50) |
ChordTuningResult |
A detected chord: name (e.g. "Am", "Cmaj7") |
SilenceTuningResult |
No sound / below the detection threshold |
đģ InstrumentProfile
| Parameters | Description |
|---|---|
name |
This parameter is required and it's the human-readable label, e.g. "Guitar" |
minFreq |
This parameter is required and it's the lowest frequency (Hz) analysis considers |
maxFreq |
This parameter is required and it's the highest frequency (Hz) considered for chroma |
bassCeiling |
This parameter is required and it's the ceiling (Hz) for the chord bass-note scan |
harmonicPivot |
This parameter is required and it's the frequency (Hz) around which harmonic down-weighting is centred |
openStrings |
This parameter is required and it's the list of OpenString values for the instrument's standard tuning |
rmsGate |
This parameter is the loudness gate (RMS); frames quieter than this are treated as silence, default value 0.01 |
useFlats |
This parameter is the option to display flats (Bb) instead of sharps (A#), default value false |
import 'package:flutter/material.dart';
import 'package:pitch_kit/pitch_kit.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Pitch Kit',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6200EE)),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _resultNote = '-';
double _resultFreq = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
'Pitch Kit',
style: TextStyle(fontWeight: FontWeight.bold),
),
centerTitle: true,
),
// ==========================================
// 1. Start the tuner
// ==========================================
body: GuitarTunerListener(
profile: InstrumentProfile.guitar,
onResult: (result) {
setState(() {
_resultFreq = result is NoteTuningResult ? result.freq : 0.0;
_resultNote = switch (result) {
NoteTuningResult() => result.name,
ChordTuningResult() => result.name,
SilenceTuningResult() => '-',
};
});
},
// ==========================================
// 2. Render your UI
// ==========================================
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
_resultNote,
style: const TextStyle(
fontSize: 96,
fontWeight: FontWeight.bold,
),
),
if (_resultFreq > 0)
Text(
'${_resultFreq.toStringAsFixed(1)} Hz',
style: const TextStyle(fontSize: 20, color: Colors.grey),
),
],
),
),
),
);
}
}
đŧ Custom Tuning
You can supply your own InstrumentProfile for non-standard tunings:
const dropD = InstrumentProfile(
name: 'Drop D',
minFreq: 60.0,
maxFreq: 5000.0,
bassCeiling: 400.0,
harmonicPivot: 200.0,
openStrings: [
OpenString('D2', 73.42),
OpenString('A2', 110.00),
OpenString('D3', 146.83),
OpenString('G3', 196.00),
OpenString('B3', 246.94),
OpenString('E4', 329.63),
],
);
GuitarTunerListener(
profile: dropD,
onResult: (result) {
//...your code here
},
child: const SizedBox(),
);
đš Supported Chords
Each of the 12 roots is combined with the following qualities: major, minor (m), dominant 7th (7), minor 7th (m7), major 7th (maj7), sus2, sus4, diminished (dim), and augmented (aug).
Examples: C, Am, G7, Em7, Dmaj7, Asus2, Bdim, Faug.
âšī¸ Additional information
Thank you for using pitch_kit! Your feedback helps make this package better. If you encounter any bugs or unexpected behavior, please open an issue on the GitHub repository.


