Beethoven is an audio processing Swift library

Overview

Beethoven

CI Status Version Carthage Compatible Swift License Platform

Beethoven is an audio processing Swift library that provides an easy-to-use interface to solve an age-old problem of pitch detection of musical signals. You can read more about this subject on Wikipedia.

The basic workflow is to get the audio buffer from the input/output source, transform it to a format applicable for processing and apply one of the pitch estimation algorithms to find the fundamental frequency. For the end user it comes down to choosing estimation algorithm and implementation of delegate methods.

Beethoven is designed to be flexible, customizable and highly extensible.

The main purpose of the library is to collect Swift implementations of various time and frequency domain algorithms for monophonic pitch extraction, with different rate of accuracy and speed, to cover as many as possible pitch detection scenarios, musical instruments and human voice. Current implementations could also be not perfect and obviously there is a place for improvements. It means that contribution is very important and more than welcome!

Table of Contents

Beethoven Icon

Key features

  • Audio signal tracking with AVAudioEngine and audio nodes.
  • Pre-processing of audio buffer by one of the available "transformers".
  • Pitch estimation.

Usage

Configuration

Configure buffer size and estimation strategy with the Config struct, which is used in the initialization of PitchEngine. For the case when a signal needs to be tracked from the device output, there is the audioUrl parameter, which is meant to be a URL of your audio file.

// Creates a configuration for the input signal tracking (by default).
let config = Config(
  bufferSize: 4096,
  estimationStrategy: .yin
)

// Creates a configuration for the output signal tracking.
let config = Config(
  bufferSize: 4096,
  estimationStrategy: .yin,
  audioUrl: URL
)

Config could also be instantiated without any parameters:

// Input signal tracking with YIN algorithm.
let config = Config()

Pitch engine

PitchEngine is the main class you are going to work with to find the pitch. It can be instantiated with a configuration and delegate:

let pitchEngine = PitchEngine(config: config, delegate: pitchEngineDelegate)

Both parameters are optional, standard config is used by default, and delegate could always be set later:

let pitchEngine = PitchEngine()
pitchEngine.delegate = pitchEngineDelegate

PitchEngine uses PitchEngineDelegate to inform about results or errors when the pitch detection has been started:

func pitchEngine(_ pitchEngine: PitchEngine, didReceivePitch pitch: Pitch)
func pitchEngine(_ pitchEngine: PitchEngine, didReceiveError error: Error)
func pitchEngineWentBelowLevelThreshold(_ pitchEngine: PitchEngine)

To start or stop the pitch tracking process just use the corresponding PitchEngine methods:

pitchEngine.start()
pitchEngine.stop()

Signal tracking

There are 2 signal tracking classes:

  • InputSignalTracker uses AVAudioInputNode to get an audio buffer from the recording input (microphone) in real-time.
  • OutputSignalTracker uses AVAudioOutputNode and AVAudioFile to play an audio file and get the audio buffer from the playback output.

Transform

Transform is the first step of audio processing where AVAudioPCMBuffer object is converted to an array of floating numbers. Also it's a place for different kind of optimizations. Then array is kept in the elements property of the internal Buffer struct, which also has optional realElements and imagElements properties that could be useful in the further calculations.

There are 3 types of transformations at the moment:

A new transform strategy could be easily added by implementing of Transformer protocol:

public protocol Transformer {
  func transform(buffer: AVAudioPCMBuffer) -> Buffer
}

Estimation

A pitch detection algorithm (PDA) is an algorithm designed to estimate the pitch or fundamental frequency. Pitch is a psycho-acoustic phenomena, and it's important to choose the most suitable algorithm for your kind of input source, considering allowable error rate and needed performance.

The list of available implemented algorithms:

A new estimation algorithm could be easily added by implementing of Estimator or LocationEstimator protocol:

protocol Estimator {
  var transformer: Transformer { get }

