Module MusistrataRenderer.SoundFontsLoader
Contains the SoundFontsLoader and SoundFontInstrument classes, which handle loading and caching samples for Samples-based instruments.
Expand source code
"""
Contains the SoundFontsLoader and SoundFontInstrument classes, which handle loading and caching samples for Samples-based instruments.
"""
from typing import Dict
from SoundFontsData import SOUNDFONT_FILES, SOUNDFONT_SETTINGS
from SoundFontsDataController import SoundFontsDataController as sfdc
from MusiStrata.Components import Song, Track, Bar, SoundEvent, Note
from MusiStrata import MidoConverter
from AudioUtils import PanMonoAudio
import Settings as SETTINGS
import librosa
import os
import numpy as np
# use a library to render sample and load it?
# should use this instead of midi2audio? https://github.com/nwhitehead/pyfluidsynth
class SoundFontInstrument(object):
"""
Class handling loading of samples for a soundfont instrument
"""
def __init__(self, nameInstrument: str, maxSample: int = 10):
self.mNameInstrument = nameInstrument
self.kSettings = sfdc.GetSettingsInstrument(nameInstrument)
self.mSamples = {}
def LoadSample(self, musistrataHeight: int) -> None:
"""
Load a sample in memory. Calls self.GenerateSample if the sample has not been extracted from the soundfont
"""
# Check if sample already generated, else generate it then load it
fileName = self.mNameInstrument + "_" + str(musistrataHeight - 12) + ".wav"
if (not os.path.exists(SETTINGS.SOUNDFONTS_SAMPLES_FOLDER + "/" + fileName)):
self.GenerateSample(musistrataHeight)
y, _ = librosa.load(SETTINGS.SOUNDFONTS_SAMPLES_FOLDER + "/" + fileName, sr=None, mono=True)
# audio is mono, so panning to get stereo
y = PanMonoAudio(y, 0.5)
self.mSamples[musistrataHeight] = y
def __call__(self, musistrataHeight: int) -> np.ndarray:
"""
Get instrument sample for given height
"""
if musistrataHeight not in self.mSamples.keys():
self.LoadSample(musistrataHeight)
return self.mSamples[musistrataHeight]
def GenerateSample(self, musistrataHeight: int) -> None:
"""
Extract sample from soundfont by calling self.GenerateMidi to generate a midi and rendering it to wav using fluidsynth + sox.
"""
baseFileName = self.mNameInstrument + "_" + str(musistrataHeight - 12)
self.GenerateMidi(musistrataHeight)
command_fluidsynth = "fluidsynth -T raw -F " + "Temp/temp " + SETTINGS.SOUNDFONTS_FOLDER + "/" + self.kSettings["File"] + " " + SETTINGS.TEMPORARY_MIDI_FOLDER + "/" + baseFileName + ".mid"
command_sox = "sox -t raw -r 44100 -e signed -b 16 -c 1 -v 15 Temp/temp " + SETTINGS.SOUNDFONTS_SAMPLES_FOLDER + "/" + baseFileName + ".wav " + "speed 2"
command = command_fluidsynth + " && " + command_sox
os.system(command)
def GenerateMidi(self, musistrataHeight: int) -> None:
"""
Generate a midi for a single note using Musistrata and instrument settings.
"""
n = Note.FromHeight(musistrataHeight)
b = Bar([SoundEvent(0.0, SETTINGS.SOUNDFONT_SAMPLE_DURATION, n)])
t = Track(Instrument=self.kSettings["SoundFontSettings"]["ChannelID"], BankUsed=self.kSettings["SoundFontSettings"]["BankID"], Bars=[b])
s = Song(Tempo=60, BeatsPerBar=SETTINGS.SOUNDFONT_SAMPLE_DURATION, Tracks=[t])
baseFileName = self.mNameInstrument + "_" + str(musistrataHeight - 12)
MidoConverter.ConvertSong(s, SETTINGS.TEMPORARY_MIDI_FOLDER + "/" + baseFileName + ".mid")
class SoundFontsLoader(object):
"""
Class handling loading and caching of soundfont-based instruments
"""
def __init__(self, nbSamplesMaxPerInstrument: int = 10):
self.mMaxSamples = nbSamplesMaxPerInstrument
self.mInstruments = {}
def __call__(self, instrumentName: str, musistrataHeight: int) -> np.ndarray:
"""
Get sample for a named instrument at given height.
"""
return self.GetInstrument(instrumentName)(musistrataHeight)
def GetInstrument(self, instrumentName: str) -> SoundFontInstrument:
"""
Get the SoundFontInstrument object associated to an instrument name.
"""
if instrumentName not in self.mInstruments.keys():
self.mInstruments[instrumentName] = SoundFontInstrument(instrumentName)
return self.mInstruments[instrumentName]
def GetSettingsInstrument(self, instrumentName: str) -> Dict:
"""
Get settings associated with a given instrument name.
"""
if instrumentName not in self.mInstruments.keys():
self.mInstruments[instrumentName] = SoundFontInstrument(instrumentName)
return self.mInstruments[instrumentName].kSettings["InstrumentSettings"]
Classes
class SoundFontInstrument (nameInstrument: str, maxSample: int = 10)
-
Class handling loading of samples for a soundfont instrument
Expand source code
class SoundFontInstrument(object): """ Class handling loading of samples for a soundfont instrument """ def __init__(self, nameInstrument: str, maxSample: int = 10): self.mNameInstrument = nameInstrument self.kSettings = sfdc.GetSettingsInstrument(nameInstrument) self.mSamples = {} def LoadSample(self, musistrataHeight: int) -> None: """ Load a sample in memory. Calls self.GenerateSample if the sample has not been extracted from the soundfont """ # Check if sample already generated, else generate it then load it fileName = self.mNameInstrument + "_" + str(musistrataHeight - 12) + ".wav" if (not os.path.exists(SETTINGS.SOUNDFONTS_SAMPLES_FOLDER + "/" + fileName)): self.GenerateSample(musistrataHeight) y, _ = librosa.load(SETTINGS.SOUNDFONTS_SAMPLES_FOLDER + "/" + fileName, sr=None, mono=True) # audio is mono, so panning to get stereo y = PanMonoAudio(y, 0.5) self.mSamples[musistrataHeight] = y def __call__(self, musistrataHeight: int) -> np.ndarray: """ Get instrument sample for given height """ if musistrataHeight not in self.mSamples.keys(): self.LoadSample(musistrataHeight) return self.mSamples[musistrataHeight] def GenerateSample(self, musistrataHeight: int) -> None: """ Extract sample from soundfont by calling self.GenerateMidi to generate a midi and rendering it to wav using fluidsynth + sox. """ baseFileName = self.mNameInstrument + "_" + str(musistrataHeight - 12) self.GenerateMidi(musistrataHeight) command_fluidsynth = "fluidsynth -T raw -F " + "Temp/temp " + SETTINGS.SOUNDFONTS_FOLDER + "/" + self.kSettings["File"] + " " + SETTINGS.TEMPORARY_MIDI_FOLDER + "/" + baseFileName + ".mid" command_sox = "sox -t raw -r 44100 -e signed -b 16 -c 1 -v 15 Temp/temp " + SETTINGS.SOUNDFONTS_SAMPLES_FOLDER + "/" + baseFileName + ".wav " + "speed 2" command = command_fluidsynth + " && " + command_sox os.system(command) def GenerateMidi(self, musistrataHeight: int) -> None: """ Generate a midi for a single note using Musistrata and instrument settings. """ n = Note.FromHeight(musistrataHeight) b = Bar([SoundEvent(0.0, SETTINGS.SOUNDFONT_SAMPLE_DURATION, n)]) t = Track(Instrument=self.kSettings["SoundFontSettings"]["ChannelID"], BankUsed=self.kSettings["SoundFontSettings"]["BankID"], Bars=[b]) s = Song(Tempo=60, BeatsPerBar=SETTINGS.SOUNDFONT_SAMPLE_DURATION, Tracks=[t]) baseFileName = self.mNameInstrument + "_" + str(musistrataHeight - 12) MidoConverter.ConvertSong(s, SETTINGS.TEMPORARY_MIDI_FOLDER + "/" + baseFileName + ".mid")
Methods
def GenerateMidi(self, musistrataHeight: int) ‑> NoneType
-
Generate a midi for a single note using Musistrata and instrument settings.
Expand source code
def GenerateMidi(self, musistrataHeight: int) -> None: """ Generate a midi for a single note using Musistrata and instrument settings. """ n = Note.FromHeight(musistrataHeight) b = Bar([SoundEvent(0.0, SETTINGS.SOUNDFONT_SAMPLE_DURATION, n)]) t = Track(Instrument=self.kSettings["SoundFontSettings"]["ChannelID"], BankUsed=self.kSettings["SoundFontSettings"]["BankID"], Bars=[b]) s = Song(Tempo=60, BeatsPerBar=SETTINGS.SOUNDFONT_SAMPLE_DURATION, Tracks=[t]) baseFileName = self.mNameInstrument + "_" + str(musistrataHeight - 12) MidoConverter.ConvertSong(s, SETTINGS.TEMPORARY_MIDI_FOLDER + "/" + baseFileName + ".mid")
def GenerateSample(self, musistrataHeight: int) ‑> NoneType
-
Extract sample from soundfont by calling self.GenerateMidi to generate a midi and rendering it to wav using fluidsynth + sox.
Expand source code
def GenerateSample(self, musistrataHeight: int) -> None: """ Extract sample from soundfont by calling self.GenerateMidi to generate a midi and rendering it to wav using fluidsynth + sox. """ baseFileName = self.mNameInstrument + "_" + str(musistrataHeight - 12) self.GenerateMidi(musistrataHeight) command_fluidsynth = "fluidsynth -T raw -F " + "Temp/temp " + SETTINGS.SOUNDFONTS_FOLDER + "/" + self.kSettings["File"] + " " + SETTINGS.TEMPORARY_MIDI_FOLDER + "/" + baseFileName + ".mid" command_sox = "sox -t raw -r 44100 -e signed -b 16 -c 1 -v 15 Temp/temp " + SETTINGS.SOUNDFONTS_SAMPLES_FOLDER + "/" + baseFileName + ".wav " + "speed 2" command = command_fluidsynth + " && " + command_sox os.system(command)
def LoadSample(self, musistrataHeight: int) ‑> NoneType
-
Load a sample in memory. Calls self.GenerateSample if the sample has not been extracted from the soundfont
Expand source code
def LoadSample(self, musistrataHeight: int) -> None: """ Load a sample in memory. Calls self.GenerateSample if the sample has not been extracted from the soundfont """ # Check if sample already generated, else generate it then load it fileName = self.mNameInstrument + "_" + str(musistrataHeight - 12) + ".wav" if (not os.path.exists(SETTINGS.SOUNDFONTS_SAMPLES_FOLDER + "/" + fileName)): self.GenerateSample(musistrataHeight) y, _ = librosa.load(SETTINGS.SOUNDFONTS_SAMPLES_FOLDER + "/" + fileName, sr=None, mono=True) # audio is mono, so panning to get stereo y = PanMonoAudio(y, 0.5) self.mSamples[musistrataHeight] = y
class SoundFontsLoader (nbSamplesMaxPerInstrument: int = 10)
-
Class handling loading and caching of soundfont-based instruments
Expand source code
class SoundFontsLoader(object): """ Class handling loading and caching of soundfont-based instruments """ def __init__(self, nbSamplesMaxPerInstrument: int = 10): self.mMaxSamples = nbSamplesMaxPerInstrument self.mInstruments = {} def __call__(self, instrumentName: str, musistrataHeight: int) -> np.ndarray: """ Get sample for a named instrument at given height. """ return self.GetInstrument(instrumentName)(musistrataHeight) def GetInstrument(self, instrumentName: str) -> SoundFontInstrument: """ Get the SoundFontInstrument object associated to an instrument name. """ if instrumentName not in self.mInstruments.keys(): self.mInstruments[instrumentName] = SoundFontInstrument(instrumentName) return self.mInstruments[instrumentName] def GetSettingsInstrument(self, instrumentName: str) -> Dict: """ Get settings associated with a given instrument name. """ if instrumentName not in self.mInstruments.keys(): self.mInstruments[instrumentName] = SoundFontInstrument(instrumentName) return self.mInstruments[instrumentName].kSettings["InstrumentSettings"]
Methods
def GetInstrument(self, instrumentName: str) ‑> SoundFontInstrument
-
Get the SoundFontInstrument object associated to an instrument name.
Expand source code
def GetInstrument(self, instrumentName: str) -> SoundFontInstrument: """ Get the SoundFontInstrument object associated to an instrument name. """ if instrumentName not in self.mInstruments.keys(): self.mInstruments[instrumentName] = SoundFontInstrument(instrumentName) return self.mInstruments[instrumentName]
def GetSettingsInstrument(self, instrumentName: str) ‑> Dict
-
Get settings associated with a given instrument name.
Expand source code
def GetSettingsInstrument(self, instrumentName: str) -> Dict: """ Get settings associated with a given instrument name. """ if instrumentName not in self.mInstruments.keys(): self.mInstruments[instrumentName] = SoundFontInstrument(instrumentName) return self.mInstruments[instrumentName].kSettings["InstrumentSettings"]