{"id":1407,"url":"https://github.com/vadymmarkov/Beethoven","last_synced_at":"2025-08-06T13:31:53.322Z","repository":{"id":47250250,"uuid":"45075414","full_name":"vadymmarkov/Beethoven","owner":"vadymmarkov","description":":guitar: A maestro of pitch detection.","archived":false,"fork":false,"pushed_at":"2021-09-06T20:36:59.000Z","size":3100,"stargazers_count":825,"open_issues_count":16,"forks_count":146,"subscribers_count":40,"default_branch":"master","last_synced_at":"2024-10-30T00:32:50.562Z","etag":null,"topics":["audio","audio-processing","ios","pitch-detection","pitch-engine","pitch-estimation","swift","tuner"],"latest_commit_sha":null,"homepage":"https://github.com/vadymmarkov","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vadymmarkov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-10-27T23:02:13.000Z","updated_at":"2024-10-29T16:30:00.000Z","dependencies_parsed_at":"2022-11-29T13:20:18.971Z","dependency_job_id":null,"html_url":"https://github.com/vadymmarkov/Beethoven","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vadymmarkov%2FBeethoven","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vadymmarkov%2FBeethoven/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vadymmarkov%2FBeethoven/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vadymmarkov%2FBeethoven/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vadymmarkov","download_url":"https://codeload.github.com/vadymmarkov/Beethoven/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228825783,"owners_count":17977556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","audio-processing","ios","pitch-detection","pitch-engine","pitch-estimation","swift","tuner"],"created_at":"2024-01-05T20:15:45.739Z","updated_at":"2024-12-09T14:30:40.342Z","avatar_url":"https://github.com/vadymmarkov.png","language":"Swift","funding_links":[],"categories":["Media","Libs","Media and Graphics","Swift","Audio [🔝](#readme)"],"sub_categories":["Audio","Other free courses"],"readme":"![Beethoven](https://github.com/vadymmarkov/Beethoven/blob/master/Resources/BeethovenPresentation.png)\n\n[![CI Status](http://img.shields.io/travis/vadymmarkov/Beethoven.svg?style=flat)](https://travis-ci.org/vadymmarkov/Beethoven)\n[![Version](https://img.shields.io/cocoapods/v/Beethoven.svg?style=flat)](http://cocoadocs.org/docsets/Beethoven)\n[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n![Swift](https://img.shields.io/badge/%20in-swift%204.0-orange.svg)\n[![License](https://img.shields.io/cocoapods/l/Beethoven.svg?style=flat)](http://cocoadocs.org/docsets/Beethoven)\n[![Platform](https://img.shields.io/cocoapods/p/Beethoven.svg?style=flat)](http://cocoadocs.org/docsets/Beethoven)\n\n**Beethoven** is an audio processing Swift library that provides an\neasy-to-use interface to solve an age-old problem of pitch detection of musical\nsignals. You can read more about this subject on\n[Wikipedia](https://en.wikipedia.org/wiki/Pitch_detection_algorithm).\n\nThe basic workflow is to get the audio buffer from the input/output source,\ntransform it to a format applicable for processing and apply one of the pitch\nestimation algorithms to find the fundamental frequency. For the end user it\ncomes down to choosing estimation algorithm and implementation of delegate\nmethods.\n\n**Beethoven** is designed to be flexible, customizable and highly extensible.\n\nThe main purpose of the library is to collect Swift implementations of various\ntime and frequency domain algorithms for monophonic pitch extraction, with\ndifferent rate of accuracy and speed, to cover as many as possible pitch\ndetection scenarios, musical instruments and human voice. Current\nimplementations could also be not perfect and obviously there is a place for\nimprovements. It means that [contribution](#contributing) is very important\nand more than welcome!\n\n## Table of Contents\n\n\u003cimg src=\"https://github.com/vadymmarkov/Beethoven/blob/master/Resources/BeethovenIcon.png\" width=\"195\" height=\"199\" alt=\"Beethoven Icon\" align=\"right\" /\u003e\n\n* [Key features](#key-features)\n* [Usage](#usage)\n  * [Configuration](#configuration)\n  * [Pitch engine](#pitch-engine)\n  * [Signal tracking](#signal-tracking)\n  * [Transform](#transform)\n  * [Estimation](#estimation)\n  * [Error handling](#error-handling)\n* [Pitch detection specifics](#pitch-detection-specifics)\n* [Examples](#examples)\n* [Installation](#installation)\n* [Components](#components)\n* [Author](#author)\n* [Contributing](#contributing)\n* [License](#license)\n\n## Key features\n\n- [x] Audio signal tracking with `AVAudioEngine` and audio nodes.\n- [x] Pre-processing of audio buffer by one of the available \"transformers\".\n- [x] Pitch estimation.\n\n## Usage\n\n### Configuration\nConfigure buffer size and estimation strategy with the `Config` struct, which\nis used in the initialization of `PitchEngine`. For the case when a signal\nneeds to be tracked from the device output, there is the `audioUrl` parameter,\nwhich is meant to be a URL of your audio file.\n\n```swift\n// Creates a configuration for the input signal tracking (by default).\nlet config = Config(\n  bufferSize: 4096,\n  estimationStrategy: .yin\n)\n\n// Creates a configuration for the output signal tracking.\nlet config = Config(\n  bufferSize: 4096,\n  estimationStrategy: .yin,\n  audioUrl: URL\n)\n```\n\n`Config` could also be instantiated without any parameters:\n\n```swift\n// Input signal tracking with YIN algorithm.\nlet config = Config()\n```\n\n### Pitch engine\n`PitchEngine` is the main class you are going to work with to find the pitch.\nIt can be instantiated with a configuration and delegate:\n\n```swift\nlet pitchEngine = PitchEngine(config: config, delegate: pitchEngineDelegate)\n```\n\nBoth parameters are optional, standard config is used by default, and `delegate`\ncould always be set later:\n\n```swift\nlet pitchEngine = PitchEngine()\npitchEngine.delegate = pitchEngineDelegate\n```\n\n`PitchEngine` uses `PitchEngineDelegate` to inform about results or errors when\nthe pitch detection has been started:\n\n```swift\nfunc pitchEngine(_ pitchEngine: PitchEngine, didReceivePitch pitch: Pitch)\nfunc pitchEngine(_ pitchEngine: PitchEngine, didReceiveError error: Error)\nfunc pitchEngineWentBelowLevelThreshold(_ pitchEngine: PitchEngine)\n```\n\nTo start or stop the pitch tracking process just use the corresponding\n`PitchEngine` methods:\n\n```swift\npitchEngine.start()\npitchEngine.stop()\n```\n\n### Signal tracking\nThere are 2 signal tracking classes:\n- `InputSignalTracker` uses `AVAudioInputNode` to get an audio buffer from the\nrecording input (microphone) in real-time.\n- `OutputSignalTracker` uses `AVAudioOutputNode` and `AVAudioFile` to play an\naudio file and get the audio buffer from the playback output.\n\n### Transform\nTransform is the first step of audio processing where `AVAudioPCMBuffer` object\nis converted to an array of floating numbers. Also it's a place for different\nkind of optimizations. Then array is kept in the `elements` property of the\ninternal `Buffer` struct, which also has optional `realElements` and\n`imagElements` properties that could be useful in the further calculations.\n\nThere are 3 types of transformations at the moment:\n- [Fast Fourier transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform)\n- [YIN](http://recherche.ircam.fr/equipes/pcm/cheveign/pss/2002_JASA_YIN.pdf)\n- `Simple` conversion to use raw float channel data\n\nA new transform strategy could be easily added by implementing of `Transformer`\nprotocol:\n\n```swift\npublic protocol Transformer {\n  func transform(buffer: AVAudioPCMBuffer) -\u003e Buffer\n}\n```\n\n### Estimation\nA pitch detection algorithm (PDA) is an algorithm designed to estimate the pitch\nor fundamental frequency. Pitch is a psycho-acoustic phenomena, and it's\nimportant to choose the most suitable algorithm for your kind of input source,\nconsidering allowable error rate and needed performance.\n\nThe list of available implemented algorithms:\n- `maxValue` - the index of the maximum value in the audio buffer used as a peak\n- `quadradic` - [Quadratic interpolation of spectral peaks](https://ccrma.stanford.edu/%7Ejos/sasp/Quadratic_Interpolation_Spectral_Peaks.html)\n- `barycentric` - [Barycentric correction](http://www.dspguru.com/dsp/howtos/how-to-interpolate-fft-peak)\n- `quinnsFirst` - [Quinn's First Estimator](http://www.dspguru.com/dsp/howtos/how-to-interpolate-fft-peak)\n- `quinnsSecond` - [Quinn's Second Estimator](http://www.dspguru.com/dsp/howtos/how-to-interpolate-fft-peak)\n- `jains` - [Jain's Method](http://www.dspguru.com/dsp/howtos/how-to-interpolate-fft-peak)\n- `hps` - [Harmonic Product Spectrum](http://musicweb.ucsd.edu/~trsmyth/analysis/Harmonic_Product_Spectrum.html)\n- `yin` - [YIN](http://recherche.ircam.fr/equipes/pcm/cheveign/pss/2002_JASA_YIN.pdf)\n\nA new estimation algorithm could be easily added by implementing of `Estimator`\nor `LocationEstimator` protocol:\n\n```swift\nprotocol Estimator {\n  var transformer: Transformer { get }\n\n  func estimateFrequency(sampleRate: Float, buffer: Buffer) throws -\u003e Float\n  func estimateFrequency(sampleRate: Float, location: Int, bufferCount: Int) -\u003e Float\n}\n\nprotocol LocationEstimator: Estimator {\n  func estimateLocation(buffer: Buffer) throws -\u003e Int\n}\n```\n\nThen it should be added to `EstimationStrategy` enum and in the `create` method\nof `EstimationFactory` struct. Normally, a buffer transformation should be\nperformed in a separate struct or class to keep the code base more clean and\nreadable.\n\n### Error handling\nPitch detection is not a trivial task due to some difficulties, such as attack\ntransients, low and high frequencies. Also it's a real-time processing, so we\nare not protected against different kinds of errors. For this purpose there is a\nrange of error types that should be handled properly.\n\n**Signal tracking errors**\n\n```swift\npublic enum InputSignalTrackerError: Error {\n  case inputNodeMissing\n}\n```\n\n**Record permission errors**\n\n`PitchEngine` asks for `AVAudioSessionRecordPermission` on start, but if\npermission is denied it produces the corresponding error:\n\n```swift\npublic enum PitchEngineError: Error {\n  case recordPermissionDenied\n}\n```\n\n**Pitch estimation errors**\n\nSome errors could occur during the process of pitch estimation:\n\n```swift\npublic enum EstimationError: Error {\n  case emptyBuffer\n  case unknownMaxIndex\n  case unknownLocation\n  case unknownFrequency\n}\n```\n\n## Pitch detection specifics\n\nAt the moment **Beethoven** performs only a pitch detection of a monophonic\nrecording.\n\n**Based on Stackoverflow** [answer](http://stackoverflow.com/a/14503090):\n\n\u003e Pitch detection depends greatly on the musical content you want to work with.\n\u003e Extracting the pitch of a monophonic recording (i.e. single instrument or voice)\n\u003e is not the same as extracting the pitch of a single instrument from a polyphonic\n\u003e mixture (e.g. extracting the pitch of the melody from a polyphonic recording).\n\n\u003e For monophonic pitch extraction there are various algorithm that could be\n\u003e implemented both in the time domain and frequency domain\n\u003e ([Wikipedia](https://en.wikipedia.org/wiki/Pitch_detection_algorithm)).\n\n\u003e However, neither will work well if you want to extract the melody from\n\u003e polyphonic material. Melody extraction from polyphonic music is still a\n\u003e research problem.\n\n## Examples\n\n\u003cimg src=\"https://github.com/vadymmarkov/Beethoven/blob/master/Resources/BeethovenTunerExample.png\" width=\"216\" height=\"384\" alt=\"Beethoven Tuner Example\" align=\"right\" /\u003e\n\nCheck out [Guitar Tuner](https://github.com/vadymmarkov/Beethoven/blob/master/Example/GuitarTuner)\nexample to see how you can use **Beethoven** in the real-world scenario to tune\nyour instrument. It uses [YIN](http://recherche.ircam.fr/equipes/pcm/cheveign/pss/2002_JASA_YIN.pdf)  \nestimation algorithm, adopted by @glaurent, and it appears to be quite accurate\nin the pitch detection of electric and acoustic guitar strings.\n\n## Installation\n\n**Beethoven** is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your Podfile:\n\n```ruby\npod 'Beethoven'\n```\n\n**Beethoven** is also available through [Carthage](https://github.com/Carthage/Carthage).\nTo install just write into your Cartfile:\n\n```ruby\ngithub \"vadymmarkov/Beethoven\"\n```\n\n**Beethoven** can also be installed manually. Just download and drop `Sources`\nfolders in your project.\n\n## Components\n\n**Beethoven** uses [Pitchy](https://github.com/vadymmarkov/Pitchy) library to\nget a music pitch with note, octave and offsets from a specified frequency.\n\n## Author\n\nVadym Markov, markov.vadym@gmail.com\n\n## Contributing\n\nCheck the [CONTRIBUTING](https://github.com/vadymmarkov/Beethoven/blob/master/CONTRIBUTING.md)\nfile for more info.\n\n## License\n\n**Beethoven** is available under the MIT license. See the [LICENSE](https://github.com/vadymmarkov/Beethoven/blob/master/LICENSE.md) file\nfor more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvadymmarkov%2FBeethoven","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvadymmarkov%2FBeethoven","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvadymmarkov%2FBeethoven/lists"}