  func estimateFrequency(sampleRate: Float, buffer: Buffer) throws -> Float
  func estimateFrequency(sampleRate: Float, location: Int, bufferCount: Int) -> Float
}

protocol LocationEstimator: Estimator {
  func estimateLocation(buffer: Buffer) throws -> Int
}

Then it should be added to EstimationStrategy enum and in the create method of EstimationFactory struct. Normally, a buffer transformation should be performed in a separate struct or class to keep the code base more clean and readable.

Error handling

Pitch detection is not a trivial task due to some difficulties, such as attack transients, low and high frequencies. Also it's a real-time processing, so we are not protected against different kinds of errors. For this purpose there is a range of error types that should be handled properly.

Signal tracking errors

public enum InputSignalTrackerError: Error {
  case inputNodeMissing
}

Record permission errors

PitchEngine asks for AVAudioSessionRecordPermission on start, but if permission is denied it produces the corresponding error:

public enum PitchEngineError: Error {
  case recordPermissionDenied
}

Pitch estimation errors

Some errors could occur during the process of pitch estimation:

public enum EstimationError: Error {
  case emptyBuffer
  case unknownMaxIndex
  case unknownLocation
  case unknownFrequency
}

Pitch detection specifics

At the moment Beethoven performs only a pitch detection of a monophonic recording.

Based on Stackoverflow answer:

Pitch detection depends greatly on the musical content you want to work with. Extracting the pitch of a monophonic recording (i.e. single instrument or voice) is not the same as extracting the pitch of a single instrument from a polyphonic mixture (e.g. extracting the pitch of the melody from a polyphonic recording).

For monophonic pitch extraction there are various algorithm that could be implemented both in the time domain and frequency domain (Wikipedia).

However, neither will work well if you want to extract the melody from polyphonic material. Melody extraction from polyphonic music is still a research problem.

Examples

Beethoven Tuner Example

Check out Guitar Tuner example to see how you can use Beethoven in the real-world scenario to tune your instrument. It uses YIN
estimation algorithm, adopted by @glaurent, and it appears to be quite accurate in the pitch detection of electric and acoustic guitar strings.

Installation

Beethoven is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Beethoven'

Beethoven is also available through Carthage. To install just write into your Cartfile:

github "vadymmarkov/Beethoven"

Beethoven can also be installed manually. Just download and drop Sources folders in your project.

Components

Beethoven uses Pitchy library to get a music pitch with note, octave and offsets from a specified frequency.

Author

Vadym Markov, [email protected]

Contributing

Check the CONTRIBUTING file for more info.

License

Beethoven is available under the MIT license. See the LICENSE file for more info.

