Skip to main content

Python bindings for FluidSynth, a MIDI synthesizer that uses SoundFont instruments

Project description

pyFluidSynth

Python bindings for FluidSynth

This package contains Python bindings for FluidSynth. FluidSynth is a software synthesizer for generating music. It works like a MIDI synthesizer. You load patches, set parameters, then send NOTEON and NOTEOFF events to play notes. Instruments are defined in SoundFonts, generally files with the extension SF2. FluidSynth can either be used to play audio itself, or you can call a function that returns chunks of audio data and output the data to the soundcard yourself. FluidSynth works on all major platforms, so pyFluidSynth should also.

GitHub Actions Workflow Status | PyPI - Version | PyPI - Python Versions |

Requirements

FluidSynth (2.0.0 or later)

(optional) NumPy 1.0 or later for some features

NOTE: If you don't need all the features of FluidSynth you may be interested in tinysoundfont-pybind which is a self-contained Python package that includes TinySoundFont for SoundFont playback and is permissively licensed.

Installation

To use the latest official release:

pip install pyfluidsynth

Pre-release Versions

To use pre-release versions of this package, clone this repository, go to the repository directory, then do:

pip install --editable .

Example

Here is a program that plays a chord for a second.

import time
import fluidsynth

fs = fluidsynth.Synth()
fs.start()

sfid = fs.sfload("example.sf2")
fs.program_select(0, sfid, 0, 0)

fs.noteon(0, 60, 30)
fs.noteon(0, 67, 30)
fs.noteon(0, 76, 30)

time.sleep(1.0)

fs.noteoff(0, 60)
fs.noteoff(0, 67)
fs.noteoff(0, 76)

time.sleep(1.0)

fs.delete()

First a Synth object is created to control playback. The start() method starts audio output in a separate thread.

To get sound, you need to choose an instrument. First load a SoundFont with sfload(), then select a bank and preset with program_select().

program_select(track, soundfontid, banknum, presetnum)

To start a note, use the noteon() method.

noteon(track, midinum, velocity)

To stop a note, use noteoff().

noteoff(track, midinum)

Managing Audio

You can also manage audio IO yourself and just use FluidSynth to calculate the samples for the music. You might do this, for example, in a game with WAV sound effects and algorithmically generated music. To do this, create the Synth object but don't call start(). To generate the next chunk of audio, call get_samples().

get_samples(len)

The length you pass will be the number of audio samples. Unless specified otherwise, FluidSynth assumes an output rate of 44100 Hz. The return value will be a Numpy array of samples. By default FluidSynth generates stereo sound, so the return array will be length 2 * len.

To join arrays together, use numpy.append().

To convert an array of samples into a string of bytes suitable for sending to the soundcard, use fluidsynth.raw_audio_string(samples).

Here is an example that generates a chord then plays the data using PyAudio.

import time
import numpy
import pyaudio
import fluidsynth

pa = pyaudio.PyAudio()
strm = pa.open(
    format = pyaudio.paInt16,
    channels = 2, 
    rate = 44100, 
    output = True)

s = []

fl = fluidsynth.Synth()

# Initial silence is 1 second
s = numpy.append(s, fl.get_samples(44100 * 1))

sfid = fl.sfload("example.sf2")
fl.program_select(0, sfid, 0, 0)

fl.noteon(0, 60, 30)
fl.noteon(0, 67, 30)
fl.noteon(0, 76, 30)

# Chord is held for 2 seconds
s = numpy.append(s, fl.get_samples(44100 * 2))

fl.noteoff(0, 60)
fl.noteoff(0, 67)
fl.noteoff(0, 76)

# Decay of chord is held for 1 second
s = numpy.append(s, fl.get_samples(44100 * 1))

fl.delete()

samps = fluidsynth.raw_audio_string(s)

print(len(samps))
print('Starting playback')
strm.write(samps)

Using the Sequencer

You can create a sequencer as follows:

import fluidsynth

seq = fluidsynth.Sequencer()

This will by default create a sequencer that will advance at a rate of 1000 ticks per second. To change the rate at which the sequencer advances, you can give it the optional time_scale parameter. As a clock source, it will use your system clock. In order to manually advance the sequencer, you can give it the parameter use_system_timer=False. You will then have to advance it using sequencer.process.

In order to make the sequencer aware of your synthesizer, you have to register it:

fs = fluidsynth.Synth()
# init and start the synthesizer as described above…

synth_id = seq.register_fluidsynth(fs)

You have to keep synth_id and use it as a target for the midi events you want to schedule. Now, you can sequence actual notes:

seq.note_on(time=500, absolute=False, channel=0, key=60, velocity=80, dest=synth_id)

If you use relative timing like above, the sequencer will schedule the event the specified time from the current position. Otherwise, if absolute is True (the default), you have to use absolute track positions (in ticks). So the following code snippet will do the same as the one above:

current_time = seq.get_tick()
seq.note_on(current_time + 500, 0, 60, 80, dest=synth_id)

You can also register your own callback functions to be called at certain ticks:

def seq_callback(time, event, seq, data):
    print('callback called!')

callback_id = sequencer.register_client("myCallback", seq_callback)

sequencer.timer(current_time + 2000, dest=callback_id)

Note that event and seq are low-level objects, not actual Python objects.

You can find a complete example (inspired by this one from the fluidsynth library) in the test folder.

Bugs and Limitations

Not all functions in FluidSynth are bound.

Not much error checking, FluidSynth will segfault/crash if you call the functions incorrectly sometimes.

Authors

This project was originally created by Nathan Whitehead nwhitehe@gmail.com but is the work of many. See CONTRIBUTORS.

License

Released under the LGPL v2.1 or any later version (this is the same as FluidSynth).

Copyright 2008--2024, Nathan Whitehead and contributors.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyfluidsynth-1.3.4.tar.gz (30.8 kB view details)

Uploaded Source

Built Distribution

pyfluidsynth-1.3.4-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

File details

Details for the file pyfluidsynth-1.3.4.tar.gz.

File metadata

  • Download URL: pyfluidsynth-1.3.4.tar.gz
  • Upload date:
  • Size: 30.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for pyfluidsynth-1.3.4.tar.gz
Algorithm Hash digest
SHA256 ca741c262b72e48963149c73bf8ae80e45c84c23c9bdf803a26ba324cbb2d5b2
MD5 632c55eabf7dd12a8b839b75ca468660
BLAKE2b-256 7370fbdd07c84d98c3d4382f4e51bdaa2cf1424a30c41f655ff43c1b2c1d3441

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyfluidsynth-1.3.4.tar.gz:

Publisher: ci.yml on nwhitehead/pyfluidsynth

Attestations:

File details

Details for the file pyfluidsynth-1.3.4-py3-none-any.whl.

File metadata

  • Download URL: pyfluidsynth-1.3.4-py3-none-any.whl
  • Upload date:
  • Size: 22.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for pyfluidsynth-1.3.4-py3-none-any.whl
Algorithm Hash digest
SHA256 c6990329db7cfb35f5e65d523dd4f0c971d928e70df3a6bceec8864827edf246
MD5 d1374ff2f96155e7587ae9cb11a4291c
BLAKE2b-256 c4914f6b28ac379da306dde66ba6ac170c4a6e7e1506cadc84a9359fe3f237ba

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyfluidsynth-1.3.4-py3-none-any.whl:

Publisher: ci.yml on nwhitehead/pyfluidsynth

Attestations:

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page