Comments
  • Requires `start` twice

    Requires `start` twice

    By some reason mic recognition start only when start called twice:

    class ProcessingUnit: NSObject {
      
      var pitchChangedHandler: ((String) -> ())?
      
      let pitchEngine: PitchEngine = {
        let config = Config()
        let engine = PitchEngine(config: config, delegate: nil)
        return engine
      }()
    		
      override init() {
        super.init()
        pitchEngine.delegate = self
        pitchEngine.start()
        pitchEngine.start()
      }
    
    }
    
    extension ProcessingUnit: PitchEngineDelegate {
      
      func pitchEngineDidReceiveError(_ pitchEngine: PitchEngine, error: Error) {
        
      }
      
      func pitchEngineWentBelowLevelThreshold(_ pitchEngine: PitchEngine) {
        
      }
      
      func pitchEngineDidReceivePitch(_ pitchEngine: PitchEngine, pitch: Pitch) {
        let value = pitch.note.letter.rawValue + "\(pitch.note.octave)"
        pitchChangedHandler?(value)
      }
    }
    
    opened by orkenstein 15
  • pointer to other algorithms ?

    pointer to other algorithms ?

    (Not really an issue)

    From my "real-world" tests, the default configuration works very well with an electric guitar plugged in an amp with no effects. However, I tried with a acoustic one, and while pitch detection is still accurate for the higher strings, it's consistently wrong with the lower ones (open low E is detected as B3, open A is E4...). Do you have any pointers to other pitch detection algorithms that would be worth looking into ? I've already skimmed through the wikipedia page.

    (add: the Fender Tune app works perfectly with that same guitar).

    opened by glaurent 10
  • YIN algorithm, conversion of GuitarTuner example to Swift 3

    YIN algorithm, conversion of GuitarTuner example to Swift 3

    The GuitarTuner example has also been changed to use the YIN algorithm, which, from the testing I've done (electric and acoustic guitar), gives better results than HPS. The correct note is found on the whole range of the instrument.

    opened by glaurent 7
  • Level threshold

    Level threshold

    This branch implements a "signal level threshold" feature which lets the user set a signal level below which no pitch detection is attempted. This avoids the PitchEngineDelegate to be called if there's silence or near silence.

    Limitation : this feature is only implemented for the InputSignalTracker, not the OutputSignalTracker. I suppose it's possible, but since I don't know much about AVFoundation I'm not sure how to do it. It's also likely that my implementation in InputSignalTracker could be improved.

    Also, there should be a way to get the peak/average levels, so the user can set the threshold without guessing.

    This branch also updates the Podfile of the GuitarTuner example.

    opened by glaurent 6
  • Individual note tracking

    Individual note tracking

    Hi @vadymmarkov, first of all, thanks for this awesome library 🎹 🎉! It's been really helpful start understanding how pitch detection works.

    My issue is that I'm trying to get individual notes in a sequence without one note being recognized multiple times. If I play say C4 for 1 second I'll get 5/6 calls to PitchEngineDelegate's func pitchEngineDidReceivePitch(_ pitchEngine: PitchEngine, pitch: Pitch).

    Is there a way to avoid this? Or some sort of comparison to detect if the exact same note is being played without interruptions? The goal is to be able to play a few individual notes (can be the same or different notes) and get the exact output like: A - A - C# - F - E

    opened by marcosgriselli 4
  • Fix PitchEngine start on iOS10 : the installTap() block would never be called

    Fix PitchEngine start on iOS10 : the installTap() block would never be called

    This fixes a problem which occurs on iOS10, where on the first time a PitchEngine is instantiated and started, the installTap() block would never be called (and so no pitch recognition would happen). For some reason, this occurs only the first time a PitchEngine is created and run. Looking for some other examples of installTap, it seems one should pass the outputFormat and not the inputFormat as a parameter.

    Signed-off-by: Guillaume Laurent [email protected]

    opened by glaurent 4
  • A way to monitor mic input level and set a threshold on it ?

    A way to monitor mic input level and set a threshold on it ?

    Hi,

    Trying the Guitar Tuner example, I find that it detects pitches just out of the base level of noise that the microphone gets. So I'm looking for a way to "filter out" low level signals so that detection would only occur if the input level is above a certain value. I don't know much about AVAudio yet, so I'm asking if you have an idea on how to do this (or are planning to do it) before I dive into docs :)

    Thanks

    opened by glaurent 4
  • 1.  Update to swift 5

    1. Update to swift 5

    1.Description of the problem: When I use pod to import the library, the swift version was wrong and I had to manually adjust a lot of things to work。

    2. Fix Update swift version to swift5, and fix warning in project。 You can now run projects directly after pod install without having to manually fix errors and adjust swift versions

    I think author should add a new pod version 5.0.0 😊

    xcode version: 12.5.1 swift version: 5

    opened by wlixcc 3
  • Beethoven crashes sometimes when stopping PitchEngine

    Beethoven crashes sometimes when stopping PitchEngine

    First of all, great library -- it's really helpful. One problem is puzzling me right now:

    screen shot 2017-12-24 at 7 32 57 pm

    I get this error sometimes (NOT ALWAYS) when I try to stop the pitch engine, which leads me to think it's some sort of memory issue.

    The offending code:

    screen shot 2017-12-24 at 7 32 06 pm

    Any advice regarding this (especially if the problem's on my end) would be appreciated!

    opened by austinchandra 2
  • Update for Swift 3.1?

    Update for Swift 3.1?

    Hi @vadymmarkov, It's been a while since I've tried to compile my app that depends on this framework, and I'm getting an error when I try to import Pitchy: "Module compiled with Swift 3.0.2 cannot be imported in Swift 3.1"

    screen shot 2017-06-20 at 2 31 24 pm

    I also tried updating Beethoven with Carthage and got the following error:

    carthage update beethoven
    *** Fetching Beethoven
    *** Fetching Pitchy
    *** Fetching Quick
    *** Fetching Nimble
    *** Checking out Nimble at "v5.1.1"
    *** Checking out Quick at "v1.0.0"
    *** Checking out Pitchy at "2.0.1"
    *** Checking out Beethoven at "3.0.1"
    *** xcodebuild output can be found in /var/folders/bm/25m_zbpn1ys21pt5xc004p6h0000gp/T/carthage-xcodebuild.PYOJL6.log
    *** Building scheme "Nimble-iOS" in Nimble.xcodeproj
    *** Building scheme "Nimble-tvOS" in Nimble.xcodeproj
    ** BUILD FAILED **
    
    
    The following build commands failed:
    	CompileSwift normal x86_64
    	CompileSwiftSources normal x86_64 com.apple.xcode.tools.swift.compiler
    (2 failures)
    

    I'm running Xcode 8.3.3

    Any change you could compile and rerelease for Swift 3.1?

    Thanks!

    -Paul

    opened by PCrompton 2
  • Refactor/transform strategy

    Refactor/transform strategy

    There is a list of changes that I'm planning to include in the next major release.

    1. Add Travis.
    2. Use SwiftLint to enforce Swift style and conventions.
    3. Refactor project structure and code.
    4. Remove public and open access modifiers to make public API cleaner.
    5. Breaking API change: Remove transform from Config, as @glaurent suggested. From now transformer should be specified in order to conform to Estimator protocol:
    protocol Estimator {
      var transformer: Transformer { get }
      // ...
    }
    
    1. Update README.
    opened by vadymmarkov 2
  • Question: why is the AudioSession category 'playAndRecord', instead of just 'record' ?

    Question: why is the AudioSession category 'playAndRecord', instead of just 'record' ?

    Hello. First of all I want to thank you for developing such an amazing library.

    I was going through the source code (inside the Source folder) to learn how the library is implemented. I understand all the source code and what's going one, except for two questions I've got:

    1. Check here. Why is the AudioSession category playAndRecord ? I mean, this library doesn't do playback at all. However, if I change the category to record only, an error is thrown at runtime.

    2. Check here. What is going on? The comment says //Check input type .... but it's clearly referencing currentRoute.outputs.... so is it input or output? I checked Apple documentation. It says this about overrideOutputAudioPort:

    If your app uses the playAndRecord category, calling this method with the AVAudioSession.PortOverride.speaker option causes the system to route audio to the built-in speaker and microphone regardless of other settings. This change remains in effect only until the current route changes or you call this method again with the AVAudioSession.PortOverride.none option.

    I understand the intent of the code. If we don't have any headphones (with microphone) plugged in, force the system to use the built-in mic. However, I think this falls back to these questions: 1. isn't this the default behaviour? and 2. why use category playAndRecord ?

    Thanks!

    opened by HectorRicardo 0
  • Getting result is too quick with multiple frequency  why not getting correct frequency,

    Getting result is too quick with multiple frequency why not getting correct frequency,

    When I play any tune there are call back method are called too many times why not call in once with correct frequency . It call to many time with diffrent frequencies.

    opened by tikamsingh 0
  • use in Unity

    use in Unity

    Hi there ! Sorry i am new to this, but I am desperate in finding a good solution for my pitch detection game made with unity. I would love to use Beethoven in Unity if there is a way to do so ? Any advice would be much appreciated. best,

    opened by GameOnGame 0
  • fix: sets `SWIFT_VERSION` to `4.0`

    fix: sets `SWIFT_VERSION` to `4.0`

    I can't seem to build in Xcode 11 with Beethoven 4.0.2 installed via cocoapods.

    It seems like SWIFT_VERSION was the issue.

    Repo steps: use pod 'Beethoven' do pod update build via Xcode 11 -> Error: unsuppored SWIFT_VERSION 3...

    No error when using the fork with the changes from this PR: pod 'Beethoven', :git => 'https://github.com/fetzig/Beethoven.git'

    opened by fetzig 0
  • Maintained?

    Maintained?

    This doesn't seem to be maintained anymore. Recommendations for suitable replacements would be helpful in this request & to leave open for discoverability. And/or add to README documentation

    opened by thejeff77 0
Releases(5.0.0)
  • 5.0.0(Sep 6, 2021)

  • 4.0.2(Jan 11, 2018)

  • 4.0.1(Jan 5, 2018)

  • 4.0.0(Oct 10, 2017)

  • 3.0.1(Nov 22, 2016)

  • 3.0.0(Oct 25, 2016)

    • YIN algorithm by @glaurent https://github.com/vadymmarkov/Beethoven/pull/39
    • Example project in Swift 3 by @glaurent https://github.com/vadymmarkov/Beethoven/pull/39
    • Breaking changes in config, see https://github.com/vadymmarkov/Beethoven/pull/40
    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(Sep 15, 2016)

  • 1.1.1(Sep 4, 2016)

    • Fix InputSignalTracker.stop() https://github.com/vadymmarkov/Beethoven/issues/31
    • Fix HPSEstimator.estimateLocation https://github.com/vadymmarkov/Beethoven/pull/30

    Thanks @glaurent for contribution!

    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Jul 14, 2016)

    "Signal level threshold" feature which lets the user set a signal level below which no pitch detection is attempted. Thanks @glaurent who made it happen https://github.com/vadymmarkov/Beethoven/pull/26

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Jan 12, 2016)

    • Audio signal tracking with AVAudioEngine and audio nodes (Available in iOS 8.0 and later).
    • Pre-processing of audio buffer by one of the available "transformers", to convert AVAudioPCMBuffer object to the array of floating numbers (with possible optimizations).
    • Pitch estimation.
    Source code(tar.gz)
    Source code(zip)
Owner
Vadym Markov
iOS Software Engineer
Vadym Markov
Swift audio synthesis, processing, & analysis platform for iOS, macOS and tvOS

AudioKit AudioKit is an audio synthesis, processing, and analysis platform for iOS, macOS (including Catalyst), and tvOS. Installation To add AudioKit

AudioKit 8.7k Sep 30, 2021
AudioKit is an audio synthesis, processing, and analysis platform for iOS, macOS, and tvOS.

AudioKit is an audio synthesis, processing, and analysis platform for iOS, macOS (including Catalyst), and tvOS. Installation To add AudioKit

AudioKit 9.5k Dec 31, 2022
The Amazing Audio Engine is a sophisticated framework for iOS audio applications, built so you don't have to.

Important Notice: The Amazing Audio Engine has been retired. See the announcement here The Amazing Audio Engine The Amazing Audio Engine is a sophisti

null 523 Nov 12, 2022
AudiosPlugin is a Godot iOS Audio Plugin that resolves the audio recording issue in iOS for Godot Engine.

This plugin solves the Godot game engine audio recording and playback issue in iOS devices. Please open the Audios Plugin XCode Project and compile the project. You can also use the libaudios_plugin.a binary in your project.

null 3 Dec 22, 2022
Extensions and classes in Swift that make it easy to get an iOS device reading and processing MIDI data

MorkAndMIDI A really thin Swift layer on top of CoreMIDI that opens a virtual MIDI destination and port and connects to any MIDI endpoints that appear

Brad Howes 11 Nov 5, 2022
TuningFork is a simple utility for processing microphone input and interpreting pitch, frequency, amplitude, etc.

Overview TuningFork is a simple utility for processing microphone input and interpreting pitch, frequency, amplitude, etc. TuningFork powers the Parti

Comyar Zaheri 419 Dec 23, 2022
YiVideoEditor is a library for rotating, cropping, adding layers (watermark) and as well as adding audio (music) to the videos.

YiVideoEditor YiVideoEditor is a library for rotating, cropping, adding layers (watermark) and as well as adding audio (music) to the videos. YiVideoE

coderyi 97 Dec 14, 2022
A drop-in universal library allows to record audio within the app with a nice User Interface.

IQAudioRecorderController IQAudioRecorderController is a drop-in universal library allows to record and crop audio within the app with a nice User Int

Mohd Iftekhar Qurashi 637 Nov 17, 2022
Subsonic is a small library that makes it easier to play audio with SwiftUI

Subsonic is a small library that makes it easier to play audio with SwiftUI

Paul Hudson 218 Dec 16, 2022
This app demonstrates how to use the Google Cloud Speech API and Apple on-device Speech library to recognize speech in live recorded audio.

SpeechRecognitionIOS This app demonstrates how to use Google Cloud Speech API and Apple on-device Speech library to recognize speech in live audio rec

Josh Uvi 0 Mar 11, 2022
SwiftAudioPlayer - Swift-based audio player with AVAudioEngine as its base

SwiftAudioPlayer Swift-based audio player with AVAudioEngine as its base. Allows for: streaming online audio, playing local file, changing audio speed

null 417 Jan 7, 2023
Voice Memos is an audio recorder App for iPhone and iPad that covers some of the new technologies and APIs introduced in iOS 8 written in Swift.

VoiceMemos Voice Memos is a voice recorder App for iPhone and iPad that covers some of the new technologies and APIs introduced in iOS 8 written in Sw

Zhouqi Mo 322 Aug 4, 2022
Multiple Audio Player With Swift

MultipleAudioPlayer I needed to play sequential audio files in a number of places so I made a package. Usage There are 2 inits, one takes filenames fr

chris cieslak 1 Apr 14, 2022
Functional DSP / Audio Framework for Swift

Lullaby Lullaby is an audio synthesis framework for Swift that supports both macOS and Linux! It was inspired by other audio environments like FAUST,

Jae 16 Nov 5, 2022
KeyAudioManager - A swift package to make it a lot easier to play audio in your app

KeyAudioManager A swift package to make it a lot easier to play audio in your ap

Pedro Esli 3 Apr 28, 2022
AudioPlayer is syntax and feature sugar over AVPlayer. It plays your audio files (local & remote).

AudioPlayer AudioPlayer is a wrapper around AVPlayer. It also offers cool features such as: Quality control based on number of interruption (buffering

Kevin Delannoy 676 Dec 25, 2022
AudioPlayer is a simple class for playing audio in iOS, macOS and tvOS apps.

AudioPlayer AudioPlayer is a simple class for playing audio in iOS, macOS and tvOS apps.

Tom Baranes 260 Nov 27, 2022
FDWaveformView is an easy way to display an audio waveform in your app

FDWaveformView is an easy way to display an audio waveform in your app. It is a nice visualization to show a playing audio file or to select a position in a file.

William Entriken 1.1k Dec 21, 2022
App for adding and listening audio files

SomeSa SomeSa (самса) – приложение, позволяющее загружать и воспроизводить произвольные аудиофайлы. Протестировано на форматах файлов .wav и .mp3, раз

Yegor Dobrodeyev 0 Nov 7, 2021