Merge pull request #6335 from igorkorsukov/mu4/audio_midi

[MU4] Audio midi
This commit is contained in:
Igor Korsukov 2020-07-20 15:08:19 +02:00 committed by GitHub
commit 0231e81024
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
87 changed files with 23294 additions and 63 deletions

View file

@ -26,6 +26,7 @@
# set(MODULE_QRC somename.qrc) - set resource (qrc) file
# set(MODULE_UI ...) - set ui headers
# set(MODULE_QML_IMPORT ...) - set Qml import for QtCreator (so that there is code highlighting, jump, etc.)
# set(MODULE_HAS_C_CODE, 1) - set if source contains C code
# After all the settings you need to do:
# include(${PROJECT_SOURCE_DIR}/build/module.cmake)
@ -77,19 +78,28 @@ target_link_libraries(${MODULE}
${MODULE_LINK}
)
if (NOT MSVC)
set_target_properties (${MODULE} PROPERTIES COMPILE_FLAGS "${PCH_INCLUDE} -g -Wall -Wextra -Winvalid-pch")
else (NOT MSVC)
set_target_properties (${MODULE} PROPERTIES COMPILE_FLAGS "${PCH_INCLUDE}" )
endif (NOT MSVC)
if (MODULE_HAS_C_CODE)
xcode_pch(${MODULE} all)
set_target_properties( ${MODULE} PROPERTIES COMPILE_FLAGS "-fPIC")
# Use MSVC pre-compiled headers
vstudio_pch( ${MODULE} )
else(MODULE_HAS_C_CODE)
if (NOT MSVC)
set_target_properties (${MODULE} PROPERTIES COMPILE_FLAGS "${PCH_INCLUDE} -g -Wall -Wextra -Winvalid-pch")
else (NOT MSVC)
set_target_properties (${MODULE} PROPERTIES COMPILE_FLAGS "${PCH_INCLUDE}" )
endif (NOT MSVC)
xcode_pch(${MODULE} all)
# Use MSVC pre-compiled headers
vstudio_pch( ${MODULE} )
# MSVC does not depend on mops1 & mops2 for PCH
if (NOT MSVC)
ADD_DEPENDENCIES(${MODULE} mops1)
ADD_DEPENDENCIES(${MODULE} mops2)
endif (NOT MSVC)
endif()
# MSVC does not depend on mops1 & mops2 for PCH
if (NOT MSVC)
ADD_DEPENDENCIES(${MODULE} mops1)
ADD_DEPENDENCIES(${MODULE} mops2)
endif (NOT MSVC)

View file

@ -7,6 +7,7 @@ add_subdirectory(shortcuts)
add_subdirectory(workspace)
add_subdirectory(audio/engine)
add_subdirectory(audio/midi)
if (BUILD_TELEMETRY_MODULE)
add_subdirectory(telemetry)

View file

@ -47,12 +47,17 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/iaudioengine.h
${CMAKE_CURRENT_LIST_DIR}/iaudiosource.h
${CMAKE_CURRENT_LIST_DIR}/audioerrors.h
${CMAKE_CURRENT_LIST_DIR}/iaudioplayer.h
${CMAKE_CURRENT_LIST_DIR}/sinesource.cpp
${CMAKE_CURRENT_LIST_DIR}/sinesource.h
${CMAKE_CURRENT_LIST_DIR}/midisource.cpp
${CMAKE_CURRENT_LIST_DIR}/midisource.h
${CMAKE_CURRENT_LIST_DIR}/internal/audioengine.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/audioengine.h
${CMAKE_CURRENT_LIST_DIR}/internal/audioplayer.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/audioplayer.h
${CMAKE_CURRENT_LIST_DIR}/devtools/audioenginedevtools.cpp
${CMAKE_CURRENT_LIST_DIR}/devtools/audioenginedevtools.h
${CMAKE_CURRENT_LIST_DIR}/devtools/sinestream.cpp
${CMAKE_CURRENT_LIST_DIR}/devtools/sinestream.h
)
set(MODULE_DEF

View file

@ -22,6 +22,7 @@
#include "modularity/ioc.h"
#include "internal/audioengine.h"
#include "internal/audioplayer.h"
#include "devtools/audioenginedevtools.h"
@ -45,6 +46,7 @@ std::string AudioEngineModule::moduleName() const
void AudioEngineModule::registerExports()
{
framework::ioc()->registerExport<IAudioEngine>(moduleName(), audioEngine);
framework::ioc()->registerExport<IAudioPlayer>(moduleName(), new AudioPlayer());
#ifdef Q_OS_LINUX
framework::ioc()->registerExport<IAudioDriver>(moduleName(), new LinuxAudioDriver());

View file

@ -19,6 +19,7 @@
#include "audioenginedevtools.h"
using namespace mu::audio::engine;
using namespace mu::audio::midi;
AudioEngineDevTools::AudioEngineDevTools(QObject* parent)
: QObject(parent)
@ -27,10 +28,73 @@ AudioEngineDevTools::AudioEngineDevTools(QObject* parent)
void AudioEngineDevTools::playSine()
{
m_sineHandle = engine()->play(&m_sineStream);
if (!m_sineSource) {
m_sineSource = std::make_shared<SineSource>();
}
m_sineHandle = audioEngine()->play(m_sineSource);
}
void AudioEngineDevTools::stopSine()
{
engine()->stop(m_sineHandle);
audioEngine()->stop(m_sineHandle);
}
void AudioEngineDevTools::playSourceMidi()
{
if (!m_midiSource) {
m_midiSource = std::make_shared<MidiSource>();
}
if (!m_midiData) {
m_midiData = makeArpeggio();
m_midiSource->init(audioEngine()->sampleRate());
m_midiSource->loadMIDI(m_midiData);
}
m_midiHandel = audioEngine()->play(m_midiSource);
}
void AudioEngineDevTools::stopSourceMidi()
{
audioEngine()->stop(m_midiHandel);
}
void AudioEngineDevTools::playPlayerMidi()
{
if (!m_midiData) {
m_midiData = makeArpeggio();
}
player()->setMidiData(m_midiData);
player()->play();
}
void AudioEngineDevTools::stopPlayerMidi()
{
player()->stop();
}
std::shared_ptr<MidiData> AudioEngineDevTools::makeArpeggio() const
{
/* notes of the arpeggio */
static std::vector<int> notes = { 60, 64, 67, 72, 76, 79, 84, 79, 76, 72, 67, 64 };
static uint64_t duration = 4440;
uint64_t note_duration = duration / notes.size();
uint64_t note_time = 0;
Channel ch;
for (int n : notes) {
ch.events.push_back(Event(note_time, ME_NOTEON, n, 100));
note_time += note_duration;
ch.events.push_back(Event(note_time, ME_NOTEOFF, n, 100));
}
Track t;
t.channels.push_back(ch);
std::shared_ptr<MidiData> data = std::make_shared<MidiData>();
data->tracks.push_back(t);
return data;
}

View file

@ -23,7 +23,9 @@
#include "modularity/ioc.h"
#include "audio/engine/iaudioengine.h"
#include "sinestream.h"
#include "audio/engine/iaudioplayer.h"
#include "sinesource.h"
#include "midisource.h"
namespace mu {
namespace audio {
@ -31,7 +33,8 @@ namespace engine {
class AudioEngineDevTools : public QObject
{
Q_OBJECT
INJECT(audio, IAudioEngine, engine)
INJECT(audio, IAudioEngine, audioEngine)
INJECT(audio, IAudioPlayer, player)
public:
explicit AudioEngineDevTools(QObject* parent = nullptr);
@ -39,10 +42,22 @@ public:
Q_INVOKABLE void playSine();
Q_INVOKABLE void stopSine();
Q_INVOKABLE void playSourceMidi();
Q_INVOKABLE void stopSourceMidi();
Q_INVOKABLE void playPlayerMidi();
Q_INVOKABLE void stopPlayerMidi();
private:
SineStream m_sineStream;
std::shared_ptr<midi::MidiData> makeArpeggio() const;
std::shared_ptr<SineSource> m_sineSource;
IAudioEngine::handle m_sineHandle = 0;
std::shared_ptr<midi::MidiData> m_midiData;
std::shared_ptr<MidiSource> m_midiSource;
IAudioEngine::handle m_midiHandel = 0;
};
}
}

View file

@ -23,6 +23,7 @@
#include "modularity/imoduleexport.h"
#include "ret.h"
#include "iaudiosource.h"
namespace mu {
@ -38,12 +39,16 @@ public:
using handle = unsigned int;
using time = float;
virtual Ret init() = 0;
virtual void deinit() = 0;
virtual bool isInited() const = 0;
virtual float sampleRate() const = 0;
virtual handle play(IAudioSource* s, float volume = -1, float pan = 0, bool paused = false) = 0;
virtual handle play(std::shared_ptr<IAudioSource> src, float volume = -1, float pan = 0, bool paused = false) = 0;
virtual void seek(time sec) = 0;
virtual void stop(handle h) = 0;
virtual void pause(handle h, bool paused) = 0;
virtual void setPause(handle h, bool paused) = 0;
virtual time position(handle h) const = 0;
virtual bool isEnded(handle h) const = 0;

View file

@ -0,0 +1,69 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_IAUDIOPLAYER_H
#define MU_AUDIO_IAUDIOPLAYER_H
#include "modularity/imoduleexport.h"
#include "retval.h"
#include "audio/midi/miditypes.h"
//! NOTE This is the main public playback control interface for consumers,
//! so namespace is just mu::audio
namespace mu {
namespace audio {
enum class PlayStatus {
UNDEFINED = 0,
STOPED,
PLAYING,
PAUSED
};
class IAudioPlayer : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(IAudioPlayer)
public:
virtual ~IAudioPlayer() = default;
virtual ValCh<PlayStatus> status() const = 0;
// data
virtual void setMidiData(std::shared_ptr<midi::MidiData> midi) = 0;
// Action
virtual bool play() = 0;
virtual void pause() = 0;
virtual void stop() = 0;
virtual void rewind() = 0;
virtual float playbackPosition() const = 0; // sec
virtual void setPlaybackPosition(float sec) = 0; // sec
// General
virtual float generalVolume() const = 0; // 0.0 to 1.0.
virtual void setGeneralVolume(float v) = 0; // 0.0 to 1.0.
virtual float generalBalance() const = 0;
virtual void setGeneralBalance(float b) = 0; // -1.0 only left, 0.0 center, 1.0 only right
};
}
}
#endif // MU_AUDIO_IAUDIOPLAYER_H

View file

@ -105,7 +105,7 @@ float AudioEngine::sampleRate() const
return m_sl->engine.getBackendSamplerate();
}
IAudioEngine::handle AudioEngine::play(IAudioSource* s, float volume, float pan, bool paused)
IAudioEngine::handle AudioEngine::play(std::shared_ptr<IAudioSource> s, float volume, float pan, bool paused)
{
IF_ASSERT_FAILED(s) {
return 0;
@ -146,7 +146,7 @@ void AudioEngine::stop(handle h)
m_sources.erase(h);
}
void AudioEngine::pause(handle h, bool paused)
void AudioEngine::setPause(handle h, bool paused)
{
LOGI() << (paused ? "pause" : "resume");

View file

@ -39,16 +39,16 @@ class AudioEngine : public IAudioEngine
public:
AudioEngine();
Ret init();
void deinit();
bool isInited() const;
Ret init() override;
void deinit() override;
bool isInited() const override;
float sampleRate() const override;
handle play(IAudioSource* s, float volume = -1, float pan = 0, bool paused = false) override;
handle play(std::shared_ptr<IAudioSource> s, float volume = -1, float pan = 0, bool paused = false) override;
void seek(time sec) override;
void stop(handle h) override;
void pause(handle h, bool paused) override;
void setPause(handle h, bool paused) override;
void syncAll(time sec);
void stopAll();
@ -64,12 +64,12 @@ private:
struct SL;
std::shared_ptr<SL> m_sl;
bool m_inited{ false };
bool m_inited = false;
struct Source {
handle handel;
IAudioSource* source{ nullptr };
bool playing{ false };
handle handel = 0;
std::shared_ptr<IAudioSource> source;
bool playing = false;
};
std::map<handle, Source> m_sources;

View file

@ -0,0 +1,238 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "audioplayer.h"
#include "log.h"
using namespace mu;
using namespace mu::audio;
using namespace mu::audio::engine;
template<class T>
static const T& clamp(const T& v, const T& lo, const T& hi)
{
return (v < lo) ? lo : (hi < v) ? hi : v;
}
AudioPlayer::AudioPlayer()
{
m_status.val = PlayStatus::UNDEFINED;
}
void AudioPlayer::setMidiData(std::shared_ptr<midi::MidiData> midi)
{
if (midi) {
m_midiSource = std::make_shared<MidiSource>();
m_midiSource->loadMIDI(midi);
m_tracks.clear();
for (size_t num = 0; num < midi->tracks.size(); ++num) {
m_tracks[num] = std::make_shared<Track>();
}
m_status.set(PlayStatus::STOPED);
} else {
m_tracks.clear();
}
}
bool AudioPlayer::play()
{
LOGD() << "try play \n";
if (m_status.val == PlayStatus::PLAYING) {
LOGW() << "already playing \n";
return true;
}
if (!doPlay()) {
LOGE() << "failed do play \n";
return false;
}
m_status.set(PlayStatus::PLAYING);
return true;
}
void AudioPlayer::pause()
{
doPause();
m_status.set(PlayStatus::PAUSED);
}
void AudioPlayer::stop()
{
doStop();
m_status.set(PlayStatus::STOPED);
}
void AudioPlayer::rewind()
{
doStop();
m_status.set(PlayStatus::STOPED);
}
bool AudioPlayer::init()
{
if (m_inited && audioEngine()->isInited()) {
return true;
}
m_inited = audioEngine()->init();
if (m_inited) {
if (m_midiSource) {
float samplerate = audioEngine()->sampleRate();
m_midiSource->init(samplerate);
}
m_inited = true;
}
return m_inited;
}
bool AudioPlayer::isInited() const
{
return m_inited;
}
bool AudioPlayer::doPlay()
{
if (!init()) {
return false;
}
if (!hasTracks()) {
return false;
}
IF_ASSERT_FAILED(m_midiSource) {
return false;
}
if (!m_midiHandle) {
m_midiHandle = audioEngine()->play(m_midiSource, -1, 0, true); // paused
}
audioEngine()->seek(m_beginPlayPosition);
audioEngine()->setPause(m_midiHandle, false);
return true;
}
void AudioPlayer::doPause()
{
m_beginPlayPosition = m_currentPlayPosition;
if (m_midiHandle) {
audioEngine()->setPause(m_midiHandle, true);
}
}
void AudioPlayer::doStop()
{
m_beginPlayPosition = 0;
m_currentPlayPosition = 0;
audioEngine()->stop(m_midiHandle);
m_midiHandle = 0;
}
float AudioPlayer::playbackPosition() const
{
if (m_status.val == PlayStatus::PLAYING) {
return m_currentPlayPosition;
}
return m_beginPlayPosition;
}
void AudioPlayer::setPlaybackPosition(float sec)
{
sec = std::max(sec, 0.f);
m_beginPlayPosition = sec;
m_currentPlayPosition = sec;
if (m_status.val == PlayStatus::PLAYING) {
audioEngine()->seek(sec);
}
}
float AudioPlayer::generalVolume() const
{
return m_generalVolume;
}
void AudioPlayer::setGeneralVolume(float v)
{
m_generalVolume = clamp(v, 0.f, 1.27f); //! NOTE 127 - midi limitation
if (!isInited()) {
return;
}
applyCurrentVolume();
}
float AudioPlayer::generalBalance() const
{
return m_generalBalance;
}
void AudioPlayer::setGeneralBalance(float b)
{
m_generalBalance = clamp(b, -1.f, 1.f);
if (!isInited()) {
return;
}
applyCurrentBalance();
}
float AudioPlayer::normalizedVolume(float volume) const
{
return clamp(m_generalVolume * volume, 0.f, 1.27f); //! NOTE 127 - midi limitation
}
float AudioPlayer::normalizedBalance(float balance) const
{
return clamp(m_generalBalance + balance, -1.f, 1.f);
}
void AudioPlayer::applyCurrentVolume()
{
for (const auto& p : m_tracks) {
m_midiSource->setTrackVolume(p.first, normalizedVolume(p.second->volume));
}
}
void AudioPlayer::applyCurrentBalance()
{
for (const auto& p : m_tracks) {
m_midiSource->setTrackBalance(p.first, normalizedBalance(p.second->balance));
}
}
bool AudioPlayer::hasTracks() const
{
return m_tracks.size() > 0;
}
ValCh<PlayStatus> AudioPlayer::status() const
{
return m_status;
}

View file

@ -0,0 +1,98 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_AUDIOPLAYER_H
#define MU_AUDIO_AUDIOPLAYER_H
#include "../iaudioplayer.h"
#include "modularity/ioc.h"
#include "../iaudioengine.h"
#include "midisource.h"
namespace mu {
namespace audio {
class AudioPlayer : public IAudioPlayer
{
INJECT(audio, engine::IAudioEngine, audioEngine)
public:
AudioPlayer();
ValCh<PlayStatus> status() const override;
// data
void setMidiData(std::shared_ptr<midi::MidiData> midi) override;
// Action
bool play() override;
void pause() override;
void stop() override;
void rewind() override;
float playbackPosition() const override;
void setPlaybackPosition(float sec) override;
// General
float generalVolume() const override;
void setGeneralVolume(float v) override;
float generalBalance() const override;
void setGeneralBalance(float b) override;
private:
struct Track {
bool muted = false;
float volume = 1.0f;
float balance = 0.0f;
};
bool init();
bool isInited() const;
bool doPlay();
void doPause();
void doStop();
bool hasTracks() const;
float normalizedVolume(float volume) const;
float normalizedBalance(float balance) const;
void applyCurrentVolume();
void applyCurrentBalance();
bool m_inited = false;
ValCh<PlayStatus> m_status;
std::shared_ptr<midi::MidiData> m_midi;
std::shared_ptr<engine::MidiSource> m_midiSource;
engine::IAudioEngine::handle m_midiHandle = 0;
float m_beginPlayPosition = 0.0f;
float m_currentPlayPosition = 0.0f;
float m_generalVolume = 1.0f;
float m_generalBalance = 0.0f;
std::map<int, std::shared_ptr<Track> > m_tracks;
};
}
}
#endif // MU_AUDIO_AUDIOPLAYER_H

View file

@ -0,0 +1,129 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "midisource.h"
#include <soloud.h>
#include "log.h"
using namespace mu::audio::engine;
using namespace mu::audio::midi;
struct MidiSource::SLInstance : public SoLoud::AudioSourceInstance {
std::shared_ptr<ISequencer> seq;
SLInstance(std::shared_ptr<ISequencer> s)
: seq(s)
{
seq->run(mStreamTime);
}
~SLInstance() override
{
seq->stop();
}
SoLoud::result seek_frame(double sec) override
{
seq->seek(sec);
return SoLoud::SO_NO_ERROR;
}
unsigned int getAudio(float* aBuffer, unsigned int aSamplesToRead, unsigned int /*aBufferSize*/) override
{
seq->getAudio(mStreamTime, aBuffer, aSamplesToRead);
return aSamplesToRead;
}
bool hasEnded() override
{
return seq->hasEnded();
}
};
struct MidiSource::SL : public SoLoud::AudioSource {
std::shared_ptr<ISequencer> seq;
~SL() override {}
SoLoud::AudioSourceInstance* createInstance() override
{
return new SLInstance(seq);
}
};
MidiSource::MidiSource()
{
m_seq = sequencer();
m_sl = std::make_shared<MidiSource::SL>();
m_sl->mChannels = 2;
m_sl->seq = m_seq;
}
void MidiSource::setSampleRate(float samplerate)
{
m_sl->mBaseSamplerate = samplerate;
}
void MidiSource::sync(float sec)
{
m_seq->seek(sec);
}
SoLoud::AudioSource* MidiSource::source()
{
return m_sl.get();
}
void MidiSource::loadMIDI(const std::shared_ptr<midi::MidiData>& midi)
{
m_seq->loadMIDI(midi);
}
void MidiSource::init(float samplerate)
{
m_seq->init(samplerate);
setSampleRate(samplerate);
}
float MidiSource::playbackSpeed() const
{
return m_seq->playbackSpeed();
}
void MidiSource::setPlaybackSpeed(float speed)
{
m_seq->setPlaybackSpeed(speed);
}
void MidiSource::setIsTrackMuted(int ti, bool mute)
{
m_seq->setIsTrackMuted(ti, mute);
}
void MidiSource::setTrackVolume(int ti, float volume)
{
m_seq->setTrackVolume(ti, volume);
}
void MidiSource::setTrackBalance(int ti, float balance)
{
m_seq->setTrackBalance(ti, balance);
}

View file

@ -0,0 +1,67 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_MIDISOURCE_H
#define MU_AUDIO_MIDISOURCE_H
#include <string>
#include <memory>
#include "iaudiosource.h"
#include "modularity/ioc.h"
#include "audio/midi/isequencer.h"
#include "audio/midi/miditypes.h"
namespace mu {
namespace audio {
namespace engine {
class MidiSource : public IAudioSource
{
INJECT(audio_engine, midi::ISequencer, sequencer)
public:
MidiSource();
void setSampleRate(float samplerate) override;
void sync(float sec) override;
SoLoud::AudioSource* source() override;
void loadMIDI(const std::shared_ptr<midi::MidiData>& midi);
void init(float samplerate);
float playbackSpeed() const;
void setPlaybackSpeed(float speed);
void setIsTrackMuted(int ti, bool mute);
void setTrackVolume(int ti, float volume);
void setTrackBalance(int ti, float balance);
private:
struct SL;
struct SLInstance;
std::shared_ptr<SL> m_sl;
std::shared_ptr<midi::ISequencer> m_seq;
};
}
}
}
#endif // MU_AUDIO_MIDISOURCE_H

View file

@ -16,7 +16,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "sinestream.h"
#include "sinesource.h"
#include <cmath>
#include <cstring>
@ -27,11 +27,11 @@
using namespace mu::audio::engine;
struct SineStream::SLInstance : public SoLoud::AudioSourceInstance {
std::shared_ptr<SineStream::Samples> samples;
struct SineSource::SLInstance : public SoLoud::AudioSourceInstance {
std::shared_ptr<SineSource::Samples> samples;
size_t position = 0;
SLInstance(std::shared_ptr<SineStream::Samples> s)
SLInstance(std::shared_ptr<SineSource::Samples> s)
: samples(s) {}
~SLInstance() override = default;
@ -60,8 +60,8 @@ struct SineStream::SLInstance : public SoLoud::AudioSourceInstance {
}
};
struct SineStream::SL : public SoLoud::AudioSource {
std::shared_ptr<SineStream::Samples> samples;
struct SineSource::SL : public SoLoud::AudioSource {
std::shared_ptr<SineSource::Samples> samples;
~SL() override = default;
SoLoud::AudioSourceInstance* createInstance() override
@ -70,7 +70,7 @@ struct SineStream::SL : public SoLoud::AudioSource {
}
};
SineStream::SineStream()
SineSource::SineSource()
{
m_samples = std::make_shared<Samples>();
@ -79,7 +79,7 @@ SineStream::SineStream()
m_sl->samples = m_samples;
}
void SineStream::generateSine(Samples& samples, float samplerate, float freq, int seconds) const
void SineSource::generateSine(Samples& samples, float samplerate, float freq, int seconds) const
{
size_t buf_size = seconds * samplerate;
samples.clear();
@ -90,18 +90,18 @@ void SineStream::generateSine(Samples& samples, float samplerate, float freq, in
}
}
void SineStream::setSampleRate(float samplerate)
void SineSource::setSampleRate(float samplerate)
{
m_sl->mBaseSamplerate = samplerate;
generateSine(*m_samples.get(), samplerate, 340.0, 10);
}
void SineStream::sync(float sec)
void SineSource::sync(float sec)
{
UNUSED(sec);
}
SoLoud::AudioSource* SineStream::source()
SoLoud::AudioSource* SineSource::source()
{
return m_sl.get();
}

View file

@ -16,22 +16,22 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_SINETREAM_H
#define MU_AUDIO_SINETREAM_H
#ifndef MU_AUDIO_SINESOURCE_H
#define MU_AUDIO_SINESOURCE_H
#include <memory>
#include <vector>
#include "../iaudiosource.h"
#include "iaudiosource.h"
namespace mu {
namespace audio {
namespace engine {
class SineStream : public IAudioSource
class SineSource : public IAudioSource
{
public:
SineStream();
~SineStream() = default;
SineSource();
~SineSource() = default;
void setSampleRate(float samplerate) override;
void sync(float sec) override;
@ -54,4 +54,4 @@ private:
}
}
#endif // MU_AUDIO_SINETREAM_H
#endif // MU_AUDIO_SINESOURCE_H

View file

@ -0,0 +1,23 @@
set(FLUIDSYNTH_DIR ${CMAKE_CURRENT_LIST_DIR}//fluidlite)
set(FLUIDSYNTH_INC ${FLUIDSYNTH_DIR}/include)
set(FLUIDSYNTH_SRC
${FLUIDSYNTH_DIR}/src/fluid_chan.c
${FLUIDSYNTH_DIR}/src/fluid_chorus.c
${FLUIDSYNTH_DIR}/src/fluid_conv.c
${FLUIDSYNTH_DIR}/src/fluid_defsfont.c
${FLUIDSYNTH_DIR}/src/fluid_dsp_float.c
${FLUIDSYNTH_DIR}/src/fluid_gen.c
${FLUIDSYNTH_DIR}/src/fluid_hash.c
${FLUIDSYNTH_DIR}/src/fluid_list.c
${FLUIDSYNTH_DIR}/src/fluid_mod.c
${FLUIDSYNTH_DIR}/src/fluid_ramsfont.c
${FLUIDSYNTH_DIR}/src/fluid_rev.c
${FLUIDSYNTH_DIR}/src/fluid_settings.c
${FLUIDSYNTH_DIR}/src/fluid_synth.c
${FLUIDSYNTH_DIR}/src/fluid_sys.c
${FLUIDSYNTH_DIR}/src/fluid_tuning.c
${FLUIDSYNTH_DIR}/src/fluid_voice.c
)

View file

@ -0,0 +1,135 @@
cmake_minimum_required(VERSION 3.1)
include(FindPkgConfig)
include(GNUInstallDirs)
project(fluidlite)
list(APPEND HEADERS
include/fluidlite.h
)
list(APPEND SCOPED_HEADERS
include/fluidsynth/types.h
include/fluidsynth/settings.h
include/fluidsynth/synth.h
include/fluidsynth/sfont.h
include/fluidsynth/ramsfont.h
include/fluidsynth/log.h
include/fluidsynth/misc.h
include/fluidsynth/mod.h
include/fluidsynth/gen.h
include/fluidsynth/voice.h
include/fluidsynth/version.h
)
list(APPEND SOURCES
src/fluid_chan.c
src/fluid_chorus.c
src/fluid_conv.c
src/fluid_defsfont.c
src/fluid_dsp_float.c
src/fluid_gen.c
src/fluid_hash.c
src/fluid_list.c
src/fluid_mod.c
src/fluid_ramsfont.c
src/fluid_rev.c
src/fluid_settings.c
src/fluid_synth.c
src/fluid_sys.c
src/fluid_tuning.c
src/fluid_voice.c
)
# Dependencies:
pkg_check_modules(LIBVORBIS vorbis>=1.3.5)
pkg_check_modules(LIBVORBISFILE vorbisfile>=1.3.5)
if (NOT LIBVORBIS_FOUND OR NOT LIBVORBISFILE_FOUND)
list(APPEND SOURCES
libvorbis-1.3.5/lib/vorbisenc.c
libvorbis-1.3.5/lib/info.c
libvorbis-1.3.5/lib/analysis.c
libvorbis-1.3.5/lib/bitrate.c
libvorbis-1.3.5/lib/block.c
libvorbis-1.3.5/lib/codebook.c
libvorbis-1.3.5/lib/envelope.c
libvorbis-1.3.5/lib/floor0.c
libvorbis-1.3.5/lib/floor1.c
libvorbis-1.3.5/lib/lookup.c
libvorbis-1.3.5/lib/lpc.c
libvorbis-1.3.5/lib/lsp.c
libvorbis-1.3.5/lib/mapping0.c
libvorbis-1.3.5/lib/mdct.c
libvorbis-1.3.5/lib/psy.c
libvorbis-1.3.5/lib/registry.c
libvorbis-1.3.5/lib/res0.c
libvorbis-1.3.5/lib/sharedbook.c
libvorbis-1.3.5/lib/smallft.c
libvorbis-1.3.5/lib/vorbisfile.c
libvorbis-1.3.5/lib/window.c
libvorbis-1.3.5/lib/synthesis.c
)
list(APPEND LIBVORBIS_INCLUDE_DIRS
${CMAKE_SOURCE_DIR}/libvorbis-1.3.5/include
${CMAKE_SOURCE_DIR}/libvorbis-1.3.5/lib
)
message(WARNING "Using libvorbis shipped sources.")
else()
message(STATUS "Using pkg-config provided libvorbis")
set(ADDITIONAL_LIBS "${LIBVORBIS_LDFLAGS} ${LIBVORBISFILE_LDFLAGS}")
endif()
pkg_check_modules(LIBOGG ogg>=1.3.2)
if (NOT LIBOGG_FOUND)
list(APPEND SOURCES
libogg-1.3.2/src/bitwise.c
libogg-1.3.2/src/framing.c
)
set(LIBOGG_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libogg-1.3.2/include)
message(WARNING "Using libogg shipped sources.")
else()
message(STATUS "Using pkg-config provided libogg")
string(CONCAT ADDITIONAL_LIBS "${ADDITIONAL_LIBS} ${LIBOGG_LDFLAGS}")
endif()
include_directories(${CMAKE_SOURCE_DIR}/src)
include_directories(${CMAKE_SOURCE_DIR}/include)
include_directories(${LIBOGG_INCLUDE_DIRS})
include_directories(${LIBVORBIS_INCLUDE_DIRS})
option(FLUIDLITE_BUILD_STATIC "Build static library" TRUE)
if(FLUIDLITE_BUILD_STATIC)
add_library(${PROJECT_NAME}-static STATIC ${SOURCES})
set_target_properties(${PROJECT_NAME}-static PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
set(FLUIDLITE_LIB_TARGET ${PROJECT_NAME}-static)
set(FLUIDLITE_INSTALL_TARGETS ${FLUIDLITE_INSTALL_TARGETS} ";fluidlite-static")
endif()
option(FLUIDLITE_BUILD_SHARED "Build shared library" TRUE)
if(FLUIDLITE_BUILD_SHARED)
add_library(${PROJECT_NAME} SHARED ${SOURCES})
target_link_libraries(${PROJECT_NAME}
${LIBVORBIS_LIBRARIES}
${LIBVORBISFILE_LIBRARIES}
${LIBOGG_LIBRARIES}
)
set(FLUIDLITE_LIB_TARGET ${PROJECT_NAME})
set(FLUIDLITE_INSTALL_TARGETS ${FLUIDLITE_INSTALL_TARGETS} ";fluidlite")
endif()
if(FLUIDLITE_BUILD_SHARED AND FLUIDLITE_BUILD_STATIC)
set_target_properties(${PROJECT_NAME} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
set_target_properties(${PROJECT_NAME}-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
endif()
configure_file(fluidlite.pc.in ${CMAKE_BINARY_DIR}/fluidlite.pc @ONLY)
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99)
install(TARGETS ${FLUIDLITE_INSTALL_TARGETS}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${SCOPED_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/fluidsynth)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fluidlite.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)

View file

@ -0,0 +1,15 @@
FluidLite (c) 2016 Robin Lobel
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

View file

@ -0,0 +1,41 @@
FLUIDLITE 1.0 (c) 2016 Robin Lobel
=========
FluidLite is a very light version of FluidSynth
designed to be hardware, platform and external dependency independant.
It only uses standard C libraries.
It also adds support for SF3 files (SF2 files compressed with ogg vorbis)
and an additional setting to remove the constraint of channel 9 (drums):
fluid_settings_setstr(settings, "synth.drums-channel.active", "no");
you can still select bank 128 on any channel to use drum kits.
FluidLite keeps very minimal functionnalities (settings and synth),
therefore MIDI file reading, realtime MIDI events and audio output must be
implemented externally.
Usage:
=====
include "fluidlite.h"
fluid_settings_t* settings=new_fluid_settings();
fluid_synth_t* synth=new_fluid_synth(settings);
fluid_synth_sfload(synth, "soundfont.sf3",1);
float* buffer=new float[44100*2];
FILE* file=fopen("float32output.pcm","wb");
fluid_synth_noteon(synth,0,60,127);
fluid_synth_write_float(synth, 44100,buffer, 0, 2, buffer, 1, 2);
fwrite(buffer,sizeof(float),44100*2,file);
fluid_synth_noteoff(synth,0,60);
fluid_synth_write_float(synth, 44100,buffer, 0, 2, buffer, 1, 2);
fwrite(buffer,sizeof(float),44100*2,file);
fclose(file);
delete_fluid_synth(synth);
delete_fluid_settings(settings);

View file

@ -0,0 +1,10 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: fluidlite
Description: Software SoundFont synth
Version: 0.0.1
Libs: -L${libdir} -lfluidlite @ADDITIONAL_LIBS@
Cflags: -I${includedir}

View file

@ -0,0 +1,97 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_H
#define _FLUIDSYNTH_H
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
//#if defined(WIN32)
//#if defined(FLUIDSYNTH_DLL_EXPORTS)
//#define FLUIDSYNTH_API __declspec(dllexport)
//#elif defined(FLUIDSYNTH_NOT_A_DLL)
//#define FLUIDSYNTH_API
//#else
//#define FLUIDSYNTH_API __declspec(dllimport)
//#endif
//#elif defined(MACOS9)
//#define FLUIDSYNTH_API __declspec(export)
//#else
//#define FLUIDSYNTH_API
//#endif
#define FLUIDSYNTH_API
/**
* @file fluidsynth.h
* @brief FluidSynth is a real-time synthesizer designed for SoundFont(R) files.
*
* This is the header of the fluidsynth library and contains the
* synthesizer's public API.
*
* Depending on how you want to use or extend the synthesizer you
* will need different API functions. You probably do not need all
* of them. Here is what you might want to do:
*
* o Embedded synthesizer: create a new synthesizer and send MIDI
* events to it. The sound goes directly to the audio output of
* your system.
*
* o Plugin synthesizer: create a synthesizer and send MIDI events
* but pull the audio back into your application.
*
* o SoundFont plugin: create a new type of "SoundFont" and allow
* the synthesizer to load your type of SoundFonts.
*
* o MIDI input: Create a MIDI handler to read the MIDI input on your
* machine and send the MIDI events directly to the synthesizer.
*
* o MIDI files: Open MIDI files and send the MIDI events to the
* synthesizer.
*
* o Command lines: You can send textual commands to the synthesizer.
*
* SoundFont(R) is a registered trademark of E-mu Systems, Inc.
*/
#include "fluidsynth/types.h"
#include "fluidsynth/settings.h"
#include "fluidsynth/synth.h"
#include "fluidsynth/sfont.h"
#include "fluidsynth/ramsfont.h"
#include "fluidsynth/log.h"
#include "fluidsynth/misc.h"
#include "fluidsynth/mod.h"
#include "fluidsynth/gen.h"
#include "fluidsynth/voice.h"
#include "fluidsynth/version.h"
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_H */

View file

@ -0,0 +1,135 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_GEN_H
#define _FLUIDSYNTH_GEN_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file gen.h
* @brief Functions and defines for SoundFont generator effects.
*/
/**
* Generator (effect) numbers (Soundfont 2.01 specifications section 8.1.3)
*/
enum fluid_gen_type {
GEN_STARTADDROFS, /**< Sample start address offset (0-32767) */
GEN_ENDADDROFS, /**< Sample end address offset (-32767-0) */
GEN_STARTLOOPADDROFS, /**< Sample loop start address offset (-32767-32767) */
GEN_ENDLOOPADDROFS, /**< Sample loop end address offset (-32767-32767) */
GEN_STARTADDRCOARSEOFS, /**< Sample start address coarse offset (X 32768) */
GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */
GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */
GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */
GEN_FILTERFC, /**< Filter cutoff */
GEN_FILTERQ, /**< Filter Q */
GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */
GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */
GEN_ENDADDRCOARSEOFS, /**< Sample end address coarse offset (X 32768) */
GEN_MODLFOTOVOL, /**< Modulation LFO to volume */
GEN_UNUSED1, /**< Unused */
GEN_CHORUSSEND, /**< Chorus send amount */
GEN_REVERBSEND, /**< Reverb send amount */
GEN_PAN, /**< Stereo panning */
GEN_UNUSED2, /**< Unused */
GEN_UNUSED3, /**< Unused */
GEN_UNUSED4, /**< Unused */
GEN_MODLFODELAY, /**< Modulation LFO delay */
GEN_MODLFOFREQ, /**< Modulation LFO frequency */
GEN_VIBLFODELAY, /**< Vibrato LFO delay */
GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */
GEN_MODENVDELAY, /**< Modulation envelope delay */
GEN_MODENVATTACK, /**< Modulation envelope attack */
GEN_MODENVHOLD, /**< Modulation envelope hold */
GEN_MODENVDECAY, /**< Modulation envelope decay */
GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */
GEN_MODENVRELEASE, /**< Modulation envelope release */
GEN_KEYTOMODENVHOLD, /**< Key to modulation envelope hold */
GEN_KEYTOMODENVDECAY, /**< Key to modulation envelope decay */
GEN_VOLENVDELAY, /**< Volume envelope delay */
GEN_VOLENVATTACK, /**< Volume envelope attack */
GEN_VOLENVHOLD, /**< Volume envelope hold */
GEN_VOLENVDECAY, /**< Volume envelope decay */
GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */
GEN_VOLENVRELEASE, /**< Volume envelope release */
GEN_KEYTOVOLENVHOLD, /**< Key to volume envelope hold */
GEN_KEYTOVOLENVDECAY, /**< Key to volume envelope decay */
GEN_INSTRUMENT, /**< Instrument ID (shouldn't be set by user) */
GEN_RESERVED1, /**< Reserved */
GEN_KEYRANGE, /**< MIDI note range */
GEN_VELRANGE, /**< MIDI velocity range */
GEN_STARTLOOPADDRCOARSEOFS, /**< Sample start loop address coarse offset (X 32768) */
GEN_KEYNUM, /**< Fixed MIDI note number */
GEN_VELOCITY, /**< Fixed MIDI velocity value */
GEN_ATTENUATION, /**< Initial volume attenuation */
GEN_RESERVED2, /**< Reserved */
GEN_ENDLOOPADDRCOARSEOFS, /**< Sample end loop address coarse offset (X 32768) */
GEN_COARSETUNE, /**< Coarse tuning */
GEN_FINETUNE, /**< Fine tuning */
GEN_SAMPLEID, /**< Sample ID (shouldn't be set by user) */
GEN_SAMPLEMODE, /**< Sample mode flags */
GEN_RESERVED3, /**< Reserved */
GEN_SCALETUNE, /**< Scale tuning */
GEN_EXCLUSIVECLASS, /**< Exclusive class number */
GEN_OVERRIDEROOTKEY, /**< Sample root note override */
/* the initial pitch is not a "standard" generator. It is not
* mentioned in the list of generator in the SF2 specifications. It
* is used, however, as the destination for the default pitch wheel
* modulator. */
GEN_PITCH, /**< Pitch (NOTE: Not a real SoundFont generator) */
GEN_LAST /**< Value defines the count of generators (#fluid_gen_type) */
};
/**
* SoundFont generator structure.
*/
typedef struct _fluid_gen_t
{
unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */
double val; /**< The nominal value */
double mod; /**< Change by modulators */
double nrpn; /**< Change by NRPN messages */
} fluid_gen_t;
/**
* Enum value for 'flags' field of #_fluid_gen_t (not really flags).
*/
enum fluid_gen_flags
{
GEN_UNUSED, /**< Generator value is not set */
GEN_SET, /**< Generator value is set */
GEN_ABS_NRPN /**< DOCME */
};
FLUIDSYNTH_API int fluid_gen_set_default_values(fluid_gen_t* gen);
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_GEN_H */

View file

@ -0,0 +1,83 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_LOG_H
#define _FLUIDSYNTH_LOG_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file log.h
* @brief Logging interface
*
* The default logging function of the fluidsynth prints its messages
* to the stderr. The synthesizer uses five level of messages: #FLUID_PANIC,
* #FLUID_ERR, #FLUID_WARN, #FLUID_INFO, and #FLUID_DBG.
*
* A client application can install a new log function to handle the
* messages differently. In the following example, the application
* sets a callback function to display #FLUID_PANIC messages in a dialog,
* and ignores all other messages by setting the log function to
* NULL:
*
* DOCME (formatting)
* fluid_set_log_function(FLUID_PANIC, show_dialog, (void*) root_window);
* fluid_set_log_function(FLUID_ERR, NULL, NULL);
* fluid_set_log_function(FLUID_WARN, NULL, NULL);
* fluid_set_log_function(FLUID_DBG, NULL, NULL);
*/
/**
* FluidSynth log levels.
*/
enum fluid_log_level {
FLUID_PANIC, /**< The synth can't function correctly any more */
FLUID_ERR, /**< Serious error occurred */
FLUID_WARN, /**< Warning */
FLUID_INFO, /**< Verbose informational messages */
FLUID_DBG, /**< Debugging messages */
LAST_LOG_LEVEL
};
/**
* Log function handler callback type used by fluid_set_log_function().
* @param level Log level (#fluid_log_level)
* @param message Log message text
* @param data User data pointer supplied to fluid_set_log_function().
*/
typedef void (*fluid_log_function_t)(int level, char* message, void* data);
FLUIDSYNTH_API
fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void* data);
FLUIDSYNTH_API void fluid_default_log_function(int level, char* message, void* data);
FLUIDSYNTH_API int fluid_log(int level, char * fmt, ...);
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_LOG_H */

View file

@ -0,0 +1,65 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_MISC_H
#define _FLUIDSYNTH_MISC_H
#ifdef __cplusplus
extern "C" {
#endif
/*
*
* Utility functions
*/
/**
* fluid_is_soundfont returns 1 if the specified filename is a
* soundfont. It retuns 0 otherwise. The current implementation only
* checks for the "RIFF" header in the file. It is useful only to
* distinguish between SoundFonts and MIDI files.
*/
FLUIDSYNTH_API int fluid_is_soundfont(char* filename);
/**
* fluid_is_midifile returns 1 if the specified filename is a MIDI
* file. It retuns 0 otherwise. The current implementation only checks
* for the "MThd" header in the file.
*/
FLUIDSYNTH_API int fluid_is_midifile(char* filename);
#ifdef WIN32
/** Set the handle to the instance of the application on the Windows
platform. The handle is needed to open DirectSound. */
FLUIDSYNTH_API void* fluid_get_hinstance(void);
FLUIDSYNTH_API void fluid_set_hinstance(void* hinstance);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_MISC_H */

View file

@ -0,0 +1,112 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_MOD_H
#define _FLUIDSYNTH_MOD_H
#ifdef __cplusplus
extern "C" {
#endif
/* Modulator-related definitions */
/* Maximum number of modulators in a voice */
#define FLUID_NUM_MOD 64
/*
* fluid_mod_t
*/
struct _fluid_mod_t
{
unsigned char dest;
unsigned char src1;
unsigned char flags1;
unsigned char src2;
unsigned char flags2;
double amount;
/* The 'next' field allows to link modulators into a list. It is
* not used in fluid_voice.c, there each voice allocates memory for a
* fixed number of modulators. Since there may be a huge number of
* different zones, this is more efficient.
*/
fluid_mod_t * next;
};
/* Flags telling the polarity of a modulator. Compare with SF2.01
section 8.2. Note: The numbers of the bits are different! (for
example: in the flags of a SF modulator, the polarity bit is bit
nr. 9) */
enum fluid_mod_flags
{
FLUID_MOD_POSITIVE = 0,
FLUID_MOD_NEGATIVE = 1,
FLUID_MOD_UNIPOLAR = 0,
FLUID_MOD_BIPOLAR = 2,
FLUID_MOD_LINEAR = 0,
FLUID_MOD_CONCAVE = 4,
FLUID_MOD_CONVEX = 8,
FLUID_MOD_SWITCH = 12,
FLUID_MOD_GC = 0,
FLUID_MOD_CC = 16
};
/* Flags telling the source of a modulator. This corresponds to
* SF2.01 section 8.2.1 */
enum fluid_mod_src
{
FLUID_MOD_NONE = 0,
FLUID_MOD_VELOCITY = 2,
FLUID_MOD_KEY = 3,
FLUID_MOD_KEYPRESSURE = 10,
FLUID_MOD_CHANNELPRESSURE = 13,
FLUID_MOD_PITCHWHEEL = 14,
FLUID_MOD_PITCHWHEELSENS = 16
};
/* Allocates memory for a new modulator */
FLUIDSYNTH_API fluid_mod_t * fluid_mod_new(void);
/* Frees the modulator */
FLUIDSYNTH_API void fluid_mod_delete(fluid_mod_t * mod);
FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags);
FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags);
FLUIDSYNTH_API void fluid_mod_set_dest(fluid_mod_t* mod, int dst);
FLUIDSYNTH_API void fluid_mod_set_amount(fluid_mod_t* mod, double amount);
FLUIDSYNTH_API int fluid_mod_get_source1(fluid_mod_t* mod);
FLUIDSYNTH_API int fluid_mod_get_flags1(fluid_mod_t* mod);
FLUIDSYNTH_API int fluid_mod_get_source2(fluid_mod_t* mod);
FLUIDSYNTH_API int fluid_mod_get_flags2(fluid_mod_t* mod);
FLUIDSYNTH_API int fluid_mod_get_dest(fluid_mod_t* mod);
FLUIDSYNTH_API double fluid_mod_get_amount(fluid_mod_t* mod);
/* Determines, if two modulators are 'identical' (all parameters
except the amount match) */
FLUIDSYNTH_API int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2);
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_MOD_H */

View file

@ -0,0 +1,113 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_RAMSFONT_H
#define _FLUIDSYNTH_RAMSFONT_H
#ifdef __cplusplus
extern "C" {
#endif
/********************************************************************************/
/********************************************************************************/
/* ram soundfonts:
October 2002 - Antoine Schmitt
ram soundfonts live in ram. The samples are loaded from files
or from RAM. A minimal API manages a soundFont structure,
with presets, each preset having only one preset-zone, which
instrument has potentially many instrument-zones. No global
zones, and nor generator nor modulator other than the default
ones are permitted. This may be extensible in the future.
*/
/********************************************************************************/
/********************************************************************************/
/*
We are not using the sfloader protocol, as we need more arguments
than what it provides.
*/
/** Creates a fluid_sfont_t wrapping an fluid_ramsfont_t */
FLUIDSYNTH_API fluid_sfont_t* fluid_ramsfont_create_sfont(void);
/***********************
* ramsfont specific API
***********************/
FLUIDSYNTH_API int fluid_ramsfont_set_name(fluid_ramsfont_t* sfont, char * name);
/* Creates one instrument zone for the sample inside the preset defined
* by bank/num
* \returns 0 if success
*/
FLUIDSYNTH_API
int fluid_ramsfont_add_izone(fluid_ramsfont_t* sfont,
unsigned int bank, unsigned int num, fluid_sample_t* sample,
int lokey, int hikey);
/* Removes the instrument zone corresponding to bank/num and to the sample
* \returns 0 if success
*/
FLUIDSYNTH_API
int fluid_ramsfont_remove_izone(fluid_ramsfont_t* sfont,
unsigned int bank, unsigned int num, fluid_sample_t* sample);
/* Sets a generator on an instrument zone
* \returns 0 if success
*/
FLUIDSYNTH_API
int fluid_ramsfont_izone_set_gen(fluid_ramsfont_t* sfont,
unsigned int bank, unsigned int num, fluid_sample_t* sample,
int gen_type, float value);
/* Utility : sets the loop start/end values
* \on = 0 or 1; if 0, loopstart and loopend are not used
* \loopstart and loopend are floats, in frames
* \loopstart is counted from frame 0
* \loopend is counted from the last frame, thus is < 0
* \returns 0 if success
*/
FLUIDSYNTH_API
int fluid_ramsfont_izone_set_loop(fluid_ramsfont_t* sfont,
unsigned int bank, unsigned int num, fluid_sample_t* sample,
int on, float loopstart, float loopend);
/***************************************
* sample_t specific API for ramsfont
***************************************/
FLUIDSYNTH_API fluid_sample_t* new_fluid_ramsample(void);
FLUIDSYNTH_API int delete_fluid_ramsample(fluid_sample_t* sample);
FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t* sample, char * name);
/* Sets the sound data of the sample
* Warning : if copy_data is FALSE, data should have 8 unused frames at start
* and 8 unused frames at the end.
*/
FLUIDSYNTH_API
int fluid_sample_set_sound_data(fluid_sample_t* sample, short *data,
unsigned int nbframes, short copy_data, int rootkey);
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_RAMSFONT_H */

View file

@ -0,0 +1,202 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_SETTINGS_H
#define _FLUIDSYNTH_SETTINGS_H
#ifdef __cplusplus
extern "C" {
#endif
/**
*
* Synthesizer settings
*
*
* The create a synthesizer object you will have to specify its
* settings. These settings are stored in the structure below.
* void my_synthesizer()
* {
* fluid_settings_t* settings;
* fluid_synth_t* synth;
* fluid_audio_driver_t* adriver;
*
*
* settings = new_fluid_settings();
* fluid_settings_setstr(settings, "audio.driver", "alsa");
* // ... change settings ...
* synth = new_fluid_synth(settings);
* adriver = new_fluid_audio_driver(settings, synth);
*
* ...
*
* }
*
*
*/
/* Hint FLUID_HINT_BOUNDED_BELOW indicates that the LowerBound field
of the FLUID_PortRangeHint should be considered meaningful. The
value in this field should be considered the (inclusive) lower
bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also
specified then the value of LowerBound should be multiplied by the
sample rate. */
#define FLUID_HINT_BOUNDED_BELOW 0x1
/* Hint FLUID_HINT_BOUNDED_ABOVE indicates that the UpperBound field
of the FLUID_PortRangeHint should be considered meaningful. The
value in this field should be considered the (inclusive) upper
bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also
specified then the value of UpperBound should be multiplied by the
sample rate. */
#define FLUID_HINT_BOUNDED_ABOVE 0x2
/* Hint FLUID_HINT_TOGGLED indicates that the data item should be
considered a Boolean toggle. Data less than or equal to zero should
be considered `off' or `false,' and data above zero should be
considered `on' or `true.' FLUID_HINT_TOGGLED may not be used in
conjunction with any other hint except FLUID_HINT_DEFAULT_0 or
FLUID_HINT_DEFAULT_1. */
#define FLUID_HINT_TOGGLED 0x4
/* Hint FLUID_HINT_SAMPLE_RATE indicates that any bounds specified
should be interpreted as multiples of the sample rate. For
instance, a frequency range from 0Hz to the Nyquist frequency (half
the sample rate) could be requested by this hint in conjunction
with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
at all must support this hint to retain meaning. */
#define FLUID_HINT_SAMPLE_RATE 0x8
/* Hint FLUID_HINT_LOGARITHMIC indicates that it is likely that the
user will find it more intuitive to view values using a logarithmic
scale. This is particularly useful for frequencies and gains. */
#define FLUID_HINT_LOGARITHMIC 0x10
/* Hint FLUID_HINT_INTEGER indicates that a user interface would
probably wish to provide a stepped control taking only integer
values. Any bounds set should be slightly wider than the actual
integer range required to avoid floating point rounding errors. For
instance, the integer set {0,1,2,3} might be described as [-0.1,
3.1]. */
#define FLUID_HINT_INTEGER 0x20
#define FLUID_HINT_FILENAME 0x01
#define FLUID_HINT_OPTIONLIST 0x02
enum fluid_types_enum {
FLUID_NO_TYPE = -1,
FLUID_NUM_TYPE,
FLUID_INT_TYPE,
FLUID_STR_TYPE,
FLUID_SET_TYPE
};
FLUIDSYNTH_API fluid_settings_t* new_fluid_settings(void);
FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t* settings);
FLUIDSYNTH_API
int fluid_settings_get_type(fluid_settings_t* settings, const char* name);
FLUIDSYNTH_API
int fluid_settings_get_hints(fluid_settings_t* settings, const char* name);
/** Returns whether the setting is changeable in real-time. */
FLUIDSYNTH_API int fluid_settings_is_realtime(fluid_settings_t* settings, const char* name);
/** returns 1 if the value has been set, 0 otherwise */
FLUIDSYNTH_API
int fluid_settings_setstr(fluid_settings_t* settings, const char* name, const char* str);
/**
Get the value of a string setting. If the value does not exists,
'str' is set to NULL. Otherwise, 'str' will point to the
value. The application does not own the returned value. Instead,
the application should make a copy of the value if it needs it
later.
\returns 1 if the value exists, 0 otherwise
*/
FLUIDSYNTH_API
int fluid_settings_getstr(fluid_settings_t* settings, const char* name, char** str);
/** Get the default value of a string setting. */
FLUIDSYNTH_API
char* fluid_settings_getstr_default(fluid_settings_t* settings, const char* name);
/** Get the value of a numeric setting.
\returns 1 if the value exists and is equal to 'value', 0
otherwise
*/
FLUIDSYNTH_API
int fluid_settings_str_equal(fluid_settings_t* settings, const char* name, char* value);
/** returns 1 if the value has been set, 0 otherwise */
FLUIDSYNTH_API
int fluid_settings_setnum(fluid_settings_t* settings, const char* name, double val);
/** returns 1 if the value exists, 0 otherwise */
FLUIDSYNTH_API
int fluid_settings_getnum(fluid_settings_t* settings, const char* name, double* val);
/** Get the default value of a string setting. */
FLUIDSYNTH_API
double fluid_settings_getnum_default(fluid_settings_t* settings, const char* name);
/** Get the range of values of a numeric settings. */
FLUIDSYNTH_API
void fluid_settings_getnum_range(fluid_settings_t* settings, const char* name,
double* min, double* max);
/** returns 1 if the value has been set, 0 otherwise */
FLUIDSYNTH_API
int fluid_settings_setint(fluid_settings_t* settings, const char* name, int val);
/** returns 1 if the value exists, 0 otherwise */
FLUIDSYNTH_API
int fluid_settings_getint(fluid_settings_t* settings, const char* name, int* val);
/** Get the default value of a string setting. */
FLUIDSYNTH_API
int fluid_settings_getint_default(fluid_settings_t* settings, const char* name);
/** Get the range of values of a numeric settings. */
FLUIDSYNTH_API
void fluid_settings_getint_range(fluid_settings_t* settings, const char* name,
int* min, int* max);
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_SETTINGS_H */

View file

@ -0,0 +1,195 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_SFONT_H
#define _FLUIDSYNTH_SFONT_H
#ifdef __cplusplus
extern "C" {
#endif
/**
*
* SoundFont plugins
*
* It is possible to add new SoundFont loaders to the
* synthesizer. The API uses a couple of "interfaces" (structures
* with callback functions): fluid_sfloader_t, fluid_sfont_t, and
* fluid_preset_t.
*
* To add a new SoundFont loader to the synthesizer, call
* fluid_synth_add_sfloader() and pass a pointer to an
* fluid_sfloader_t structure. The important callback function in
* this structure is "load", which should try to load a file and
* returns a fluid_sfont_t structure, or NULL if it fails.
*
* The fluid_sfont_t structure contains a callback to obtain the
* name of the soundfont. It contains two functions to iterate
* though the contained presets, and one function to obtain a
* preset corresponding to a bank and preset number. This
* function should return an fluid_preset_t structure.
*
* The fluid_preset_t structure contains some functions to obtain
* information from the preset (name, bank, number). The most
* important callback is the noteon function. The noteon function
* should call fluid_synth_alloc_voice() for every sample that has
* to be played. fluid_synth_alloc_voice() expects a pointer to a
* fluid_sample_t structure and returns a pointer to the opaque
* fluid_voice_t structure. To set or increments the values of a
* generator, use fluid_voice_gen_{set,incr}. When you are
* finished initializing the voice call fluid_voice_start() to
* start playing the synthesis voice.
* */
enum {
FLUID_PRESET_SELECTED,
FLUID_PRESET_UNSELECTED,
FLUID_SAMPLE_DONE
};
/*
* fluid_sfloader_t
*/
struct _fluid_sfloader_t {
/** Private data */
void* data;
/** The free must free the memory allocated for the loader in
* addition to any private data. It should return 0 if no error
* occured, non-zero otherwise.*/
int (*free)(fluid_sfloader_t* loader);
/** Load a file. Returns NULL if an error occured. */
fluid_sfont_t* (*load)(fluid_sfloader_t* loader, const char* filename);
};
/*
* fluid_sfont_t
*/
struct _fluid_sfont_t {
void* data;
unsigned int id;
/** The 'free' callback function should return 0 when it was able to
free all resources. It should return a non-zero value if some of
the samples could not be freed because they are still in use. */
int (*free)(fluid_sfont_t* sfont);
/** Return the name of the sfont */
char* (*get_name)(fluid_sfont_t* sfont);
/** Return the preset with the specified bank and preset number. All
* the fields, including the 'sfont' field, should * be filled
* in. If the preset cannot be found, the function returns NULL. */
fluid_preset_t* (*get_preset)(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum);
void (*iteration_start)(fluid_sfont_t* sfont);
/* return 0 when no more presets are available, 1 otherwise */
int (*iteration_next)(fluid_sfont_t* sfont, fluid_preset_t* preset);
};
#define fluid_sfont_get_id(_sf) ((_sf)->id)
/*
* fluid_preset_t
*/
struct _fluid_preset_t {
void* data;
fluid_sfont_t* sfont;
int (*free)(fluid_preset_t* preset);
char* (*get_name)(fluid_preset_t* preset);
int (*get_banknum)(fluid_preset_t* preset);
int (*get_num)(fluid_preset_t* preset);
/** handle a noteon event. Returns 0 if no error occured. */
int (*noteon)(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
/** Implement this function if the preset needs to be notified about
preset select and unselect events. */
int (*notify)(fluid_preset_t* preset, int reason, int chan);
};
/*
* fluid_sample_t
*/
struct _fluid_sample_t
{
char name[21];
unsigned int start;
unsigned int end; /* Note: Index of last valid sample point (contrary to SF spec) */
unsigned int loopstart;
unsigned int loopend; /* Note: first point following the loop (superimposed on loopstart) */
unsigned int samplerate;
int origpitch;
int pitchadj;
int sampletype;
int valid;
short* data;
/** The amplitude, that will lower the level of the sample's loop to
the noise floor. Needed for note turnoff optimization, will be
filled out automatically */
/* Set this to zero, when submitting a new sample. */
int amplitude_that_reaches_noise_floor_is_valid;
double amplitude_that_reaches_noise_floor;
/** Count the number of playing voices that use this sample. */
unsigned int refcount;
/** Implement this function if the sample or SoundFont needs to be
notified when the sample is no longer used. */
int (*notify)(fluid_sample_t* sample, int reason);
/** Pointer to SoundFont specific data */
void* userdata;
};
#define fluid_sample_refcount(_sample) ((_sample)->refcount)
/** Sample types */
#define FLUID_SAMPLETYPE_MONO 1
#define FLUID_SAMPLETYPE_RIGHT 2
#define FLUID_SAMPLETYPE_LEFT 4
#define FLUID_SAMPLETYPE_LINKED 8
#define FLUID_SAMPLETYPE_OGG_VORBIS 0x10 /**< Flag for #fluid_sample_t \a sampletype field for Ogg Vorbis compressed samples */
#define FLUID_SAMPLETYPE_OGG_VORBIS_UNPACKED 0x20
#define FLUID_SAMPLETYPE_ROM 0x8000
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_SFONT_H */

View file

@ -0,0 +1,728 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_SYNTH_H
#define _FLUIDSYNTH_SYNTH_H
#ifdef __cplusplus
extern "C" {
#endif
/** Embedded synthesizer
*
* You create a new synthesizer with new_fluid_synth() and you destroy
* if with delete_fluid_synth(). Use the settings structure to specify
* the synthesizer characteristics.
*
* You have to load a SoundFont in order to hear any sound. For that
* you use the fluid_synth_sfload() function.
*
* You can use the audio driver functions described below to open
* the audio device and create a background audio thread.
*
* The API for sending MIDI events is probably what you expect:
* fluid_synth_noteon(), fluid_synth_noteoff(), ...
*
*/
/** Creates a new synthesizer object.
*
* Creates a new synthesizer object. As soon as the synthesizer is
* created, it will start playing.
*
* \param settings a pointer to a settings structure
* \return a newly allocated synthesizer or NULL in case of error
*/
FLUIDSYNTH_API fluid_synth_t* new_fluid_synth(fluid_settings_t* settings);
FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate);
/**
* Deletes the synthesizer previously created with new_fluid_synth.
*
* \param synth the synthesizer object
* \return 0 if no error occured, -1 otherwise
*/
FLUIDSYNTH_API int delete_fluid_synth(fluid_synth_t* synth);
/** Get a reference to the settings of the synthesizer.
*
* \param synth the synthesizer object
* \return pointer to the settings
*/
FLUIDSYNTH_API fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth);
/*
*
* MIDI channel messages
*
*/
/** Send a noteon message. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel);
/** Send a noteoff message. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key);
/** Send a control change message. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t* synth, int chan, int ctrl, int val);
/** Get a control value. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int ctrl, int* pval);
/** Send a pitch bend message. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val);
/** Get the pitch bend value. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API
int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend);
/** Set the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val);
/** Get the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval);
/** Send a program change message. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program);
FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val);
FLUIDSYNTH_API int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len,
char *response, int *response_len, int *handled, int dryrun);
/** Select a bank. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API
int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank);
/** Select a sfont. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API
int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id);
/** Select a preset for a channel. The preset is specified by the
SoundFont ID, the bank number, and the preset number. This
allows any preset to be selected and circumvents preset masking
due to previously loaded SoundFonts on the SoundFont stack.
\param synth The synthesizer
\param chan The channel on which to set the preset
\param sfont_id The ID of the SoundFont
\param bank_num The bank number
\param preset_num The preset number
\return 0 if no errors occured, -1 otherwise
*/
FLUIDSYNTH_API
int fluid_synth_program_select(fluid_synth_t* synth, int chan,
unsigned int sfont_id,
unsigned int bank_num,
unsigned int preset_num);
/** Returns the program, bank, and SoundFont number of the preset on
a given channel. Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API
int fluid_synth_get_program(fluid_synth_t* synth, int chan,
unsigned int* sfont_id,
unsigned int* bank_num,
unsigned int* preset_num);
/** Send a bank select and a program change to every channel to
* reinitialize the preset of the channel. This function is useful
* mainly after a SoundFont has been loaded, unloaded or
* reloaded. . Returns 0 if no error occurred, -1 otherwise. */
FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t* synth);
/** Send a reset. A reset turns all the notes off and resets the
controller values. */
FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t* synth);
FLUIDSYNTH_API int fluid_synth_all_notes_off(fluid_synth_t *synth, int chan);
FLUIDSYNTH_API int fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan);
/*
*
* Low level access
*
*/
/** Create and start voices using a preset. The id passed as
* argument will be used as the voice group id. */
FLUIDSYNTH_API int fluid_synth_start(fluid_synth_t* synth, unsigned int id,
fluid_preset_t* preset, int audio_chan,
int midi_chan, int key, int vel);
/** Stop the voices in the voice group defined by id. */
FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t* synth, unsigned int id);
/** Change the value of a generator of the voices in the voice group
* defined by id. */
/* FLUIDSYNTH_API int fluid_synth_ctrl(fluid_synth_t* synth, int id, */
/* int gen, float value, */
/* int absolute, int normalized); */
/*
*
* SoundFont management
*
*/
/** Set an optional function callback each time a preset has finished loading.
This can be useful when calling fluid_synth_sfload asynchronously.
The function must be formatted like this:
void my_callback_function(int bank, int num, char* name)
\param callback Pointer to the function
*/
FLUIDSYNTH_API
void fluid_synth_set_preset_callback(void* callback);
/** Loads a SoundFont file and creates a new SoundFont. The newly
loaded SoundFont will be put on top of the SoundFont
stack. Presets are searched starting from the SoundFont on the
top of the stack, working the way down the stack until a preset
is found.
\param synth The synthesizer object
\param filename The file name
\param reset_presets If non-zero, the presets on the channels will be reset
\returns The ID of the loaded SoundFont, or -1 in case of error
*/
FLUIDSYNTH_API
int fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets);
/** Reload a SoundFont. The reloaded SoundFont retains its ID and
index on the stack.
\param synth The synthesizer object
\param id The id of the SoundFont
\returns The ID of the loaded SoundFont, or -1 in case of error
*/
FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id);
/** Removes a SoundFont from the stack and deallocates it.
\param synth The synthesizer object
\param id The id of the SoundFont
\param reset_presets If TRUE then presets will be reset for all channels
\returns 0 if no error, -1 otherwise
*/
FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets);
/** Add a SoundFont. The SoundFont will be put on top of
the SoundFont stack.
\param synth The synthesizer object
\param sfont The SoundFont
\returns The ID of the loaded SoundFont, or -1 in case of error
*/
FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont);
/** Remove a SoundFont that was previously added using
* fluid_synth_add_sfont(). The synthesizer does not delete the
* SoundFont; this is responsability of the caller.
\param synth The synthesizer object
\param sfont The SoundFont
*/
FLUIDSYNTH_API void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont);
/** Count the number of loaded SoundFonts.
\param synth The synthesizer object
\returns The number of loaded SoundFonts
*/
FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t* synth);
/** Get a SoundFont. The SoundFont is specified by its index on the
stack. The top of the stack has index zero.
\param synth The synthesizer object
\param num The number of the SoundFont (0 <= num < sfcount)
\returns A pointer to the SoundFont
*/
FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num);
/** Get a SoundFont. The SoundFont is specified by its ID.
\param synth The synthesizer object
\param id The id of the sfont
\returns A pointer to the SoundFont
*/
FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id);
/** Get the preset of a channel */
FLUIDSYNTH_API fluid_preset_t* fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan);
/** Offset the bank numbers in a SoundFont. Returns -1 if an error
* occured (out of memory or negative offset) */
FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset);
/** Get the offset of the bank numbers in a SoundFont. */
FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id);
/*
*
* Reverb
*
*/
/** Set the parameters for the built-in reverb unit */
FLUIDSYNTH_API void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize,
double damping, double width, double level);
/** Turn on (1) / off (0) the built-in reverb unit */
FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on);
/** Query the current state of the reverb. */
FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth);
FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t* synth);
FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t* synth);
FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t* synth);
/* Those are the default settings for the reverb */
#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f
#define FLUID_REVERB_DEFAULT_DAMP 0.0f
#define FLUID_REVERB_DEFAULT_WIDTH 0.5f
#define FLUID_REVERB_DEFAULT_LEVEL 0.9f
/*
*
* Chorus
*
*/
enum fluid_chorus_mod {
FLUID_CHORUS_MOD_SINE = 0,
FLUID_CHORUS_MOD_TRIANGLE = 1
};
/** Set up the chorus. It should be turned on with fluid_synth_set_chorus_on.
* If faulty parameters are given, all new settings are discarded.
* Keep in mind, that the needed CPU time is proportional to 'nr'.
*/
FLUIDSYNTH_API void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level,
double speed, double depth_ms, int type);
/** Turn on (1) / off (0) the built-in chorus unit */
FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on);
/** Query the current state of the chorus. */
FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t* synth);
FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t* synth);
FLUIDSYNTH_API double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth);
FLUIDSYNTH_API double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth);
FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t* synth); /* see fluid_chorus_mod */
/* Those are the default settings for the chorus. */
#define FLUID_CHORUS_DEFAULT_N 3
#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f
#define FLUID_CHORUS_DEFAULT_SPEED 0.3f
#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f
#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE
/*
*
* Audio and MIDI channels
*
*/
/** Returns the number of MIDI channels that the synthesizer uses
internally */
FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t* synth);
/** Returns the number of audio channels that the synthesizer uses
internally */
FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t* synth);
/** Returns the number of audio groups that the synthesizer uses
internally. This is usually identical to audio_channels. */
FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t* synth);
/** Returns the number of effects channels that the synthesizer uses
internally */
FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t* synth);
/*
*
* Synthesis parameters
*
*/
/** Set the master gain */
FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t* synth, float gain);
/** Get the master gain */
FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t* synth);
/** Set the polyphony limit (FluidSynth >= 1.0.6) */
FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony);
/** Get the polyphony limit (FluidSynth >= 1.0.6) */
FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t* synth);
/** Get the internal buffer size. The internal buffer size if not the
same thing as the buffer size specified in the
settings. Internally, the synth *always* uses a specific buffer
size independent of the buffer size used by the audio driver. The
internal buffer size is normally 64 samples. The reason why it
uses an internal buffer size is to allow audio drivers to call the
synthesizer with a variable buffer length. The internal buffer
size is useful for client who want to optimize their buffer sizes.
*/
FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t* synth);
/** Set the interpolation method for one channel or all channels (chan = -1) */
FLUIDSYNTH_API
int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method);
/* Flags to choose the interpolation method */
enum fluid_interp {
/* no interpolation: Fastest, but questionable audio quality */
FLUID_INTERP_NONE = 0,
/* Straight-line interpolation: A bit slower, reasonable audio quality */
FLUID_INTERP_LINEAR = 1,
/* Fourth-order interpolation: Requires 50 % of the whole DSP processing time, good quality
* Default. */
FLUID_INTERP_DEFAULT = 4,
FLUID_INTERP_4THORDER = 4,
FLUID_INTERP_7THORDER = 7,
FLUID_INTERP_HIGHEST=7
};
/*
*
* Generator interface
*
*/
/** Change the value of a generator. This function allows to control
all synthesis parameters in real-time. The changes are additive,
i.e. they add up to the existing parameter value. This function is
similar to sending an NRPN message to the synthesizer. The
function accepts a float as the value of the parameter. The
parameter numbers and ranges are described in the SoundFont 2.01
specification, paragraph 8.1.3, page 48. See also 'fluid_gen_type'.
\param synth The synthesizer object.
\param chan The MIDI channel number.
\param param The parameter number.
\param value The parameter value.
\returns Your favorite dish.
*/
FLUIDSYNTH_API
int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value);
/** Retreive the value of a generator. This function returns the value
set by a previous call 'fluid_synth_set_gen' or by an NRPN message.
\param synth The synthesizer object.
\param chan The MIDI channel number.
\param param The generator number.
\returns The value of the generator.
*/
FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param);
/*
*
* Tuning
*
*/
/** Create a new key-based tuning with given name, number, and
pitches. The array 'pitches' should have length 128 and contains
the pitch in cents of every key in cents. However, if 'pitches' is
NULL, a new tuning is created with the well-tempered scale.
\param synth The synthesizer object
\param tuning_bank The tuning bank number [0-127]
\param tuning_prog The tuning program number [0-127]
\param name The name of the tuning
\param pitch The array of pitch values. The array length has to be 128.
*/
FLUIDSYNTH_API
int fluid_synth_create_key_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog,
const char* name, double* pitch);
/** Create a new octave-based tuning with given name, number, and
pitches. The array 'pitches' should have length 12 and contains
derivation in cents from the well-tempered scale. For example, if
pitches[0] equals -33, then the C-keys will be tuned 33 cents
below the well-tempered C.
\param synth The synthesizer object
\param tuning_bank The tuning bank number [0-127]
\param tuning_prog The tuning program number [0-127]
\param name The name of the tuning
\param pitch The array of pitch derivations. The array length has to be 12.
*/
FLUIDSYNTH_API
int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog,
const char* name, const double* pitch);
FLUIDSYNTH_API
int fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog,
const char* name, const double* pitch, int apply);
/** Request a note tuning changes. Both they 'keys' and 'pitches'
arrays should be of length 'num_pitches'. If 'apply' is non-zero,
the changes should be applied in real-time, i.e. sounding notes
will have their pitch updated. 'APPLY' IS CURRENTLY IGNORED. The
changes will be available for newly triggered notes only.
\param synth The synthesizer object
\param tuning_bank The tuning bank number [0-127]
\param tuning_prog The tuning program number [0-127]
\param len The length of the keys and pitch arrays
\param keys The array of keys values.
\param pitch The array of pitch values.
\param apply Flag to indicate whether to changes should be applied in real-time.
*/
FLUIDSYNTH_API
int fluid_synth_tune_notes(fluid_synth_t* synth, int tuning_bank, int tuning_prog,
int len, int *keys, double* pitch, int apply);
/** Select a tuning for a channel.
\param synth The synthesizer object
\param chan The channel number [0-max channels]
\param tuning_bank The tuning bank number [0-127]
\param tuning_prog The tuning program number [0-127]
*/
FLUIDSYNTH_API
int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int tuning_bank, int tuning_prog);
int fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, int apply);
/** Set the tuning to the default well-tempered tuning on a channel.
\param synth The synthesizer object
\param chan The channel number [0-max channels]
*/
FLUIDSYNTH_API int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan);
/** Start the iteration throught the list of available tunings.
\param synth The synthesizer object
*/
FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t* synth);
/** Get the next tuning in the iteration. This functions stores the
bank and program number of the next tuning in the pointers given as
arguments.
\param synth The synthesizer object
\param bank Pointer to an int to store the bank number
\param prog Pointer to an int to store the program number
\returns 1 if there is a next tuning, 0 otherwise
*/
FLUIDSYNTH_API
int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog);
/** Dump the data of a tuning. This functions stores the name and
pitch values of a tuning in the pointers given as arguments. Both
name and pitch can be NULL is the data is not needed.
\param synth The synthesizer object
\param bank The tuning bank number [0-127]
\param prog The tuning program number [0-127]
\param name Pointer to a buffer to store the name
\param len The length of the name buffer
\param pitch Pointer to buffer to store the pitch values
*/
FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog,
char* name, int len, double* pitch);
/*
*
* Misc
*
*/
/** Get a textual representation of the last error */
FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth);
/*
*
* Synthesizer plugin
*
*
* To create a synthesizer plugin, create the synthesizer as
* explained above. Once the synthesizer is created you can call
* any of the functions below to get the audio.
*
*/
/** Generate a number of samples. This function expects two signed
* 16bits buffers (left and right channel) that will be filled with
* samples.
*
* \param synth The synthesizer
* \param len The number of samples to generate
* \param lout The sample buffer for the left channel
* \param loff The offset, in samples, in the left buffer where the writing pointer starts
* \param lincr The increment, in samples, of the writing pointer in the left buffer
* \param rout The sample buffer for the right channel
* \param roff The offset, in samples, in the right buffer where the writing pointer starts
* \param rincr The increment, in samples, of the writing pointer in the right buffer
* \returns 0 if no error occured, non-zero otherwise
*/
FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t* synth, int len,
void* lout, int loff, int lincr,
void* rout, int roff, int rincr);
/** Generate a number of samples. This function expects two floating
* point buffers (left and right channel) that will be filled with
* samples.
*
* \param synth The synthesizer
* \param len The number of samples to generate
* \param lout The sample buffer for the left channel
* \param loff The offset, in samples, in the left buffer where the writing pointer starts
* \param lincr The increment, in samples, of the writing pointer in the left buffer
* \param rout The sample buffer for the right channel
* \param roff The offset, in samples, in the right buffer where the writing pointer starts
* \param rincr The increment, in samples, of the writing pointer in the right buffer
* \returns 0 if no error occured, non-zero otherwise
*/
FLUIDSYNTH_API int fluid_synth_write_float(fluid_synth_t* synth, int len,
void* lout, int loff, int lincr,
void* rout, int roff, int rincr);
FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
float** left, float** right,
float** fx_left, float** fx_right);
/** Generate a number of samples. This function expects planar floating
* point buffers (first half of the buffer is filled by the left channel, second half is filled by the right channel) that will be filled with
* samples.
*
* \param synth The synthesizer
* \param len The number of samples to generate
* \param out The sample buffer for the left channel
* \returns 0 if no error occured, non-zero otherwise
*/
FLUIDSYNTH_API int fluid_synth_pnwrite_float(fluid_synth_t* synth, int len, void* lout, void* rout);
/** Generate a number of samples. This function implements the
* default interface defined in fluidsynth/audio.h. This function
* ignores the input buffers and expects at least two output
* buffer.
*
* \param synth The synthesizer
* \param len The number of samples to generate
* \param nin The number of input buffers
* \param in The array of input buffers
* \param nout The number of output buffers
* \param out The array of output buffers
* \returns 0 if no error occured, non-zero otherwise
*/
FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t* synth, int len,
int nin, float** in,
int nout, float** out);
/* Type definition of the synthesizer's audio callback function. */
typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len,
void* out1, int loff, int lincr,
void* out2, int roff, int rincr);
/*
* Synthesizer's interface to handle SoundFont loaders
*/
/** Add a SoundFont loader to the synthesizer. Note that SoundFont
loader don't necessarily load SoundFonts. They can load any type
of wavetable data but export a SoundFont interface. */
FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader);
/** Allocate a synthesis voice. This function is called by a
soundfont's preset in response to a noteon event.
The returned voice comes with default modulators installed (velocity-to-attenuation,
velocity to filter, ...)
Note: A single noteon event may create any number of voices, when the preset is layered.
Typically 1 (mono) or 2 (stereo).*/
FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample,
int channum, int key, int vel);
/** Start a synthesis voice. This function is called by a
soundfont's preset in response to a noteon event after the voice
has been allocated with fluid_synth_alloc_voice() and
initialized.
Exclusive classes are processed here.*/
FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice);
/** Write a list of all voices matching ID into buf, but not more than bufsize voices.
* If ID <0, return all voices. */
FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth,
fluid_voice_t* buf[], int bufsize, int ID);
//midi router disabled
// /** This is a hack to get command handlers working */
//FLUIDSYNTH_API void fluid_synth_set_midi_router(fluid_synth_t* synth,
// fluid_midi_router_t* router);
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_SYNTH_H */

View file

@ -0,0 +1,66 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_TYPES_H
#define _FLUIDSYNTH_TYPES_H
#ifdef __cplusplus
extern "C" {
#endif
/*
Forward declarations
*/
typedef struct _fluid_hashtable_t fluid_settings_t;
typedef struct _fluid_synth_t fluid_synth_t;
typedef struct _fluid_voice_t fluid_voice_t;
typedef struct _fluid_sfloader_t fluid_sfloader_t;
typedef struct _fluid_sfont_t fluid_sfont_t;
typedef struct _fluid_preset_t fluid_preset_t;
typedef struct _fluid_sample_t fluid_sample_t;
typedef struct _fluid_mod_t fluid_mod_t;
typedef struct _fluid_audio_driver_t fluid_audio_driver_t;
typedef struct _fluid_player_t fluid_player_t;
typedef struct _fluid_midi_event_t fluid_midi_event_t;
typedef struct _fluid_midi_driver_t fluid_midi_driver_t;
typedef struct _fluid_midi_router_t fluid_midi_router_t;
typedef struct _fluid_midi_router_rule_t fluid_midi_router_rule_t;
typedef struct _fluid_hashtable_t fluid_cmd_handler_t;
typedef struct _fluid_shell_t fluid_shell_t;
typedef struct _fluid_server_t fluid_server_t;
typedef struct _fluid_event_t fluid_event_t;
typedef struct _fluid_sequencer_t fluid_sequencer_t;
typedef struct _fluid_ramsfont_t fluid_ramsfont_t;
typedef struct _fluid_rampreset_t fluid_rampreset_t;
typedef int fluid_istream_t;
typedef int fluid_ostream_t;
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_TYPES_H */

View file

@ -0,0 +1,44 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_VERSION_H
#define _FLUIDSYNTH_VERSION_H
#ifdef __cplusplus
extern "C" {
#endif
#define FLUIDSYNTH_VERSION "1.1.0"
#define FLUIDSYNTH_VERSION_MAJOR 1
#define FLUIDSYNTH_VERSION_MINOR 1
#define FLUIDSYNTH_VERSION_MICRO 0
FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro);
FLUIDSYNTH_API char* fluid_version_str(void);
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_VERSION_H */

View file

@ -0,0 +1,97 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_VOICE_H
#define _FLUIDSYNTH_VOICE_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* The interface to the synthesizer's voices
* Examples on using them can be found in fluid_defsfont.c
*/
/** Update all the synthesis parameters, which depend on generator gen.
This is only necessary after changing a generator of an already operating voice.
Most applications will not need this function.*/
FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t* voice, int gen);
/* for fluid_voice_add_mod */
enum fluid_voice_add_mod{
FLUID_VOICE_OVERWRITE,
FLUID_VOICE_ADD,
FLUID_VOICE_DEFAULT
};
/* Add a modulator to a voice (SF2.1 only). */
FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode);
/** Set the value of a generator */
FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t* voice, int gen, float val);
/** Get the value of a generator */
FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t* voice, int gen);
/** Modify the value of a generator by val */
FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t* voice, int gen, float val);
/** Return the unique ID of the noteon-event. A sound font loader
* may store the voice processes it has created for * real-time
* control during the operation of a voice (for example: parameter
* changes in sound font editor). The synth uses a pool of
* voices, which are 'recycled' and never deallocated.
*
* Before modifying an existing voice, check
* - that its state is still 'playing'
* - that the ID is still the same
* Otherwise the voice has finished playing.
*/
FLUIDSYNTH_API unsigned int fluid_voice_get_id(fluid_voice_t* voice);
FLUIDSYNTH_API int fluid_voice_is_playing(fluid_voice_t* voice);
/** If the peak volume during the loop is known, then the voice can
* be released earlier during the release phase. Otherwise, the
* voice will operate (inaudibly), until the envelope is at the
* nominal turnoff point. In many cases the loop volume is many dB
* below the maximum volume. For example, the loop volume for a
* typical acoustic piano is 20 dB below max. Taking that into
* account in the turn-off algorithm we can save 20 dB / 100 dB =>
* 1/5 of the total release time.
* So it's a good idea to call fluid_voice_optimize_sample
* on each sample once.
*/
FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t* s);
#ifdef __cplusplus
}
#endif
#endif /* _FLUIDSYNTH_VOICE_H */

View file

@ -0,0 +1,444 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_chan.h"
#include "fluid_mod.h"
#include "fluid_synth.h"
#include "fluid_sfont.h"
#define SETCC(_c,_n,_v) _c->cc[_n] = _v
/*
* new_fluid_channel
*/
fluid_channel_t*
new_fluid_channel(fluid_synth_t* synth, int num)
{
fluid_channel_t* chan;
chan = FLUID_NEW(fluid_channel_t);
if (chan == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
chan->synth = synth;
chan->channum = num;
chan->preset = NULL;
fluid_channel_init(chan);
fluid_channel_init_ctrl(chan,0);
return chan;
}
void
fluid_channel_init(fluid_channel_t* chan)
{
chan->prognum = 0;
chan->banknum = 0;
chan->sfontnum = 0;
if (chan->preset) delete_fluid_preset (chan->preset);
chan->preset = fluid_synth_find_preset(chan->synth, chan->banknum, chan->prognum);
chan->interp_method = FLUID_INTERP_DEFAULT;
chan->tuning = NULL;
chan->nrpn_select = 0;
chan->nrpn_active = 0;
}
/*
@param is_all_ctrl_off if nonzero, only resets some controllers, according to
http://www.midi.org/techspecs/rp15.php
*/
void
fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off)
{
int i;
chan->key_pressure = 0;
chan->channel_pressure = 0;
chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */
for (i = 0; i < GEN_LAST; i++) {
chan->gen[i] = 0.0f;
chan->gen_abs[i] = 0;
}
if (is_all_ctrl_off) {
for (i = 0; i < ALL_SOUND_OFF; i++) {
if (i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5) {
continue;
}
if (i >= SOUND_CTRL1 && i <= SOUND_CTRL10) {
continue;
}
if (i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB ||
i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB) {
continue;
}
SETCC(chan, i, 0);
}
}
else {
for (i = 0; i < 128; i++) {
SETCC(chan, i, 0);
}
}
/* Set RPN controllers to NULL state */
SETCC(chan, RPN_LSB, 127);
SETCC(chan, RPN_MSB, 127);
/* Set NRPN controllers to NULL state */
SETCC(chan, NRPN_LSB, 127);
SETCC(chan, NRPN_MSB, 127);
/* Expression (MSB & LSB) */
SETCC(chan, EXPRESSION_MSB, 127);
SETCC(chan, EXPRESSION_LSB, 127);
if (!is_all_ctrl_off) {
chan->pitch_wheel_sensitivity = 2; /* two semi-tones */
/* Just like panning, a value of 64 indicates no change for sound ctrls */
for (i = SOUND_CTRL1; i <= SOUND_CTRL10; i++) {
SETCC(chan, i, 64);
}
/* Volume / initial attenuation (MSB & LSB) */
SETCC(chan, VOLUME_MSB, 100);
SETCC(chan, VOLUME_LSB, 0);
/* Pan (MSB & LSB) */
SETCC(chan, PAN_MSB, 64);
SETCC(chan, PAN_LSB, 0);
/* Reverb */
/* SETCC(chan, EFFECTS_DEPTH1, 40); */
/* Note: although XG standard specifies the default amount of reverb to
be 40, most people preferred having it at zero.
See http://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */
}
}
void
fluid_channel_reset(fluid_channel_t* chan)
{
fluid_channel_init(chan);
fluid_channel_init_ctrl(chan,0);
}
/*
* delete_fluid_channel
*/
int
delete_fluid_channel(fluid_channel_t* chan)
{
if (chan->preset) delete_fluid_preset (chan->preset);
FLUID_FREE(chan);
return FLUID_OK;
}
/*
* fluid_channel_set_preset
*/
int
fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset)
{
fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum);
fluid_preset_notify(preset, FLUID_PRESET_SELECTED, chan->channum);
if (chan->preset) delete_fluid_preset (chan->preset);
chan->preset = preset;
return FLUID_OK;
}
/*
* fluid_channel_get_preset
*/
fluid_preset_t*
fluid_channel_get_preset(fluid_channel_t* chan)
{
return chan->preset;
}
/*
* fluid_channel_get_banknum
*/
unsigned int
fluid_channel_get_banknum(fluid_channel_t* chan)
{
return chan->banknum;
}
/*
* fluid_channel_set_prognum
*/
int
fluid_channel_set_prognum(fluid_channel_t* chan, int prognum)
{
chan->prognum = prognum;
return FLUID_OK;
}
/*
* fluid_channel_get_prognum
*/
int
fluid_channel_get_prognum(fluid_channel_t* chan)
{
return chan->prognum;
}
/*
* fluid_channel_set_banknum
*/
int
fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int banknum)
{
chan->banknum = banknum;
return FLUID_OK;
}
/*
* fluid_channel_cc
*/
int
fluid_channel_cc(fluid_channel_t* chan, int num, int value)
{
chan->cc[num] = value;
switch (num) {
case SUSTAIN_SWITCH:
{
if (value < 64) {
/* printf("** sustain off\n"); */
fluid_synth_damp_voices(chan->synth, chan->channum);
} else {
/* printf("** sustain on\n"); */
}
}
break;
case BANK_SELECT_MSB:
{
chan->bank_msb = (unsigned char) (value & 0x7f);
/* printf("** bank select msb recieved: %d\n", value); */
/* I fixed the handling of a MIDI bank select controller 0,
e.g., bank select MSB (or "coarse" bank select according to
my spec). Prior to this fix a channel's bank number was only
changed upon reception of MIDI bank select controller 32,
e.g, bank select LSB (or "fine" bank-select according to my
spec). [KLE]
FIXME: is this correct? [PH] */
fluid_channel_set_banknum(chan, (unsigned int)(value & 0x7f)); /* KLE */
}
break;
case BANK_SELECT_LSB:
{
/* FIXME: according to the Downloadable Sounds II specification,
bit 31 should be set when we receive the message on channel
10 (drum channel) */
fluid_channel_set_banknum(chan, (((unsigned int) value & 0x7f)
+ ((unsigned int) chan->bank_msb << 7)));
}
break;
case ALL_NOTES_OFF:
fluid_synth_all_notes_off(chan->synth, chan->channum);
break;
case ALL_SOUND_OFF:
fluid_synth_all_sounds_off(chan->synth, chan->channum);
break;
case ALL_CTRL_OFF:
fluid_channel_init_ctrl(chan,1);
fluid_synth_modulate_voices_all(chan->synth, chan->channum);
break;
case DATA_ENTRY_MSB:
{
int data = (value << 7) + chan->cc[DATA_ENTRY_LSB];
if (chan->nrpn_active) /* NRPN is active? */
{
/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
if ((chan->cc[NRPN_MSB] == 120) && (chan->cc[NRPN_LSB] < 100))
{
if (chan->nrpn_select < GEN_LAST)
{
float val = fluid_gen_scale_nrpn(chan->nrpn_select, data);
fluid_synth_set_gen(chan->synth, chan->channum, chan->nrpn_select, val);
}
chan->nrpn_select = 0; /* Reset to 0 */
}
}
else if (chan->cc[RPN_MSB] == 0) /* RPN is active: MSB = 0? */
{
switch (chan->cc[RPN_LSB])
{
case RPN_PITCH_BEND_RANGE:
fluid_channel_pitch_wheel_sens (chan, value); /* Set bend range in semitones */
/* FIXME - Handle LSB? (Fine bend range in cents) */
break;
case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over +/-1 semitone (+/- 100 cents, 8192 = center) */
fluid_synth_set_gen(chan->synth, chan->channum, GEN_FINETUNE,
(data - 8192) / 8192.0 * 100.0);
break;
case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */
fluid_synth_set_gen(chan->synth, chan->channum, GEN_COARSETUNE,
value - 64);
break;
case RPN_TUNING_PROGRAM_CHANGE:
break;
case RPN_TUNING_BANK_SELECT:
break;
case RPN_MODULATION_DEPTH_RANGE:
break;
}
}
break;
}
case NRPN_MSB:
chan->cc[NRPN_LSB] = 0;
chan->nrpn_select = 0;
chan->nrpn_active = 1;
break;
case NRPN_LSB:
/* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
if (chan->cc[NRPN_MSB] == 120) {
if (value == 100) {
chan->nrpn_select += 100;
} else if (value == 101) {
chan->nrpn_select += 1000;
} else if (value == 102) {
chan->nrpn_select += 10000;
} else if (value < 100) {
chan->nrpn_select += value;
}
}
chan->nrpn_active = 1;
break;
case RPN_MSB:
case RPN_LSB:
chan->nrpn_active = 0;
break;
default:
fluid_synth_modulate_voices(chan->synth, chan->channum, 1, num);
}
return FLUID_OK;
}
/*
* fluid_channel_get_cc
*/
int
fluid_channel_get_cc(fluid_channel_t* chan, int num)
{
return ((num >= 0) && (num < 128))? chan->cc[num] : 0;
}
/*
* fluid_channel_pressure
*/
int
fluid_channel_pressure(fluid_channel_t* chan, int val)
{
chan->channel_pressure = val;
fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_CHANNELPRESSURE);
return FLUID_OK;
}
/*
* fluid_channel_pitch_bend
*/
int
fluid_channel_pitch_bend(fluid_channel_t* chan, int val)
{
chan->pitch_bend = val;
fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEEL);
return FLUID_OK;
}
/*
* fluid_channel_pitch_wheel_sens
*/
int
fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val)
{
chan->pitch_wheel_sensitivity = val;
fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEELSENS);
return FLUID_OK;
}
/*
* fluid_channel_get_num
*/
int
fluid_channel_get_num(fluid_channel_t* chan)
{
return chan->channum;
}
/* Purpose:
* Sets the index of the interpolation method used on this channel,
* as in fluid_interp in fluidlite.h
*/
void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method)
{
chan->interp_method = new_method;
}
/* Purpose:
* Returns the index of the interpolation method used on this channel,
* as in fluid_interp in fluidlite.h
*/
int fluid_channel_get_interp_method(fluid_channel_t* chan)
{
return chan->interp_method;
}
unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan)
{
return chan->sfontnum;
}
int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfontnum)
{
chan->sfontnum = sfontnum;
return FLUID_OK;
}

View file

@ -0,0 +1,110 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_CHAN_H
#define _FLUID_CHAN_H
#include "fluidsynth_priv.h"
#include "fluid_midi.h"
#include "fluid_tuning.h"
/*
* fluid_channel_t
*/
struct _fluid_channel_t
{
int channum;
unsigned int sfontnum;
unsigned int banknum;
unsigned int prognum;
fluid_preset_t* preset;
fluid_synth_t* synth;
short key_pressure;
short channel_pressure;
short pitch_bend;
short pitch_wheel_sensitivity;
/* controller values */
short cc[128];
/* cached values of last MSB values of MSB/LSB controllers */
unsigned char bank_msb;
int interp_method;
/* the micro-tuning */
fluid_tuning_t* tuning;
/* NRPN system */
short nrpn_select;
short nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */
/* The values of the generators, set by NRPN messages, or by
* fluid_synth_set_gen(), are cached in the channel so they can be
* applied to future notes. They are copied to a voice's generators
* in fluid_voice_init(), wihich calls fluid_gen_init(). */
fluid_real_t gen[GEN_LAST];
/* By default, the NRPN values are relative to the values of the
* generators set in the SoundFont. For example, if the NRPN
* specifies an attack of 100 msec then 100 msec will be added to the
* combined attack time of the sound font and the modulators.
*
* However, it is useful to be able to specify the generator value
* absolutely, completely ignoring the generators of the sound font
* and the values of modulators. The gen_abs field, is a boolean
* flag indicating whether the NRPN value is absolute or not.
*/
char gen_abs[GEN_LAST];
};
fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num);
int delete_fluid_channel(fluid_channel_t* chan);
void fluid_channel_init(fluid_channel_t* chan);
void fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off);
void fluid_channel_reset(fluid_channel_t* chan);
int fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset);
fluid_preset_t* fluid_channel_get_preset(fluid_channel_t* chan);
unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan);
int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfont);
unsigned int fluid_channel_get_banknum(fluid_channel_t* chan);
int fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int bank);
int fluid_channel_set_prognum(fluid_channel_t* chan, int prognum);
int fluid_channel_get_prognum(fluid_channel_t* chan);
int fluid_channel_cc(fluid_channel_t* chan, int ctrl, int val);
int fluid_channel_pressure(fluid_channel_t* chan, int val);
int fluid_channel_pitch_bend(fluid_channel_t* chan, int val);
int fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val);
int fluid_channel_get_cc(fluid_channel_t* chan, int num);
int fluid_channel_get_num(fluid_channel_t* chan);
void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method);
int fluid_channel_get_interp_method(fluid_channel_t* chan);
#define fluid_channel_set_tuning(_c, _t) { (_c)->tuning = _t; }
#define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL)
#define fluid_channel_get_tuning(_c) ((_c)->tuning)
#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64)
#define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; }
#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n])
#define fluid_channel_get_gen_abs(_c, _n) ((_c)->gen_abs[_n])
#define fluid_channel_get_min_note_length_ticks(chan) \
((chan)->synth->min_note_length_ticks)
#endif /* _FLUID_CHAN_H */

View file

@ -0,0 +1,606 @@
/*
* August 24, 1998
* Copyright (C) 1998 Juergen Mueller And Sundry Contributors
* This source code is freely redistributable and may be used for
* any purpose. This copyright notice must be maintained.
* Juergen Mueller And Sundry Contributors are not responsible for
* the consequences of using this software.
*/
/*
CHANGES
- Adapted for fluidsynth, Peter Hanappe, March 2002
- Variable delay line implementation using bandlimited
interpolation, code reorganization: Markus Nentwig May 2002
*/
/*
* Chorus effect.
*
* Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ):
*
* * gain-in ___
* ibuff -----+--------------------------------------------->| |
* | _________ | |
* | | | * level 1 | |
* +---->| delay 1 |----------------------------->| |
* | |_________| | |
* | /|\ | |
* : | | |
* : +-----------------+ +--------------+ | + |
* : | Delay control 1 |<--| mod. speed 1 | | |
* : +-----------------+ +--------------+ | |
* | _________ | |
* | | | * level n | |
* +---->| delay n |----------------------------->| |
* |_________| | |
* /|\ |___|
* | |
* +-----------------+ +--------------+ | * gain-out
* | Delay control n |<--| mod. speed n | |
* +-----------------+ +--------------+ +----->obuff
*
*
* The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n).
*
* The delay of each block is modulated between 0..depth ms
*
*/
/* Variable delay line implementation
* ==================================
*
* The modulated delay needs the value of the delayed signal between
* samples. A lowpass filter is used to obtain intermediate values
* between samples (bandlimited interpolation). The sample pulse
* train is convoluted with the impulse response of the low pass
* filter (sinc function). To make it work with a small number of
* samples, the sinc function is windowed (Hamming window).
*
*/
#include "fluid_chorus.h"
#include "fluid_sys.h"
#define MAX_CHORUS 99
#define MAX_DELAY 100
#define MAX_DEPTH 10
#define MIN_SPEED_HZ 0.29
#define MAX_SPEED_HZ 5
/* Length of one delay line in samples:
* Set through MAX_SAMPLES_LN2.
* For example:
* MAX_SAMPLES_LN2=12
* => MAX_SAMPLES=pow(2,12)=4096
* => MAX_SAMPLES_ANDMASK=4095
*/
#define MAX_SAMPLES_LN2 12
#define MAX_SAMPLES (1 << (MAX_SAMPLES_LN2-1))
#define MAX_SAMPLES_ANDMASK (MAX_SAMPLES-1)
/* Interpolate how many steps between samples? Must be power of two
For example: 8 => use a resolution of 256 steps between any two
samples
*/
#define INTERPOLATION_SUBSAMPLES_LN2 8
#define INTERPOLATION_SUBSAMPLES (1 << (INTERPOLATION_SUBSAMPLES_LN2-1))
#define INTERPOLATION_SUBSAMPLES_ANDMASK (INTERPOLATION_SUBSAMPLES-1)
/* Use how many samples for interpolation? Must be odd. '7' sounds
relatively clean, when listening to the modulated delay signal
alone. For a demo on aliasing try '1' With '3', the aliasing is
still quite pronounced for some input frequencies
*/
#define INTERPOLATION_SAMPLES 5
/* Private data for SKEL file */
struct _fluid_chorus_t {
/* Store the values between fluid_chorus_set_xxx and fluid_chorus_update
* Logic behind this:
* - both 'parameter' and 'new_parameter' hold the same value.
* - To change the chorus settings, 'new_parameter' is modified and
* fluid_chorus_update is called.
* - If the new value is valid, it is copied to 'parameter'.
* - If it is invalid, 'new_parameter' is restored to 'parameter'.
*/
int type; /* current value */
int new_type; /* next value, if parameter check is OK */
fluid_real_t depth_ms; /* current value */
fluid_real_t new_depth_ms; /* next value, if parameter check is OK */
fluid_real_t level; /* current value */
fluid_real_t new_level; /* next value, if parameter check is OK */
fluid_real_t speed_Hz; /* current value */
fluid_real_t new_speed_Hz; /* next value, if parameter check is OK */
int number_blocks; /* current value */
int new_number_blocks; /* next value, if parameter check is OK */
fluid_real_t *chorusbuf;
int counter;
long phase[MAX_CHORUS];
long modulation_period_samples;
int *lookup_tab;
fluid_real_t sample_rate;
/* sinc lookup table */
fluid_real_t sinc_table[INTERPOLATION_SAMPLES][INTERPOLATION_SUBSAMPLES];
};
void fluid_chorus_triangle(int *buf, int len, int depth);
void fluid_chorus_sine(int *buf, int len, int depth);
fluid_chorus_t*
new_fluid_chorus(fluid_real_t sample_rate)
{
int i; int ii;
fluid_chorus_t* chorus;
chorus = FLUID_NEW(fluid_chorus_t);
if (chorus == NULL) {
fluid_log(FLUID_PANIC, "chorus: Out of memory");
return NULL;
}
FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t));
chorus->sample_rate = sample_rate;
/* Lookup table for the SI function (impulse response of an ideal low pass) */
/* i: Offset in terms of whole samples */
for (i = 0; i < INTERPOLATION_SAMPLES; i++){
/* ii: Offset in terms of fractional samples ('subsamples') */
for (ii = 0; ii < INTERPOLATION_SUBSAMPLES; ii++){
/* Move the origin into the center of the table */
double i_shifted = ((double) i- ((double) INTERPOLATION_SAMPLES) / 2.
+ (double) ii / (double) INTERPOLATION_SUBSAMPLES);
if (fabs(i_shifted) < 0.000001) {
/* sinc(0) cannot be calculated straightforward (limit needed
for 0/0) */
chorus->sinc_table[i][ii] = (fluid_real_t)1.;
} else {
chorus->sinc_table[i][ii] = (fluid_real_t)sin(i_shifted * M_PI) / (M_PI * i_shifted);
/* Hamming window */
chorus->sinc_table[i][ii] *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * M_PI * i_shifted / (fluid_real_t)INTERPOLATION_SAMPLES));
};
};
};
/* allocate lookup tables */
chorus->lookup_tab = FLUID_ARRAY(int, (int) (chorus->sample_rate / MIN_SPEED_HZ));
if (chorus->lookup_tab == NULL) {
fluid_log(FLUID_PANIC, "chorus: Out of memory");
goto error_recovery;
}
/* allocate sample buffer */
chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES);
if (chorus->chorusbuf == NULL) {
fluid_log(FLUID_PANIC, "chorus: Out of memory");
goto error_recovery;
}
if (fluid_chorus_init(chorus) != FLUID_OK){
goto error_recovery;
};
return chorus;
error_recovery:
delete_fluid_chorus(chorus);
return NULL;
}
int
fluid_chorus_init(fluid_chorus_t* chorus)
{
int i;
for (i = 0; i < MAX_SAMPLES; i++) {
chorus->chorusbuf[i] = 0.0;
}
/* initialize the chorus with the default settings */
fluid_chorus_set_nr(chorus, FLUID_CHORUS_DEFAULT_N);
fluid_chorus_set_level(chorus, FLUID_CHORUS_DEFAULT_LEVEL);
fluid_chorus_set_speed_Hz(chorus, FLUID_CHORUS_DEFAULT_SPEED);
fluid_chorus_set_depth_ms(chorus, FLUID_CHORUS_DEFAULT_DEPTH);
fluid_chorus_set_type(chorus, FLUID_CHORUS_MOD_SINE);
return fluid_chorus_update(chorus);
}
/* Purpose:
* Sets the number of stages.
* Requires call to fluid_chorus_update afterwards.
* Range checking is performed there.*/
void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr)
{
chorus->new_number_blocks = nr;
}
/* Purpose:
* API function, read the current state of the chorus
*/
int fluid_chorus_get_nr(fluid_chorus_t* chorus)
{
return chorus->number_blocks;
};
/* Purpose:
* Sets the mixing level of the signal from each delay line (linear).
* Requires calling fluid_chorus_update afterwards.*/
void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level)
{
chorus->new_level = level;
}
/* Purpose:
* API function, read the current state of the chorus
*/
fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus)
{
return chorus->level;
};
/* Purpose:
* Sets the modulation frequency.
* Requires call to fluid_chorus_update afterwards.
* Range checking is performed there.*/
void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz)
{
chorus->new_speed_Hz = speed_Hz;
}
/* Purpose:
* API function, read the current state of the chorus
*/
fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus)
{
return chorus->speed_Hz;
};
/* Purpose:
* Sets the modulation depth in ms.
* Requires call to fluid_chorus_update afterwards.
* Range checking is performed there.*/
void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms)
{
chorus->new_depth_ms=depth_ms;
}
/* Purpose:
* API function, read the current state of the chorus
*/
fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus)
{
return chorus->depth_ms;
};
/* Purpose:
* Sets the type of the modulation waveform.
* Requires call to fluid_chorus_update afterwards.
* Check for meaningful values is performed there.*/
void fluid_chorus_set_type(fluid_chorus_t* chorus, int type)
{
chorus->new_type=type;
}
/* Purpose:
* API function, read the current state of the chorus
*/
int fluid_chorus_get_type(fluid_chorus_t* chorus)
{
return chorus->type;
};
void
delete_fluid_chorus(fluid_chorus_t* chorus)
{
if (chorus == NULL) {
return;
}
if (chorus->chorusbuf != NULL) {
FLUID_FREE(chorus->chorusbuf);
}
if (chorus->lookup_tab != NULL) {
FLUID_FREE(chorus->lookup_tab);
}
FLUID_FREE(chorus);
}
/* Purpose:
* Calculates the internal chorus parameters using the settings from
* fluid_chorus_set_xxx. */
int
fluid_chorus_update(fluid_chorus_t* chorus)
{
int i;
int modulation_depth_samples;
if (chorus->new_number_blocks < 0) {
fluid_log(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0.");
chorus->new_number_blocks = 0;
} else if (chorus->new_number_blocks > MAX_CHORUS) {
fluid_log(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.",
MAX_CHORUS);
chorus->new_number_blocks = MAX_CHORUS;
};
if (chorus->new_speed_Hz < MIN_SPEED_HZ) {
fluid_log(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.",
(double) MIN_SPEED_HZ);
chorus->new_speed_Hz = MIN_SPEED_HZ;
} else if (chorus->new_speed_Hz > MAX_SPEED_HZ) {
fluid_log(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.",
(double) MAX_SPEED_HZ);
chorus->new_speed_Hz = MAX_SPEED_HZ;
}
if (chorus->new_depth_ms < 0.0) {
fluid_log(FLUID_WARN, "chorus: depth must be positive! Setting value to 0.");
chorus->new_depth_ms = 0.0;
}
/* Depth: Check for too high value through modulation_depth_samples. */
if (chorus->new_level < 0.0) {
fluid_log(FLUID_WARN, "chorus: level must be positive! Setting value to 0.");
chorus->new_level = 0.0;
} else if (chorus->new_level > 10) {
fluid_log(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! "
"Setting it to 0.1.");
chorus->new_level = 0.1;
}
/* The modulating LFO goes through a full period every x samples: */
chorus->modulation_period_samples = chorus->sample_rate / chorus->new_speed_Hz;
/* The variation in delay time is x: */
modulation_depth_samples = (int)
(chorus->new_depth_ms / 1000.0 /* convert modulation depth in ms to s*/
* chorus->sample_rate);
if (modulation_depth_samples > MAX_SAMPLES) {
fluid_log(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES);
modulation_depth_samples = MAX_SAMPLES;
}
/* initialize LFO table */
if (chorus->type == FLUID_CHORUS_MOD_SINE) {
fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples,
modulation_depth_samples);
} else if (chorus->type == FLUID_CHORUS_MOD_TRIANGLE) {
fluid_chorus_triangle(chorus->lookup_tab, chorus->modulation_period_samples,
modulation_depth_samples);
} else {
fluid_log(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave.");
chorus->type = FLUID_CHORUS_MOD_SINE;
fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples,
modulation_depth_samples);
};
for (i = 0; i < chorus->number_blocks; i++) {
/* Set the phase of the chorus blocks equally spaced */
chorus->phase[i] = (int) ((double) chorus->modulation_period_samples
* (double) i / (double) chorus->number_blocks);
}
/* Start of the circular buffer */
chorus->counter = 0;
chorus->type = chorus->new_type;
chorus->depth_ms = chorus->new_depth_ms;
chorus->level = chorus->new_level;
chorus->speed_Hz = chorus->new_speed_Hz;
chorus->number_blocks = chorus->new_number_blocks;
return FLUID_OK;
/* failure: */
/* Note: This lives on the assumption, that the last chorus values were correct.
* If not, this will loop forever and a day. */
/* fluid_log(FLUID_WARN, "chorus: Restoring last good settings"); */
/* chorus->new_type = chorus->type; */
/* chorus->new_depth_ms = chorus->depth_ms; */
/* chorus->new_level = chorus->level; */
/* chorus->new_speed_Hz = chorus->speed_Hz; */
/* chorus->new_number_blocks = chorus->number_blocks; */
/* return FLUID_FAILED; */
}
void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out)
{
int sample_index;
int i;
fluid_real_t d_in, d_out;
for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
d_in = in[sample_index];
d_out = 0.0f;
# if 0
/* Debug: Listen to the chorus signal only */
left_out[sample_index]=0;
right_out[sample_index]=0;
#endif
/* Write the current sample into the circular buffer */
chorus->chorusbuf[chorus->counter] = d_in;
for (i = 0; i < chorus->number_blocks; i++) {
int ii;
/* Calculate the delay in subsamples for the delay line of chorus block nr. */
/* The value in the lookup table is so, that this expression
* will always be positive. It will always include a number of
* full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to
* remain positive at all times. */
int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
- chorus->lookup_tab[chorus->phase[i]]);
int pos_samples = pos_subsamples/INTERPOLATION_SUBSAMPLES;
/* modulo divide by INTERPOLATION_SUBSAMPLES */
pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){
/* Add the delayed signal to the chorus sum d_out Note: The
* delay in the delay line moves backwards for increasing
* delay!*/
/* The & in chorusbuf[...] is equivalent to a division modulo
MAX_SAMPLES, only faster. */
d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
* chorus->sinc_table[ii][pos_subsamples];
pos_samples--;
};
/* Cycle the phase of the modulating LFO */
chorus->phase[i]++;
chorus->phase[i] %= (chorus->modulation_period_samples);
} /* foreach chorus block */
d_out *= chorus->level;
/* Add the chorus sum d_out to output */
left_out[sample_index] += d_out;
right_out[sample_index] += d_out;
/* Move forward in circular buffer */
chorus->counter++;
chorus->counter %= MAX_SAMPLES;
} /* foreach sample */
}
/* Duplication of code ... (replaces sample data instead of mixing) */
void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out)
{
int sample_index;
int i;
fluid_real_t d_in, d_out;
for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
d_in = in[sample_index];
d_out = 0.0f;
# if 0
/* Debug: Listen to the chorus signal only */
left_out[sample_index]=0;
right_out[sample_index]=0;
#endif
/* Write the current sample into the circular buffer */
chorus->chorusbuf[chorus->counter] = d_in;
for (i = 0; i < chorus->number_blocks; i++) {
int ii;
/* Calculate the delay in subsamples for the delay line of chorus block nr. */
/* The value in the lookup table is so, that this expression
* will always be positive. It will always include a number of
* full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to
* remain positive at all times. */
int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
- chorus->lookup_tab[chorus->phase[i]]);
int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES;
/* modulo divide by INTERPOLATION_SUBSAMPLES */
pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){
/* Add the delayed signal to the chorus sum d_out Note: The
* delay in the delay line moves backwards for increasing
* delay!*/
/* The & in chorusbuf[...] is equivalent to a division modulo
MAX_SAMPLES, only faster. */
d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
* chorus->sinc_table[ii][pos_subsamples];
pos_samples--;
};
/* Cycle the phase of the modulating LFO */
chorus->phase[i]++;
chorus->phase[i] %= (chorus->modulation_period_samples);
} /* foreach chorus block */
d_out *= chorus->level;
/* Store the chorus sum d_out to output */
left_out[sample_index] = d_out;
right_out[sample_index] = d_out;
/* Move forward in circular buffer */
chorus->counter++;
chorus->counter %= MAX_SAMPLES;
} /* foreach sample */
}
/* Purpose:
*
* Calculates a modulation waveform (sine) Its value ( modulo
* MAXSAMPLES) varies between 0 and depth*INTERPOLATION_SUBSAMPLES.
* Its period length is len. The waveform data will be used modulo
* MAXSAMPLES only. Since MAXSAMPLES is substracted from the waveform
* a couple of times here, the resulting (current position in
* buffer)-(waveform sample) will always be positive.
*/
void fluid_chorus_sine(int *buf, int len, int depth)
{
int i;
double val;
for (i = 0; i < len; i++) {
val = sin((double) i / (double)len * 2.0 * M_PI);
buf[i] = (int) ((1.0 + val) * (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES);
buf[i] -= 3* MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
// printf("%i %i\n",i,buf[i]);
}
}
/* Purpose:
* Calculates a modulation waveform (triangle)
* See fluid_chorus_sine for comments.
*/
void fluid_chorus_triangle(int *buf, int len, int depth)
{
int i=0;
int ii=len-1;
double val;
double val2;
while (i <= ii){
val = i * 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES;
val2= (int) (val + 0.5) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
buf[i++] = (int) val2;
buf[ii--] = (int) val2;
}
}
void
fluid_chorus_reset(fluid_chorus_t* chorus)
{
fluid_chorus_init(chorus);
}

View file

@ -0,0 +1,56 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_CHORUS_H
#define _FLUID_CHORUS_H
#include "fluidsynth_priv.h"
typedef struct _fluid_chorus_t fluid_chorus_t;
/*
* chorus
*/
fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate);
void delete_fluid_chorus(fluid_chorus_t* chorus);
void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);
void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);
int fluid_chorus_init(fluid_chorus_t* chorus);
void fluid_chorus_reset(fluid_chorus_t* chorus);
void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr);
void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level);
void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz);
void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms);
void fluid_chorus_set_type(fluid_chorus_t* chorus, int type);
int fluid_chorus_update(fluid_chorus_t* chorus);
int fluid_chorus_get_nr(fluid_chorus_t* chorus);
fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus);
fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus);
fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus);
int fluid_chorus_get_type(fluid_chorus_t* chorus);
#endif /* _FLUID_CHORUS_H */

View file

@ -0,0 +1,30 @@
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* Define to activate debugging message */
#undef DEBUG
/* Version number of package */
#define VERSION "1.0.9"
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
#undef WORDS_BIGENDIAN
#define SF3_SUPPORT 0
#define WITH_FLOAT 1
#define HAVE_STRING_H 1
#define HAVE_STDLIB_H 1
#define HAVE_STDIO_H 1
#define HAVE_MATH_H 1
#define HAVE_STDARG_H 1
#define HAVE_FCNTL_H 1
#define HAVE_LIMITS_H 1
//#pragma warning(disable : 4244)
//#pragma warning(disable : 4101)
//#pragma warning(disable : 4305)
//#pragma warning(disable : 4996)

View file

@ -0,0 +1,320 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_conv.h"
/* conversion tables */
fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
fluid_real_t fluid_atten2amp_tab[FLUID_ATTEN_AMP_SIZE];
fluid_real_t fluid_posbp_tab[128];
fluid_real_t fluid_concave_tab[128];
fluid_real_t fluid_convex_tab[128];
fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
/*
* void fluid_synth_init
*
* Does all the initialization for this module.
*/
void
fluid_conversion_config(void)
{
int i;
double x;
for (i = 0; i < FLUID_CENTS_HZ_SIZE; i++) {
fluid_ct2hz_tab[i] = (fluid_real_t) pow(2.0, (double) i / 1200.0);
}
/* centibels to amplitude conversion
* Note: SF2.01 section 8.1.3: Initial attenuation range is
* between 0 and 144 dB. Therefore a negative attenuation is
* not allowed.
*/
for (i = 0; i < FLUID_CB_AMP_SIZE; i++) {
fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0);
}
/* NOTE: EMU8k and EMU10k devices don't conform to the SoundFont
* specification in regards to volume attenuation. The below calculation
* is an approx. equation for generating a table equivelant to the
* cb_to_amp_table[] in tables.c of the TiMidity++ source, which I'm told
* was generated from device testing. By the spec this should be centibels.
*/
for (i = 0; i < FLUID_ATTEN_AMP_SIZE; i++) {
fluid_atten2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / FLUID_ATTEN_POWER_FACTOR);
}
/* initialize the conversion tables (see fluid_mod.c
fluid_mod_get_value cases 4 and 8) */
/* concave unipolar positive transform curve */
fluid_concave_tab[0] = 0.0;
fluid_concave_tab[127] = 1.0;
/* convex unipolar positive transform curve */
fluid_convex_tab[0] = 0;
fluid_convex_tab[127] = 1.0;
x = log10(128.0 / 127.0);
/* There seems to be an error in the specs. The equations are
implemented according to the pictures on SF2.01 page 73. */
for (i = 1; i < 127; i++) {
x = -20.0 / 96.0 * log((i * i) / (127.0 * 127.0)) / log(10.0);
fluid_convex_tab[i] = (fluid_real_t) (1.0 - x);
fluid_concave_tab[127 - i] = (fluid_real_t) x;
}
/* initialize the pan conversion table */
x = PI / 2.0 / (FLUID_PAN_SIZE - 1.0);
for (i = 0; i < FLUID_PAN_SIZE; i++) {
fluid_pan_tab[i] = (fluid_real_t) sin(i * x);
}
}
/*
* fluid_ct2hz
*/
fluid_real_t
fluid_ct2hz_real(fluid_real_t cents)
{
if (cents < 0)
return (fluid_real_t) 1.0;
else if (cents < 900) {
return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int) (cents + 300)];
} else if (cents < 2100) {
return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int) (cents - 900)];
} else if (cents < 3300) {
return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int) (cents - 2100)];
} else if (cents < 4500) {
return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int) (cents - 3300)];
} else if (cents < 5700) {
return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int) (cents - 4500)];
} else if (cents < 6900) {
return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int) (cents - 5700)];
} else if (cents < 8100) {
return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int) (cents - 6900)];
} else if (cents < 9300) {
return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int) (cents - 8100)];
} else if (cents < 10500) {
return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int) (cents - 9300)];
} else if (cents < 11700) {
return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int) (cents - 10500)];
} else if (cents < 12900) {
return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int) (cents - 11700)];
} else if (cents < 14100) {
return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int) (cents - 12900)];
} else {
return (fluid_real_t) 1.0; /* some loony trying to make you deaf */
}
}
/*
* fluid_ct2hz
*/
fluid_real_t
fluid_ct2hz(fluid_real_t cents)
{
/* Filter fc limit: SF2.01 page 48 # 8 */
if (cents >= 13500){
cents = 13500; /* 20 kHz */
} else if (cents < 1500){
cents = 1500; /* 20 Hz */
}
return fluid_ct2hz_real(cents);
}
/*
* fluid_cb2amp
*
* in: a value between 0 and 960, 0 is no attenuation
* out: a value between 1 and 0
*/
fluid_real_t
fluid_cb2amp(fluid_real_t cb)
{
/*
* cb: an attenuation in 'centibels' (1/10 dB)
* SF2.01 page 49 # 48 limits it to 144 dB.
* 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit.
*/
/* minimum attenuation: 0 dB */
if (cb < 0) {
return 1.0;
}
if (cb >= FLUID_CB_AMP_SIZE) {
return 0.0;
}
return fluid_cb2amp_tab[(int) cb];
}
/*
* fluid_atten2amp
*
* in: a value between 0 and 1440, 0 is no attenuation
* out: a value between 1 and 0
*
* Note: Volume attenuation is supposed to be centibels but EMU8k/10k don't
* follow this. Thats the reason for separate fluid_cb2amp and fluid_atten2amp.
*/
fluid_real_t
fluid_atten2amp(fluid_real_t atten)
{
if (atten < 0) return 1.0;
else if (atten >= FLUID_ATTEN_AMP_SIZE) return 0.0;
else return fluid_atten2amp_tab[(int) atten];
}
/*
* fluid_tc2sec_delay
*/
fluid_real_t
fluid_tc2sec_delay(fluid_real_t tc)
{
/* SF2.01 section 8.1.2 items 21, 23, 25, 33
* SF2.01 section 8.1.3 items 21, 23, 25, 33
*
* The most negative number indicates a delay of 0. Range is limited
* from -12000 to 5000 */
if (tc <= -32768.0f) {
return (fluid_real_t) 0.0f;
};
if (tc < -12000.) {
tc = (fluid_real_t) -12000.0f;
}
if (tc > 5000.0f) {
tc = (fluid_real_t) 5000.0f;
}
return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
}
/*
* fluid_tc2sec_attack
*/
fluid_real_t
fluid_tc2sec_attack(fluid_real_t tc)
{
/* SF2.01 section 8.1.2 items 26, 34
* SF2.01 section 8.1.3 items 26, 34
* The most negative number indicates a delay of 0
* Range is limited from -12000 to 8000 */
if (tc<=-32768.){return (fluid_real_t) 0.0;};
if (tc<-12000.){tc=(fluid_real_t) -12000.0;};
if (tc>8000.){tc=(fluid_real_t) 8000.0;};
return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
}
/*
* fluid_tc2sec
*/
fluid_real_t
fluid_tc2sec(fluid_real_t tc)
{
/* No range checking here! */
return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
}
/*
* fluid_tc2sec_release
*/
fluid_real_t
fluid_tc2sec_release(fluid_real_t tc)
{
/* SF2.01 section 8.1.2 items 30, 38
* SF2.01 section 8.1.3 items 30, 38
* No 'most negative number' rule here!
* Range is limited from -12000 to 8000 */
if (tc<=-32768.){return (fluid_real_t) 0.0;};
if (tc<-12000.){tc=(fluid_real_t) -12000.0;};
if (tc>8000.){tc=(fluid_real_t) 8000.0;};
return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
}
/*
* fluid_act2hz
*
* Convert from absolute cents to Hertz
*/
fluid_real_t
fluid_act2hz(fluid_real_t c)
{
return (fluid_real_t) (8.176 * pow(2.0, (double) c / 1200.0));
}
/*
* fluid_hz2ct
*
* Convert from Hertz to cents
*/
fluid_real_t
fluid_hz2ct(fluid_real_t f)
{
return (fluid_real_t) (6900 + 1200 * log(f / 440.0) / log(2.0));
}
/*
* fluid_pan
*/
fluid_real_t
fluid_pan(fluid_real_t c, int left)
{
if (left) {
c = -c;
}
if (c < -500) {
return (fluid_real_t) 0.0;
} else if (c > 500) {
return (fluid_real_t) 1.0;
} else {
return fluid_pan_tab[(int) (c + 500)];
}
}
/*
* fluid_concave
*/
fluid_real_t
fluid_concave(fluid_real_t val)
{
if (val < 0) {
return 0;
} else if (val > 127) {
return 1;
}
return fluid_concave_tab[(int) val];
}
/*
* fluid_convex
*/
fluid_real_t
fluid_convex(fluid_real_t val)
{
if (val < 0) {
return 0;
} else if (val > 127) {
return 1;
}
return fluid_convex_tab[(int) val];
}

View file

@ -0,0 +1,63 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_CONV_H
#define _FLUID_CONV_H
#include "fluidsynth_priv.h"
#define FLUID_CENTS_HZ_SIZE 1200
#define FLUID_VEL_CB_SIZE 128
#define FLUID_CB_AMP_SIZE 961
#define FLUID_ATTEN_AMP_SIZE 1441
#define FLUID_PAN_SIZE 1002
/* EMU 8k/10k don't follow spec in regards to volume attenuation.
* This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR).
* By the standard this should be -200.0. */
/* 07/11/2008 modified by S. Christian Collins for increased velocity sensitivity. Now it equals the response of EMU10K1 programming.*/
#define FLUID_ATTEN_POWER_FACTOR (-200.0) /* was (-531.509)*/
void fluid_conversion_config(void);
fluid_real_t fluid_ct2hz_real(fluid_real_t cents);
fluid_real_t fluid_ct2hz(fluid_real_t cents);
fluid_real_t fluid_cb2amp(fluid_real_t cb);
fluid_real_t fluid_atten2amp(fluid_real_t atten);
fluid_real_t fluid_tc2sec(fluid_real_t tc);
fluid_real_t fluid_tc2sec_delay(fluid_real_t tc);
fluid_real_t fluid_tc2sec_attack(fluid_real_t tc);
fluid_real_t fluid_tc2sec_release(fluid_real_t tc);
fluid_real_t fluid_act2hz(fluid_real_t c);
fluid_real_t fluid_hz2ct(fluid_real_t c);
fluid_real_t fluid_pan(fluid_real_t c, int left);
fluid_real_t fluid_concave(fluid_real_t val);
fluid_real_t fluid_convex(fluid_real_t val);
extern fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
extern fluid_real_t fluid_vel2cb_tab[FLUID_VEL_CB_SIZE];
extern fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
extern fluid_real_t fluid_posbp_tab[128];
extern fluid_real_t fluid_concave_tab[128];
extern fluid_real_t fluid_convex_tab[128];
extern fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
#endif /* _FLUID_CONV_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,608 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_DEFSFONT_H
#define _FLUID_DEFSFONT_H
#include <stdint.h>
#include "fluidlite.h"
#include "fluidsynth_priv.h"
#include "fluid_list.h"
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/*-----------------------------------sfont.h----------------------------*/
#define SF_SAMPMODES_LOOP 1
#define SF_SAMPMODES_UNROLL 2
#define SF_MIN_SAMPLERATE 400
#define SF_MAX_SAMPLERATE 50000
#define SF_MIN_SAMPLE_LENGTH 32
/* Sound Font structure defines */
typedef struct _SFVersion
{ /* version structure */
unsigned short major;
unsigned short minor;
}
SFVersion;
typedef struct _SFMod
{ /* Modulator structure */
unsigned short src; /* source modulator */
unsigned short dest; /* destination generator */
signed short amount; /* signed, degree of modulation */
unsigned short amtsrc; /* second source controls amnt of first */
unsigned short trans; /* transform applied to source */
}
SFMod;
typedef union _SFGenAmount
{ /* Generator amount structure */
signed short sword; /* signed 16 bit value */
unsigned short uword; /* unsigned 16 bit value */
struct
{
unsigned char lo; /* low value for ranges */
unsigned char hi; /* high value for ranges */
}
range;
}
SFGenAmount;
typedef struct _SFGen
{ /* Generator structure */
unsigned short id; /* generator ID */
SFGenAmount amount; /* generator value */
}
SFGen;
typedef struct _SFZone
{ /* Sample/instrument zone structure */
fluid_list_t *instsamp; /* instrument/sample pointer for zone */
fluid_list_t *gen; /* list of generators */
fluid_list_t *mod; /* list of modulators */
}
SFZone;
typedef struct _SFSample
{ /* Sample structure */
char name[21]; /* Name of sample */
unsigned char samfile; /* Loaded sfont/sample buffer = 0/1 */
unsigned int start; /* Offset in sample area to start of sample */
unsigned int end; /* Offset from start to end of sample,
this is the last point of the
sample, the SF spec has this as the
1st point after, corrected on
load/save */
unsigned int loopstart; /* Offset from start to start of loop */
unsigned int loopend; /* Offset from start to end of loop,
marks the first point after loop,
whose sample value is ideally
equivalent to loopstart */
unsigned int samplerate; /* Sample rate recorded at */
unsigned char origpitch; /* root midi key number */
signed char pitchadj; /* pitch correction in cents */
unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */
}
SFSample;
typedef struct _SFInst
{ /* Instrument structure */
char name[21]; /* Name of instrument */
fluid_list_t *zone; /* list of instrument zones */
}
SFInst;
typedef struct _SFPreset
{ /* Preset structure */
char name[21]; /* preset name */
unsigned short prenum; /* preset number */
unsigned short bank; /* bank number */
unsigned int libr; /* Not used (preserved) */
unsigned int genre; /* Not used (preserved) */
unsigned int morph; /* Not used (preserved) */
fluid_list_t *zone; /* list of preset zones */
}
SFPreset;
/* NOTE: sffd is also used to determine if sound font is new (NULL) */
typedef struct _SFData
{ /* Sound font data structure */
SFVersion version; /* sound font version */
SFVersion romver; /* ROM version */
unsigned int samplepos; /* position within sffd of the sample chunk */
unsigned int samplesize; /* length within sffd of the sample chunk */
char *fname; /* file name */
FILE *sffd; /* loaded sfont file descriptor */
fluid_list_t *info; /* linked list of info strings (1st byte is ID) */
fluid_list_t *preset; /* linked list of preset info */
fluid_list_t *inst; /* linked list of instrument info */
fluid_list_t *sample; /* linked list of sample info */
}
SFData;
/* sf file chunk IDs */
enum
{ UNKN_ID, RIFF_ID, LIST_ID, SFBK_ID,
INFO_ID, SDTA_ID, PDTA_ID, /* info/sample/preset */
IFIL_ID, ISNG_ID, INAM_ID, IROM_ID, /* info ids (1st byte of info strings) */
IVER_ID, ICRD_ID, IENG_ID, IPRD_ID, /* more info ids */
ICOP_ID, ICMT_ID, ISFT_ID, /* and yet more info ids */
SNAM_ID, SMPL_ID, /* sample ids */
PHDR_ID, PBAG_ID, PMOD_ID, PGEN_ID, /* preset ids */
IHDR_ID, IBAG_ID, IMOD_ID, IGEN_ID, /* instrument ids */
SHDR_ID /* sample info */
};
/* generator types */
typedef enum
{ Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs,
Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_ModLFO2Pitch,
Gen_VibLFO2Pitch, Gen_ModEnv2Pitch, Gen_FilterFc, Gen_FilterQ,
Gen_ModLFO2FilterFc, Gen_ModEnv2FilterFc, Gen_EndAddrCoarseOfs,
Gen_ModLFO2Vol, Gen_Unused1, Gen_ChorusSend, Gen_ReverbSend, Gen_Pan,
Gen_Unused2, Gen_Unused3, Gen_Unused4,
Gen_ModLFODelay, Gen_ModLFOFreq, Gen_VibLFODelay, Gen_VibLFOFreq,
Gen_ModEnvDelay, Gen_ModEnvAttack, Gen_ModEnvHold, Gen_ModEnvDecay,
Gen_ModEnvSustain, Gen_ModEnvRelease, Gen_Key2ModEnvHold,
Gen_Key2ModEnvDecay, Gen_VolEnvDelay, Gen_VolEnvAttack,
Gen_VolEnvHold, Gen_VolEnvDecay, Gen_VolEnvSustain, Gen_VolEnvRelease,
Gen_Key2VolEnvHold, Gen_Key2VolEnvDecay, Gen_Instrument,
Gen_Reserved1, Gen_KeyRange, Gen_VelRange,
Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity,
Gen_Attenuation, Gen_Reserved2, Gen_EndLoopAddrCoarseOfs,
Gen_CoarseTune, Gen_FineTune, Gen_SampleId, Gen_SampleModes,
Gen_Reserved3, Gen_ScaleTune, Gen_ExclusiveClass, Gen_OverrideRootKey,
Gen_Dummy
}
Gen_Type;
#define Gen_MaxValid Gen_Dummy - 1 /* maximum valid generator */
#define Gen_Count Gen_Dummy /* count of generators */
#define GenArrSize sizeof(SFGenAmount)*Gen_Count /* gen array size */
/* generator unit type */
typedef enum
{
None, /* No unit type */
Unit_Smpls, /* in samples */
Unit_32kSmpls, /* in 32k samples */
Unit_Cent, /* in cents (1/100th of a semitone) */
Unit_HzCent, /* in Hz Cents */
Unit_TCent, /* in Time Cents */
Unit_cB, /* in centibels (1/100th of a decibel) */
Unit_Percent, /* in percentage */
Unit_Semitone, /* in semitones */
Unit_Range /* a range of values */
}
Gen_Unit;
/* global data */
extern unsigned short badgen[]; /* list of bad generators */
extern unsigned short badpgen[]; /* list of bad preset generators */
/* functions */
void sfont_init_chunks (void);
void sfont_close (SFData * sf);
void sfont_free_zone (SFZone * zone);
int sfont_preset_compare_func (void* a, void* b);
void sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone);
fluid_list_t *gen_inlist (int gen, fluid_list_t * genlist);
int gen_valid (int gen);
int gen_validp (int gen);
/*-----------------------------------sffile.h----------------------------*/
/*
File structures and routines (used to be in sffile.h)
*/
#define CHNKIDSTR(id) &idlist[(id - 1) * 4]
/* sfont file chunk sizes */
#define SFPHDRSIZE 38
#define SFBAGSIZE 4
#define SFMODSIZE 10
#define SFGENSIZE 4
#define SFIHDRSIZE 22
#define SFSHDRSIZE 46
/* sfont file data structures */
typedef struct _SFChunk
{ /* RIFF file chunk structure */
unsigned int id; /* chunk id */
unsigned int size; /* size of the following chunk */
}
SFChunk;
typedef struct _SFPhdr
{
unsigned char name[20]; /* preset name */
unsigned short preset; /* preset number */
unsigned short bank; /* bank number */
unsigned short pbagndx; /* index into preset bag */
unsigned int library; /* just for preserving them */
unsigned int genre; /* Not used */
unsigned int morphology; /* Not used */
}
SFPhdr;
typedef struct _SFBag
{
unsigned short genndx; /* index into generator list */
unsigned short modndx; /* index into modulator list */
}
SFBag;
typedef struct _SFIhdr
{
char name[20]; /* Name of instrument */
unsigned short ibagndx; /* Instrument bag index */
}
SFIhdr;
typedef struct _SFShdr
{ /* Sample header loading struct */
char name[20]; /* Sample name */
unsigned int start; /* Offset to start of sample */
unsigned int end; /* Offset to end of sample */
unsigned int loopstart; /* Offset to start of loop */
unsigned int loopend; /* Offset to end of loop */
unsigned int samplerate; /* Sample rate recorded at */
unsigned char origpitch; /* root midi key number */
signed char pitchadj; /* pitch correction in cents */
unsigned short samplelink; /* Not used */
unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */
}
SFShdr;
/* data */
extern char idlist[];
/* functions */
SFData *sfload_file (const char * fname);
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* Provide definitions for some commonly used macros.
* Some of them are only provided if they haven't already
* been defined. It is assumed that if they are already
* defined then the current definition is correct.
*/
#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#define GPOINTER_TO_INT(p) ((int) (p))
#define GINT_TO_POINTER(i) ((void *) (uintptr_t)(i))
char* g_strdup (const char *str);
/* Provide simple macro statement wrappers (adapted from Perl):
* G_STMT_START { statements; } G_STMT_END;
* can be used as a single statement, as in
* if (x) G_STMT_START { ... } G_STMT_END; else ...
*
* For gcc we will wrap the statements within `({' and `})' braces.
* For SunOS they will be wrapped within `if (1)' and `else (void) 0',
* and otherwise within `do' and `while (0)'.
*/
#if !(defined (G_STMT_START) && defined (G_STMT_END))
# if defined (__GNUC__) && !defined (__STRICT_ANSI__) && !defined (__cplusplus)
# define G_STMT_START (void)(
# define G_STMT_END )
# else
# if (defined (sun) || defined (__sun__))
# define G_STMT_START if (1)
# define G_STMT_END else (void)0
# else
# define G_STMT_START do
# define G_STMT_END while (0)
# endif
# endif
#endif
/* Basic bit swapping functions
*/
#define GUINT16_SWAP_LE_BE_CONSTANT(val) ((unsigned short) ( \
(((unsigned short) (val) & (unsigned short) 0x00ffU) << 8) | \
(((unsigned short) (val) & (unsigned short) 0xff00U) >> 8)))
#define GUINT32_SWAP_LE_BE_CONSTANT(val) ((unsigned int) ( \
(((unsigned int) (val) & (unsigned int) 0x000000ffU) << 24) | \
(((unsigned int) (val) & (unsigned int) 0x0000ff00U) << 8) | \
(((unsigned int) (val) & (unsigned int) 0x00ff0000U) >> 8) | \
(((unsigned int) (val) & (unsigned int) 0xff000000U) >> 24)))
#define GUINT16_SWAP_LE_BE(val) (GUINT16_SWAP_LE_BE_CONSTANT (val))
#define GUINT32_SWAP_LE_BE(val) (GUINT32_SWAP_LE_BE_CONSTANT (val))
#define GINT16_TO_LE(val) ((signed short) (val))
#define GUINT16_TO_LE(val) ((unsigned short) (val))
#define GINT16_TO_BE(val) ((signed short) GUINT16_SWAP_LE_BE (val))
#define GUINT16_TO_BE(val) (GUINT16_SWAP_LE_BE (val))
#define GINT32_TO_LE(val) ((signed int) (val))
#define GUINT32_TO_LE(val) ((unsigned int) (val))
#define GINT32_TO_BE(val) ((signed int) GUINT32_SWAP_LE_BE (val))
#define GUINT32_TO_BE(val) (GUINT32_SWAP_LE_BE (val))
/* The G*_TO_?E() macros are defined in glibconfig.h.
* The transformation is symmetric, so the FROM just maps to the TO.
*/
#define GINT16_FROM_LE(val) (GINT16_TO_LE (val))
#define GUINT16_FROM_LE(val) (GUINT16_TO_LE (val))
#define GINT16_FROM_BE(val) (GINT16_TO_BE (val))
#define GUINT16_FROM_BE(val) (GUINT16_TO_BE (val))
#define GINT32_FROM_LE(val) (GINT32_TO_LE (val))
#define GUINT32_FROM_LE(val) (GUINT32_TO_LE (val))
#define GINT32_FROM_BE(val) (GINT32_TO_BE (val))
#define GUINT32_FROM_BE(val) (GUINT32_TO_BE (val))
/*-----------------------------------util.h----------------------------*/
/*
Utility functions (formerly in util.h)
*/
#define FAIL 0
#define OK 1
enum
{ ErrWarn, ErrFatal, ErrStatus, ErrCorr, ErrEof, ErrMem, Errno,
ErrRead, ErrWrite
};
#define ErrMax ErrWrite
#define ErrnoStart Errno
#define ErrnoEnd ErrWrite
int gerr (int ev, char * fmt, ...);
int safe_fread (void *buf, int count, FILE * fd);
int safe_fwrite (void *buf, int count, FILE * fd);
int safe_fseek (FILE * fd, long ofs, int whence);
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/********************************************************************************/
/***************************************************************
*
* FORWARD DECLARATIONS
*/
typedef struct _fluid_defsfont_t fluid_defsfont_t;
typedef struct _fluid_defpreset_t fluid_defpreset_t;
typedef struct _fluid_preset_zone_t fluid_preset_zone_t;
typedef struct _fluid_inst_t fluid_inst_t;
typedef struct _fluid_inst_zone_t fluid_inst_zone_t;
/*
Public interface
*/
fluid_sfloader_t* new_fluid_defsfloader(void);
int delete_fluid_defsfloader(fluid_sfloader_t* loader);
fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename);
int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont);
char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont);
fluid_preset_t* fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum);
void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont);
int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset);
int fluid_defpreset_preset_delete(fluid_preset_t* preset);
char* fluid_defpreset_preset_get_name(fluid_preset_t* preset);
int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset);
int fluid_defpreset_preset_get_num(fluid_preset_t* preset);
int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
/*
* fluid_defsfont_t
*/
struct _fluid_defsfont_t
{
char* filename; /* the filename of this soundfont */
unsigned int samplepos; /* the position in the file at which the sample data starts */
unsigned int samplesize; /* the size of the sample data */
short* sampledata; /* the sample data, loaded in ram */
fluid_list_t* sample; /* the samples in this soundfont */
fluid_defpreset_t* preset; /* the presets of this soundfont */
fluid_preset_t iter_preset; /* preset interface used in the iteration */
fluid_defpreset_t* iter_cur; /* the current preset in the iteration */
};
fluid_defsfont_t* new_fluid_defsfont(void);
int delete_fluid_defsfont(fluid_defsfont_t* sfont);
int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file);
char* fluid_defsfont_get_name(fluid_defsfont_t* sfont);
fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int prenum);
void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont);
int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset);
int fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont);
int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample);
int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset);
fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s);
/*
* fluid_preset_t
*/
struct _fluid_defpreset_t
{
fluid_defpreset_t* next;
fluid_defsfont_t* sfont; /* the soundfont this preset belongs to */
char name[21]; /* the name of the preset */
unsigned int bank; /* the bank number */
unsigned int num; /* the preset number */
fluid_preset_zone_t* global_zone; /* the global zone of the preset */
fluid_preset_zone_t* zone; /* the chained list of preset zones */
};
fluid_defpreset_t* new_fluid_defpreset(fluid_defsfont_t* sfont);
int delete_fluid_defpreset(fluid_defpreset_t* preset);
fluid_defpreset_t* fluid_defpreset_next(fluid_defpreset_t* preset);
int fluid_defpreset_import_sfont(fluid_defpreset_t* preset, SFPreset* sfpreset, fluid_defsfont_t* sfont);
int fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone);
int fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone);
fluid_preset_zone_t* fluid_defpreset_get_zone(fluid_defpreset_t* preset);
fluid_preset_zone_t* fluid_defpreset_get_global_zone(fluid_defpreset_t* preset);
int fluid_defpreset_get_banknum(fluid_defpreset_t* preset);
int fluid_defpreset_get_num(fluid_defpreset_t* preset);
char* fluid_defpreset_get_name(fluid_defpreset_t* preset);
int fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
/*
* fluid_preset_zone
*/
struct _fluid_preset_zone_t
{
fluid_preset_zone_t* next;
char* name;
fluid_inst_t* inst;
int keylo;
int keyhi;
int vello;
int velhi;
fluid_gen_t gen[GEN_LAST];
fluid_mod_t * mod; /* List of modulators */
};
fluid_preset_zone_t* new_fluid_preset_zone(char* name);
int delete_fluid_preset_zone(fluid_preset_zone_t* zone);
fluid_preset_zone_t* fluid_preset_zone_next(fluid_preset_zone_t* preset);
int fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone* sfzone, fluid_defsfont_t* sfont);
int fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel);
fluid_inst_t* fluid_preset_zone_get_inst(fluid_preset_zone_t* zone);
/*
* fluid_inst_t
*/
struct _fluid_inst_t
{
char name[21];
fluid_inst_zone_t* global_zone;
fluid_inst_zone_t* zone;
};
fluid_inst_t* new_fluid_inst(void);
int delete_fluid_inst(fluid_inst_t* inst);
int fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont);
int fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone);
int fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone);
fluid_inst_zone_t* fluid_inst_get_zone(fluid_inst_t* inst);
fluid_inst_zone_t* fluid_inst_get_global_zone(fluid_inst_t* inst);
/*
* fluid_inst_zone_t
*/
struct _fluid_inst_zone_t
{
fluid_inst_zone_t* next;
char* name;
fluid_sample_t* sample;
int keylo;
int keyhi;
int vello;
int velhi;
fluid_gen_t gen[GEN_LAST];
fluid_mod_t * mod; /* List of modulators */
};
fluid_inst_zone_t* new_fluid_inst_zone(char* name);
int delete_fluid_inst_zone(fluid_inst_zone_t* zone);
fluid_inst_zone_t* fluid_inst_zone_next(fluid_inst_zone_t* zone);
int fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont);
int fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel);
fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone);
fluid_sample_t* new_fluid_sample(void);
int delete_fluid_sample(fluid_sample_t* sample);
int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont);
int fluid_sample_in_rom(fluid_sample_t* sample);
#endif /* _FLUID_SFONT_H */

View file

@ -0,0 +1,685 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluidsynth_priv.h"
#include "fluid_phase.h"
/* Purpose:
*
* Interpolates audio data (obtains values between the samples of the original
* waveform data).
*
* Variables loaded from the voice structure (assigned in fluid_voice_write()):
* - dsp_data: Pointer to the original waveform data
* - dsp_phase: The position in the original waveform data.
* This has an integer and a fractional part (between samples).
* - dsp_phase_incr: For each output sample, the position in the original
* waveform advances by dsp_phase_incr. This also has an integer
* part and a fractional part.
* If a sample is played at root pitch (no pitch change),
* dsp_phase_incr is integer=1 and fractional=0.
* - dsp_amp: The current amplitude envelope value.
* - dsp_amp_incr: The changing rate of the amplitude envelope.
*
* A couple of variables are used internally, their results are discarded:
* - dsp_i: Index through the output buffer
* - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length)
*/
#include "fluidsynth_priv.h"
#include "fluid_synth.h"
#include "fluid_voice.h"
/* Interpolation (find a value between two samples of the original waveform) */
/* Linear interpolation table (2 coefficients centered on 1st) */
static fluid_real_t interp_coeff_linear[FLUID_INTERP_MAX][2];
/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */
static fluid_real_t interp_coeff[FLUID_INTERP_MAX][4];
/* 7th order interpolation (7 coefficients centered on 3rd) */
static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7];
#define SINC_INTERP_ORDER 7 /* 7th order constant */
/* Initializes interpolation tables */
void fluid_dsp_float_config (void)
{
int i, i2;
double x, v;
double i_shifted;
/* Initialize the coefficients for the interpolation. The math comes
* from a mail, posted by Olli Niemitalo to the music-dsp mailing
* list (I found it in the music-dsp archives
* http://www.smartelectronix.com/musicdsp/). */
for (i = 0; i < FLUID_INTERP_MAX; i++)
{
x = (double) i / (double) FLUID_INTERP_MAX;
interp_coeff[i][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x)));
interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5));
interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x)));
interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0));
interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x);
interp_coeff_linear[i][1] = (fluid_real_t)x;
}
/* i: Offset in terms of whole samples */
for (i = 0; i < SINC_INTERP_ORDER; i++)
{ /* i2: Offset in terms of fractional samples ('subsamples') */
for (i2 = 0; i2 < FLUID_INTERP_MAX; i2++)
{
/* center on middle of table */
i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0)
+ (double)i2 / (double)FLUID_INTERP_MAX;
/* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */
if (fabs (i_shifted) > 0.000001)
{
v = (fluid_real_t)sin (i_shifted * M_PI) / (M_PI * i_shifted);
/* Hamming window */
v *= (fluid_real_t)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (fluid_real_t)SINC_INTERP_ORDER));
}
else v = 1.0;
sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v;
}
}
#if 0
for (i = 0; i < FLUID_INTERP_MAX; i++)
{
printf ("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n",
i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i],
sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]);
}
#endif
}
/* No interpolation. Just take the sample, which is closest to
* the playback pointer. Questionable quality, but very
* efficient. */
int
fluid_dsp_float_interpolate_none (fluid_voice_t *voice)
{
fluid_phase_t dsp_phase = voice->phase;
fluid_phase_t dsp_phase_incr;
short int *dsp_data = voice->sample->data;
fluid_real_t *dsp_buf = voice->dsp_buf;
fluid_real_t dsp_amp = voice->amp;
fluid_real_t dsp_amp_incr = voice->amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int end_index;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* voice is currently looping? */
looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
|| (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
end_index = looping ? voice->loopend - 1 : voice->end;
while (1)
{
dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */
/* interpolate sequence of sample points */
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index];
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */
dsp_amp += dsp_amp_incr;
}
/* break out if not looping (buffer may not be full) */
if (!looping) break;
/* go back to loop start */
if (dsp_phase_index > end_index)
{
fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
voice->has_looped = 1;
}
/* break out if filled buffer */
if (dsp_i >= FLUID_BUFSIZE) break;
}
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return (dsp_i);
}
/* Straight line interpolation.
* Returns number of samples processed (usually FLUID_BUFSIZE but could be
* smaller if end of sample occurs).
*/
int
fluid_dsp_float_interpolate_linear (fluid_voice_t *voice)
{
fluid_phase_t dsp_phase = voice->phase;
fluid_phase_t dsp_phase_incr;
short int *dsp_data = voice->sample->data;
fluid_real_t *dsp_buf = voice->dsp_buf;
fluid_real_t dsp_amp = voice->amp;
fluid_real_t dsp_amp_incr = voice->amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int end_index;
short int point;
fluid_real_t *coeffs;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* voice is currently looping? */
looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
|| (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
/* last index before 2nd interpolation point must be specially handled */
end_index = (looping ? voice->loopend - 1 : voice->end) - 1;
/* 2nd interpolation point to use at end of loop or sample */
if (looping) point = dsp_data[voice->loopstart]; /* loop start */
else point = dsp_data[voice->end]; /* duplicate end for samples no longer looping */
while (1)
{
dsp_phase_index = fluid_phase_index (dsp_phase);
/* interpolate the sequence of sample points */
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index]
+ coeffs[1] * dsp_data[dsp_phase_index+1]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
/* break out if buffer filled */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index++; /* we're now interpolating the last point */
/* interpolate within last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index]
+ coeffs[1] * point);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr; /* increment amplitude */
}
if (!looping) break; /* break out if not looping (end of sample) */
/* go back to loop start (if past */
if (dsp_phase_index > end_index)
{
fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
voice->has_looped = 1;
}
/* break out if filled buffer */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index--; /* set end back to second to last sample point */
}
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return (dsp_i);
}
/* 4th order (cubic) interpolation.
* Returns number of samples processed (usually FLUID_BUFSIZE but could be
* smaller if end of sample occurs).
*/
int
fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice)
{
fluid_phase_t dsp_phase = voice->phase;
fluid_phase_t dsp_phase_incr;
short int *dsp_data = voice->sample->data;
fluid_real_t *dsp_buf = voice->dsp_buf;
fluid_real_t dsp_amp = voice->amp;
fluid_real_t dsp_amp_incr = voice->amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int start_index, end_index;
short int start_point, end_point1, end_point2;
fluid_real_t *coeffs;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* voice is currently looping? */
looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
|| (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
/* last index before 4th interpolation point must be specially handled */
end_index = (looping ? voice->loopend - 1 : voice->end) - 2;
if (voice->has_looped) /* set start_index and start point if looped or not */
{
start_index = voice->loopstart;
start_point = dsp_data[voice->loopend - 1]; /* last point in loop (wrap around) */
}
else
{
start_index = voice->start;
start_point = dsp_data[voice->start]; /* just duplicate the point */
}
/* get points off the end (loop start if looping, duplicate point if end) */
if (looping)
{
end_point1 = dsp_data[voice->loopstart];
end_point2 = dsp_data[voice->loopstart + 1];
}
else
{
end_point1 = dsp_data[voice->end];
end_point2 = end_point1;
}
while (1)
{
dsp_phase_index = fluid_phase_index (dsp_phase);
/* interpolate first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * start_point
+ coeffs[1] * dsp_data[dsp_phase_index]
+ coeffs[2] * dsp_data[dsp_phase_index+1]
+ coeffs[3] * dsp_data[dsp_phase_index+2]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
/* interpolate the sequence of sample points */
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ coeffs[1] * dsp_data[dsp_phase_index]
+ coeffs[2] * dsp_data[dsp_phase_index+1]
+ coeffs[3] * dsp_data[dsp_phase_index+2]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
/* break out if buffer filled */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index++; /* we're now interpolating the 2nd to last point */
/* interpolate within 2nd to last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ coeffs[1] * dsp_data[dsp_phase_index]
+ coeffs[2] * dsp_data[dsp_phase_index+1]
+ coeffs[3] * end_point1);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
end_index++; /* we're now interpolating the last point */
/* interpolate within the last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ coeffs[1] * dsp_data[dsp_phase_index]
+ coeffs[2] * end_point1
+ coeffs[3] * end_point2);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
if (!looping) break; /* break out if not looping (end of sample) */
/* go back to loop start */
if (dsp_phase_index > end_index)
{
fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
if (!voice->has_looped)
{
voice->has_looped = 1;
start_index = voice->loopstart;
start_point = dsp_data[voice->loopend - 1];
}
}
/* break out if filled buffer */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index -= 2; /* set end back to third to last sample point */
}
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return (dsp_i);
}
/* 7th order interpolation.
* Returns number of samples processed (usually FLUID_BUFSIZE but could be
* smaller if end of sample occurs).
*/
int
fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice)
{
fluid_phase_t dsp_phase = voice->phase;
fluid_phase_t dsp_phase_incr;
short int *dsp_data = voice->sample->data;
fluid_real_t *dsp_buf = voice->dsp_buf;
fluid_real_t dsp_amp = voice->amp;
fluid_real_t dsp_amp_incr = voice->amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int start_index, end_index;
short int start_points[3];
short int end_points[3];
fluid_real_t *coeffs;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* add 1/2 sample to dsp_phase since 7th order interpolation is centered on
* the 4th sample point */
fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000);
/* voice is currently looping? */
looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
|| (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
/* last index before 7th interpolation point must be specially handled */
end_index = (looping ? voice->loopend - 1 : voice->end) - 3;
if (voice->has_looped) /* set start_index and start point if looped or not */
{
start_index = voice->loopstart;
start_points[0] = dsp_data[voice->loopend - 1];
start_points[1] = dsp_data[voice->loopend - 2];
start_points[2] = dsp_data[voice->loopend - 3];
}
else
{
start_index = voice->start;
start_points[0] = dsp_data[voice->start]; /* just duplicate the start point */
start_points[1] = start_points[0];
start_points[2] = start_points[0];
}
/* get the 3 points off the end (loop start if looping, duplicate point if end) */
if (looping)
{
end_points[0] = dsp_data[voice->loopstart];
end_points[1] = dsp_data[voice->loopstart + 1];
end_points[2] = dsp_data[voice->loopstart + 2];
}
else
{
end_points[0] = dsp_data[voice->end];
end_points[1] = end_points[0];
end_points[2] = end_points[0];
}
while (1)
{
dsp_phase_index = fluid_phase_index (dsp_phase);
/* interpolate first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)start_points[2]
+ coeffs[1] * (fluid_real_t)start_points[1]
+ coeffs[2] * (fluid_real_t)start_points[0]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
start_index++;
/* interpolate 2nd to first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)start_points[1]
+ coeffs[1] * (fluid_real_t)start_points[0]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
start_index++;
/* interpolate 3rd to first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)start_points[0]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
start_index -= 2; /* set back to original start index */
/* interpolate the sequence of sample points */
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
/* break out if buffer filled */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index++; /* we're now interpolating the 3rd to last point */
/* interpolate within 3rd to last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)end_points[0]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
end_index++; /* we're now interpolating the 2nd to last point */
/* interpolate within 2nd to last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)end_points[0]
+ coeffs[6] * (fluid_real_t)end_points[1]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
end_index++; /* we're now interpolating the last point */
/* interpolate within last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)end_points[0]
+ coeffs[5] * (fluid_real_t)end_points[1]
+ coeffs[6] * (fluid_real_t)end_points[2]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
if (!looping) break; /* break out if not looping (end of sample) */
/* go back to loop start */
if (dsp_phase_index > end_index)
{
fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
if (!voice->has_looped)
{
voice->has_looped = 1;
start_index = voice->loopstart;
start_points[0] = dsp_data[voice->loopend - 1];
start_points[1] = dsp_data[voice->loopend - 2];
start_points[2] = dsp_data[voice->loopend - 3];
}
}
/* break out if filled buffer */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index -= 3; /* set end back to 4th to last sample point */
}
/* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on
* the 4th sample point (correct back to real value) */
fluid_phase_decr (dsp_phase, (fluid_phase_t)0x80000000);
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return (dsp_i);
}

View file

@ -0,0 +1,120 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
/* Purpose:
* Low-level voice processing:
*
* - interpolates (obtains values between the samples of the original waveform data)
* - filters (applies a lowpass filter with variable cutoff frequency and quality factor)
* - mixes the processed sample to left and right output using the pan setting
* - sends the processed sample to chorus and reverb
*
*
* This file does -not- generate an object file.
* Instead, it is #included in several places in fluid_voice.c.
* The motivation for this is
* - Calling it as a subroutine may be time consuming, especially with optimization off
* - The previous implementation as a macro was clumsy to handle
*
*
* Fluid_voice.c sets a couple of variables before #including this:
* - dsp_data: Pointer to the original waveform data
* - dsp_left_buf: The generated signal goes here, left channel
* - dsp_right_buf: right channel
* - dsp_reverb_buf: Send to reverb unit
* - dsp_chorus_buf: Send to chorus unit
* - dsp_start: Start processing at this output buffer index
* - dsp_end: End processing just before this output buffer index
* - dsp_a1: Coefficient for the filter
* - dsp_a2: same
* - dsp_b0: same
* - dsp_b1: same
* - dsp_b2: same
* - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use
* the filter at all. If it is left at its default setting
* of roughly 20 kHz, there is no need to apply filterling.)
* - dsp_interp_method: Which interpolation method to use.
* - voice holds the voice structure
*
* Some variables are set and modified:
* - dsp_phase: The position in the original waveform data.
* This has an integer and a fractional part (between samples).
* - dsp_phase_incr: For each output sample, the position in the original
* waveform advances by dsp_phase_incr. This also has an integer
* part and a fractional part.
* If a sample is played at root pitch (no pitch change),
* dsp_phase_incr is integer=1 and fractional=0.
* - dsp_amp: The current amplitude envelope value.
* - dsp_amp_incr: The changing rate of the amplitude envelope.
*
* A couple of variables are used internally, their results are discarded:
* - dsp_i: Index through the output buffer
* - dsp_phase_fractional: The fractional part of dsp_phase
* - dsp_coeff: A table of four coefficients, depending on the fractional phase.
* Used to interpolate between samples.
* - dsp_process_buffer: Holds the processed signal between stages
* - dsp_centernode: delay line for the IIR filter
* - dsp_hist1: same
* - dsp_hist2: same
*
*/
/* Nonoptimized DSP loop */
#warning "This code is meant for experiments only.";
/* wave table interpolation */
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
dsp_coeff = &interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)];
dsp_phase_index = fluid_phase_index(dsp_phase);
dsp_sample = (dsp_amp *
(dsp_coeff->a0 * dsp_data[dsp_phase_index]
+ dsp_coeff->a1 * dsp_data[dsp_phase_index+1]
+ dsp_coeff->a2 * dsp_data[dsp_phase_index+2]
+ dsp_coeff->a3 * dsp_data[dsp_phase_index+3]));
/* increment phase and amplitude */
fluid_phase_incr(dsp_phase, dsp_phase_incr);
dsp_amp += dsp_amp_incr;
/* filter */
/* The filter is implemented in Direct-II form. */
dsp_centernode = dsp_sample - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
dsp_sample = dsp_b0 * dsp_centernode + dsp_b1 * dsp_hist1 + dsp_b2 * dsp_hist2;
dsp_hist2 = dsp_hist1;
dsp_hist1 = dsp_centernode;
/* pan */
dsp_left_buf[dsp_i] += voice->amp_left * dsp_sample;
dsp_right_buf[dsp_i] += voice->amp_right * dsp_sample;
/* reverb */
if (dsp_reverb_buf){
dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_sample;
}
/* chorus */
if (dsp_chorus_buf){
dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_sample;
}
}

View file

@ -0,0 +1,149 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_gen.h"
#include "fluid_chan.h"
/* See SFSpec21 $8.1.3 */
fluid_gen_info_t fluid_gen_info[] = {
/* number/name init scale min max def */
{ GEN_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f },
{ GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f },
{ GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f },
{ GEN_ENDLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f },
{ GEN_STARTADDRCOARSEOFS, 0, 1, 0.0f, 1e10f, 0.0f },
{ GEN_MODLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_VIBLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_MODENVTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_FILTERFC, 1, 2, 1500.0f, 13500.0f, 13500.0f },
{ GEN_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f },
{ GEN_MODLFOTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_MODENVTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f },
{ GEN_ENDADDRCOARSEOFS, 0, 1, -1e10f, 0.0f, 0.0f },
{ GEN_MODLFOTOVOL, 1, 1, -960.0f, 960.0f, 0.0f },
{ GEN_UNUSED1, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_CHORUSSEND, 1, 1, 0.0f, 1000.0f, 0.0f },
{ GEN_REVERBSEND, 1, 1, 0.0f, 1000.0f, 0.0f },
{ GEN_PAN, 1, 1, -500.0f, 500.0f, 0.0f },
{ GEN_UNUSED2, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_UNUSED3, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_UNUSED4, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_MODLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_MODLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f },
{ GEN_VIBLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_VIBLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f },
{ GEN_MODENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_MODENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_MODENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_MODENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_MODENVSUSTAIN, 0, 1, 0.0f, 1000.0f, 0.0f },
{ GEN_MODENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_KEYTOMODENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f },
{ GEN_KEYTOMODENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f },
{ GEN_VOLENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_VOLENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_VOLENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f },
{ GEN_VOLENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_VOLENVSUSTAIN, 0, 1, 0.0f, 1440.0f, 0.0f },
{ GEN_VOLENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f },
{ GEN_KEYTOVOLENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f },
{ GEN_KEYTOVOLENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f },
{ GEN_INSTRUMENT, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_RESERVED1, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_KEYRANGE, 0, 0, 0.0f, 127.0f, 0.0f },
{ GEN_VELRANGE, 0, 0, 0.0f, 127.0f, 0.0f },
{ GEN_STARTLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f },
{ GEN_KEYNUM, 1, 0, 0.0f, 127.0f, -1.0f },
{ GEN_VELOCITY, 1, 1, 0.0f, 127.0f, -1.0f },
{ GEN_ATTENUATION, 1, 1, 0.0f, 1440.0f, 0.0f },
{ GEN_RESERVED2, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_ENDLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f },
{ GEN_COARSETUNE, 0, 1, -120.0f, 120.0f, 0.0f },
{ GEN_FINETUNE, 0, 1, -99.0f, 99.0f, 0.0f },
{ GEN_SAMPLEID, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_SAMPLEMODE, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_RESERVED3, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_SCALETUNE, 0, 1, 0.0f, 1200.0f, 100.0f },
{ GEN_EXCLUSIVECLASS, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_OVERRIDEROOTKEY, 1, 0, 0.0f, 127.0f, -1.0f },
{ GEN_PITCH, 1, 0, 0.0f, 127.0f, 0.0f }
};
/**
* Set an array of generators to their default values.
* @param gen Array of generators (should be #GEN_LAST in size).
* @return Always returns 0
*/
int
fluid_gen_set_default_values(fluid_gen_t* gen)
{
int i;
for (i = 0; i < GEN_LAST; i++) {
gen[i].flags = GEN_UNUSED;
gen[i].mod = 0.0;
gen[i].nrpn = 0.0;
gen[i].val = fluid_gen_info[i].def;
}
return FLUID_OK;
}
/* fluid_gen_init
*
* Set an array of generators to their initial value
*/
int
fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel)
{
int i;
fluid_gen_set_default_values(gen);
for (i = 0; i < GEN_LAST; i++) {
gen[i].nrpn = fluid_channel_get_gen(channel, i);
/* This is an extension to the SoundFont standard. More
* documentation is available at the fluid_synth_set_gen2()
* function. */
if (fluid_channel_get_gen_abs(channel, i)) {
gen[i].flags = GEN_ABS_NRPN;
}
}
return FLUID_OK;
}
fluid_real_t fluid_gen_scale(int gen, float value)
{
return (fluid_gen_info[gen].min
+ value * (fluid_gen_info[gen].max - fluid_gen_info[gen].min));
}
fluid_real_t fluid_gen_scale_nrpn(int gen, int data)
{
fluid_real_t value = (float) data - 8192.0f;
fluid_clip(value, -8192, 8192);
return value * (float) fluid_gen_info[gen].nrpn_scale;
}

View file

@ -0,0 +1,44 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_GEN_H
#define _FLUID_GEN_H
#include "fluidsynth_priv.h"
typedef struct _fluid_gen_info_t {
char num; /* Generator number */
char init; /* Does the generator need to be initialized (cfr. fluid_voice_init()) */
char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */
float min; /* The minimum value */
float max; /* The maximum value */
float def; /* The default value (cfr. fluid_gen_set_default_values()) */
} fluid_gen_info_t;
#define fluid_gen_set_mod(_gen, _val) { (_gen)->mod = (double) (_val); }
#define fluid_gen_set_nrpn(_gen, _val) { (_gen)->nrpn = (double) (_val); }
fluid_real_t fluid_gen_scale(int gen, float value);
fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn);
int fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel);
#endif /* _FLUID_GEN_H */

View file

@ -0,0 +1,388 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
/*
* MT safe
*/
#include "fluidsynth_priv.h"
#include "fluid_hash.h"
#define HASH_TABLE_MIN_SIZE 7
#define HASH_TABLE_MAX_SIZE 13845163
typedef struct _fluid_hashnode_t fluid_hashnode_t;
struct _fluid_hashnode_t {
char* key;
void* value;
int type;
fluid_hashnode_t *next;
};
static fluid_hashnode_t* new_fluid_hashnode(char* key, void* value, int type);
static void delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del);
static void delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del);
struct _fluid_hashtable_t {
unsigned int size;
unsigned int nnodes;
fluid_hashnode_t **nodes;
fluid_hash_delete_t del;
};
#define FLUID_HASHTABLE_RESIZE(hash_table) \
if ((3 * hash_table->size <= hash_table->nnodes) \
&& (hash_table->size < HASH_TABLE_MAX_SIZE)) { \
fluid_hashtable_resize(hash_table); \
}
static void fluid_hashtable_resize(fluid_hashtable_t *hash_table);
static fluid_hashnode_t** fluid_hashtable_lookup_node(fluid_hashtable_t *hash_table, char* key);
/**
* new_fluid_hashtable:
*
* Creates a new #fluid_hashtable_t.
*
* Return value: a new #fluid_hashtable_t.
**/
fluid_hashtable_t*
new_fluid_hashtable(fluid_hash_delete_t del)
{
fluid_hashtable_t *hash_table;
unsigned int i;
hash_table = FLUID_NEW(fluid_hashtable_t);
hash_table->size = HASH_TABLE_MIN_SIZE;
hash_table->nnodes = 0;
hash_table->nodes = FLUID_ARRAY(fluid_hashnode_t*, hash_table->size);
hash_table->del = del;
for (i = 0; i < hash_table->size; i++) {
hash_table->nodes[i] = NULL;
}
return hash_table;
}
/**
* delete_fluid_hashtable:
* @hash_table: a #fluid_hashtable_t.
*
* Destroys the #fluid_hashtable_t. If keys and/or values are dynamically
* allocated, you should either free them first or create the #fluid_hashtable_t
* using fluid_hashtable_new_full(). In the latter case the destroy functions
* you supplied will be called on all keys and values before destroying
* the #fluid_hashtable_t.
**/
void
delete_fluid_hashtable(fluid_hashtable_t *hash_table)
{
unsigned int i;
if (hash_table == NULL) {
return;
}
for (i = 0; i < hash_table->size; i++) {
delete_fluid_hashnodes(hash_table->nodes[i], hash_table->del);
}
FLUID_FREE(hash_table->nodes);
FLUID_FREE(hash_table);
}
static /*inline*/ fluid_hashnode_t**
fluid_hashtable_lookup_node (fluid_hashtable_t* hash_table, char* key)
{
fluid_hashnode_t **node;
node = &hash_table->nodes[fluid_str_hash(key) % hash_table->size];
while (*node && (FLUID_STRCMP((*node)->key, key) != 0)) {
node = &(*node)->next;
}
return node;
}
/**
* fluid_hashtable_lookup:
* @hash_table: a #fluid_hashtable_t.
* @key: the key to look up.
*
* Looks up a key in a #fluid_hashtable_t.
*
* Return value: the associated value, or %NULL if the key is not found.
**/
int
fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type)
{
fluid_hashnode_t *node;
node = *fluid_hashtable_lookup_node(hash_table, key);
if (node) {
if (value) {
*value = node->value;
}
if (type) {
*type = node->type;
}
return 1;
} else {
return 0;
}
}
/**
* fluid_hashtable_insert:
* @hash_table: a #fluid_hashtable_t.
* @key: a key to insert.
* @value: the value to associate with the key.
*
* Inserts a new key and value into a #fluid_hashtable_t.
*
* If the key already exists in the #fluid_hashtable_t its current value is replaced
* with the new value. If you supplied a @value_destroy_func when creating the
* #fluid_hashtable_t, the old value is freed using that function. If you supplied
* a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed
* using that function.
**/
void
fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type)
{
fluid_hashnode_t **node;
node = fluid_hashtable_lookup_node(hash_table, key);
if (*node) {
(*node)->value = value;
(*node)->type = type;
} else {
*node = new_fluid_hashnode(key, value, type);
hash_table->nnodes++;
FLUID_HASHTABLE_RESIZE(hash_table);
}
}
/**
* fluid_hashtable_replace:
* @hash_table: a #GHashTable.
* @key: a key to insert.
* @value: the value to associate with the key.
*
* Inserts a new key and value into a #GHashTable similar to
* fluid_hashtable_insert(). The difference is that if the key already exists
* in the #GHashTable, it gets replaced by the new key. If you supplied a
* @value_destroy_func when creating the #GHashTable, the old value is freed
* using that function. If you supplied a @key_destroy_func when creating the
* #GHashTable, the old key is freed using that function.
**/
void
fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type)
{
fluid_hashnode_t **node;
node = fluid_hashtable_lookup_node(hash_table, key);
if (*node) {
if (hash_table->del) {
hash_table->del((*node)->value, (*node)->type);
}
(*node)->value = value;
} else {
*node = new_fluid_hashnode(key, value, type);
hash_table->nnodes++;
FLUID_HASHTABLE_RESIZE(hash_table);
}
}
/**
* fluid_hashtable_remove:
* @hash_table: a #fluid_hashtable_t.
* @key: the key to remove.
*
* Removes a key and its associated value from a #fluid_hashtable_t.
*
* If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the
* key and value are freed using the supplied destroy functions, otherwise
* you have to make sure that any dynamically allocated values are freed
* yourself.
*
* Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t.
**/
int
fluid_hashtable_remove (fluid_hashtable_t *hash_table, char* key)
{
fluid_hashnode_t **node, *dest;
node = fluid_hashtable_lookup_node(hash_table, key);
if (*node) {
dest = *node;
(*node) = dest->next;
delete_fluid_hashnode(dest, hash_table->del);
hash_table->nnodes--;
FLUID_HASHTABLE_RESIZE (hash_table);
return 1;
}
return 0;
}
/**
* fluid_hashtable_foreach:
* @hash_table: a #fluid_hashtable_t.
* @func: the function to call for each key/value pair.
* @user_data: user data to pass to the function.
*
* Calls the given function for each of the key/value pairs in the
* #fluid_hashtable_t. The function is passed the key and value of each
* pair, and the given @user_data parameter. The hash table may not
* be modified while iterating over it (you can't add/remove
* items). To remove all items matching a predicate, use
* fluid_hashtable_remove().
**/
void
fluid_hashtable_foreach(fluid_hashtable_t *hash_table, fluid_hash_iter_t func, void* data)
{
fluid_hashnode_t *node = NULL;
unsigned int i;
for (i = 0; i < hash_table->size; i++) {
for (node = hash_table->nodes[i]; node != NULL; node = node->next) {
(*func)(node->key, node->value, node->type, data);
}
}
}
/**
* fluid_hashtable_size:
* @hash_table: a #fluid_hashtable_t.
*
* Returns the number of elements contained in the #fluid_hashtable_t.
*
* Return value: the number of key/value pairs in the #fluid_hashtable_t.
**/
unsigned int
fluid_hashtable_size(fluid_hashtable_t *hash_table)
{
return hash_table->nnodes;
}
static void
fluid_hashtable_resize(fluid_hashtable_t *hash_table)
{
fluid_hashnode_t **new_nodes;
fluid_hashnode_t *node;
fluid_hashnode_t *next;
unsigned int hash_val;
int new_size;
unsigned int i;
new_size = 3 * hash_table->size + 1;
new_size = (new_size > HASH_TABLE_MAX_SIZE)? HASH_TABLE_MAX_SIZE : new_size;
/* printf("%s: %d: resizing, new size = %d\n", __FILE__, __LINE__, new_size); */
new_nodes = FLUID_ARRAY(fluid_hashnode_t*, new_size);
FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t*));
for (i = 0; i < hash_table->size; i++) {
for (node = hash_table->nodes[i]; node; node = next) {
next = node->next;
hash_val = fluid_str_hash(node->key) % new_size;
node->next = new_nodes[hash_val];
new_nodes[hash_val] = node;
}
}
FLUID_FREE(hash_table->nodes);
hash_table->nodes = new_nodes;
hash_table->size = new_size;
}
static fluid_hashnode_t*
new_fluid_hashnode(char* key, void* value, int type)
{
fluid_hashnode_t *hash_node;
hash_node = FLUID_NEW(fluid_hashnode_t);
hash_node->key = FLUID_STRDUP(key);
hash_node->value = value;
hash_node->type = type;
hash_node->next = NULL;
return hash_node;
}
static void
delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del)
{
if (del) {
(*del)(hash_node->value, hash_node->type);
}
if (hash_node->key) {
FLUID_FREE(hash_node->key);
}
FLUID_FREE(hash_node);
}
static void
delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del)
{
while (hash_node) {
fluid_hashnode_t *next = hash_node->next;
delete_fluid_hashnode(hash_node, del);
hash_node = next;
}
}
/* 31 bit hash function */
unsigned int
fluid_str_hash(char* key)
{
char *p = key;
unsigned int h = *p;
if (h) {
for (p += 1; *p != '\0'; p++) {
h = (h << 5) - h + *p;
}
}
return h;
}

View file

@ -0,0 +1,64 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
/*
* Demolished by Peter Hanappe [December 2002]
*
* - only string as key
* - stores additional type info
* - removed use of GLib types (gpointer, gint, ...)
* - reduced the number of API functions
* - changed names to fluid_hashtable_...
*/
#ifndef _FLUID_HASH_H
#define _FLUID_HASH_H
typedef int (*fluid_hash_iter_t)(char* key, void* value, int type, void* data);
typedef void (*fluid_hash_delete_t)(void* value, int type);
fluid_hashtable_t* new_fluid_hashtable(fluid_hash_delete_t delete);
void delete_fluid_hashtable(fluid_hashtable_t *hash_table);
void fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type);
void fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type);
/* Returns non-zero if found, 0 if not found */
int fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type);
/* Returns non-zero if removed, 0 if not removed */
int fluid_hashtable_remove(fluid_hashtable_t *hash_table, char* key);
void fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hash_iter_t fun, void* data);
unsigned int fluid_hashtable_size(fluid_hashtable_t *hash_table);
unsigned int fluid_str_hash(char* v);
#endif /* _FLUID_HASH_H */

View file

@ -0,0 +1,257 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GLib Team and others 1997-1999. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "fluid_list.h"
fluid_list_t*
new_fluid_list(void)
{
fluid_list_t* list;
list = (fluid_list_t*) FLUID_MALLOC(sizeof(fluid_list_t));
list->data = NULL;
list->next = NULL;
return list;
}
void
delete_fluid_list(fluid_list_t *list)
{
fluid_list_t *next;
while (list) {
next = list->next;
FLUID_FREE(list);
list = next;
}
}
void
delete1_fluid_list(fluid_list_t *list)
{
if (list) {
FLUID_FREE(list);
}
}
fluid_list_t*
fluid_list_append(fluid_list_t *list, void* data)
{
fluid_list_t *new_list;
fluid_list_t *last;
new_list = new_fluid_list();
new_list->data = data;
if (list)
{
last = fluid_list_last(list);
/* g_assert (last != NULL); */
last->next = new_list;
return list;
}
else
return new_list;
}
fluid_list_t*
fluid_list_prepend(fluid_list_t *list, void* data)
{
fluid_list_t *new_list;
new_list = new_fluid_list();
new_list->data = data;
new_list->next = list;
return new_list;
}
fluid_list_t*
fluid_list_nth(fluid_list_t *list, int n)
{
while ((n-- > 0) && list) {
list = list->next;
}
return list;
}
fluid_list_t*
fluid_list_remove(fluid_list_t *list, void* data)
{
fluid_list_t *tmp;
fluid_list_t *prev;
prev = NULL;
tmp = list;
while (tmp) {
if (tmp->data == data) {
if (prev) {
prev->next = tmp->next;
}
if (list == tmp) {
list = list->next;
}
tmp->next = NULL;
delete_fluid_list(tmp);
break;
}
prev = tmp;
tmp = tmp->next;
}
return list;
}
fluid_list_t*
fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link)
{
fluid_list_t *tmp;
fluid_list_t *prev;
prev = NULL;
tmp = list;
while (tmp) {
if (tmp == link) {
if (prev) {
prev->next = tmp->next;
}
if (list == tmp) {
list = list->next;
}
tmp->next = NULL;
break;
}
prev = tmp;
tmp = tmp->next;
}
return list;
}
static fluid_list_t*
fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func)
{
fluid_list_t list, *l;
l = &list;
while (l1 && l2) {
if (compare_func(l1->data,l2->data) < 0) {
l = l->next = l1;
l1 = l1->next;
} else {
l = l->next = l2;
l2 = l2->next;
}
}
l->next= l1 ? l1 : l2;
return list.next;
}
fluid_list_t*
fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func)
{
fluid_list_t *l1, *l2;
if (!list) {
return NULL;
}
if (!list->next) {
return list;
}
l1 = list;
l2 = list->next;
while ((l2 = l2->next) != NULL) {
if ((l2 = l2->next) == NULL)
break;
l1=l1->next;
}
l2 = l1->next;
l1->next = NULL;
return fluid_list_sort_merge(fluid_list_sort(list, compare_func),
fluid_list_sort(l2, compare_func),
compare_func);
}
fluid_list_t*
fluid_list_last(fluid_list_t *list)
{
if (list) {
while (list->next)
list = list->next;
}
return list;
}
int
fluid_list_size(fluid_list_t *list)
{
int n = 0;
while (list) {
n++;
list = list->next;
}
return n;
}
fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data)
{
fluid_list_t *new_list;
fluid_list_t *cur;
fluid_list_t *prev = NULL;
new_list = new_fluid_list();
new_list->data = data;
cur = list;
while ((n-- > 0) && cur) {
prev = cur;
cur = cur->next;
}
new_list->next = cur;
if (prev) {
prev->next = new_list;
return list;
} else {
return new_list;
}
}

View file

@ -0,0 +1,61 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef _FLUID_LIST_H
#define _FLUID_LIST_H
#include "fluidsynth_priv.h"
/*
*
* Lists
*
* A sound font loader has to pack the data from the .SF2 file into
* list structures of this type.
*
*/
typedef struct _fluid_list_t fluid_list_t;
typedef int (*fluid_compare_func_t)(void* a, void* b);
struct _fluid_list_t
{
void* data;
fluid_list_t *next;
};
fluid_list_t* new_fluid_list(void);
void delete_fluid_list(fluid_list_t *list);
void delete1_fluid_list(fluid_list_t *list);
fluid_list_t* fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func);
fluid_list_t* fluid_list_append(fluid_list_t *list, void* data);
fluid_list_t* fluid_list_prepend(fluid_list_t *list, void* data);
fluid_list_t* fluid_list_remove(fluid_list_t *list, void* data);
fluid_list_t* fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink);
fluid_list_t* fluid_list_nth(fluid_list_t *list, int n);
fluid_list_t* fluid_list_last(fluid_list_t *list);
fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data);
int fluid_list_size(fluid_list_t *list);
#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL)
#define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL)
#endif /* _FLUID_LIST_H */

View file

@ -0,0 +1,247 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_MIDI_H
#define _FLUID_MIDI_H
#include "fluidsynth_priv.h"
#include "fluid_sys.h"
#include "fluid_list.h"
typedef struct _fluid_midi_parser_t fluid_midi_parser_t;
fluid_midi_parser_t* new_fluid_midi_parser(void);
int delete_fluid_midi_parser(fluid_midi_parser_t* parser);
fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c);
int fluid_midi_send_event(fluid_synth_t* synth, fluid_player_t* player, fluid_midi_event_t* evt);
/***************************************************************
*
* CONSTANTS & ENUM
*/
#define MAX_NUMBER_OF_TRACKS 128
enum fluid_midi_event_type {
/* channel messages */
NOTE_OFF = 0x80,
NOTE_ON = 0x90,
KEY_PRESSURE = 0xa0,
CONTROL_CHANGE = 0xb0,
PROGRAM_CHANGE = 0xc0,
CHANNEL_PRESSURE = 0xd0,
PITCH_BEND = 0xe0,
/* system exclusive */
MIDI_SYSEX = 0xf0,
/* system common - never in midi files */
MIDI_TIME_CODE = 0xf1,
MIDI_SONG_POSITION = 0xf2,
MIDI_SONG_SELECT = 0xf3,
MIDI_TUNE_REQUEST = 0xf6,
MIDI_EOX = 0xf7,
/* system real-time - never in midi files */
MIDI_SYNC = 0xf8,
MIDI_TICK = 0xf9,
MIDI_START = 0xfa,
MIDI_CONTINUE = 0xfb,
MIDI_STOP = 0xfc,
MIDI_ACTIVE_SENSING = 0xfe,
MIDI_SYSTEM_RESET = 0xff,
/* meta event - for midi files only */
MIDI_META_EVENT = 0xff
};
enum fluid_midi_control_change {
BANK_SELECT_MSB = 0x00,
MODULATION_MSB = 0x01,
BREATH_MSB = 0x02,
FOOT_MSB = 0x04,
PORTAMENTO_TIME_MSB = 0x05,
DATA_ENTRY_MSB = 0x06,
VOLUME_MSB = 0x07,
BALANCE_MSB = 0x08,
PAN_MSB = 0x0A,
EXPRESSION_MSB = 0x0B,
EFFECTS1_MSB = 0x0C,
EFFECTS2_MSB = 0x0D,
GPC1_MSB = 0x10, /* general purpose controller */
GPC2_MSB = 0x11,
GPC3_MSB = 0x12,
GPC4_MSB = 0x13,
BANK_SELECT_LSB = 0x20,
MODULATION_WHEEL_LSB = 0x21,
BREATH_LSB = 0x22,
FOOT_LSB = 0x24,
PORTAMENTO_TIME_LSB = 0x25,
DATA_ENTRY_LSB = 0x26,
VOLUME_LSB = 0x27,
BALANCE_LSB = 0x28,
PAN_LSB = 0x2A,
EXPRESSION_LSB = 0x2B,
EFFECTS1_LSB = 0x2C,
EFFECTS2_LSB = 0x2D,
GPC1_LSB = 0x30,
GPC2_LSB = 0x31,
GPC3_LSB = 0x32,
GPC4_LSB = 0x33,
SUSTAIN_SWITCH = 0x40,
PORTAMENTO_SWITCH = 0x41,
SOSTENUTO_SWITCH = 0x42,
SOFT_PEDAL_SWITCH = 0x43,
LEGATO_SWITCH = 0x45,
HOLD2_SWITCH = 0x45,
SOUND_CTRL1 = 0x46,
SOUND_CTRL2 = 0x47,
SOUND_CTRL3 = 0x48,
SOUND_CTRL4 = 0x49,
SOUND_CTRL5 = 0x4A,
SOUND_CTRL6 = 0x4B,
SOUND_CTRL7 = 0x4C,
SOUND_CTRL8 = 0x4D,
SOUND_CTRL9 = 0x4E,
SOUND_CTRL10 = 0x4F,
GPC5 = 0x50,
GPC6 = 0x51,
GPC7 = 0x52,
GPC8 = 0x53,
PORTAMENTO_CTRL = 0x54,
EFFECTS_DEPTH1 = 0x5B,
EFFECTS_DEPTH2 = 0x5C,
EFFECTS_DEPTH3 = 0x5D,
EFFECTS_DEPTH4 = 0x5E,
EFFECTS_DEPTH5 = 0x5F,
DATA_ENTRY_INCR = 0x60,
DATA_ENTRY_DECR = 0x61,
NRPN_LSB = 0x62,
NRPN_MSB = 0x63,
RPN_LSB = 0x64,
RPN_MSB = 0x65,
ALL_SOUND_OFF = 0x78,
ALL_CTRL_OFF = 0x79,
LOCAL_CONTROL = 0x7A,
ALL_NOTES_OFF = 0x7B,
OMNI_OFF = 0x7C,
OMNI_ON = 0x7D,
POLY_OFF = 0x7E,
POLY_ON = 0x7F
};
/* General MIDI RPN event numbers (LSB, MSB = 0) */
enum midi_rpn_event {
RPN_PITCH_BEND_RANGE = 0x00,
RPN_CHANNEL_FINE_TUNE = 0x01,
RPN_CHANNEL_COARSE_TUNE = 0x02,
RPN_TUNING_PROGRAM_CHANGE = 0x03,
RPN_TUNING_BANK_SELECT = 0x04,
RPN_MODULATION_DEPTH_RANGE = 0x05
};
enum midi_meta_event {
MIDI_COPYRIGHT = 0x02,
MIDI_TRACK_NAME = 0x03,
MIDI_INST_NAME = 0x04,
MIDI_LYRIC = 0x05,
MIDI_MARKER = 0x06,
MIDI_CUE_POINT = 0x07,
MIDI_EOT = 0x2f,
MIDI_SET_TEMPO = 0x51,
MIDI_SMPTE_OFFSET = 0x54,
MIDI_TIME_SIGNATURE = 0x58,
MIDI_KEY_SIGNATURE = 0x59,
MIDI_SEQUENCER_EVENT = 0x7f
};
/* MIDI SYSEX useful manufacturer values */
enum midi_sysex_manuf {
MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */
MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */
MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */
};
#define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */
/* SYSEX sub-ID #1 which follows device ID */
#define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */
#define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */
/**
* SYSEX tuning message IDs.
*/
enum midi_sysex_tuning_msg_id {
MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */
MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */
MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */
MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */
MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump resonse (with bank, non-realtime) */
MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */
MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */
MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */
MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */
MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */
};
/* General MIDI sub-ID #2 */
#define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */
#define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */
enum fluid_player_status
{
FLUID_PLAYER_READY,
FLUID_PLAYER_PLAYING,
FLUID_PLAYER_DONE
};
enum fluid_driver_status
{
FLUID_MIDI_READY,
FLUID_MIDI_LISTENING,
FLUID_MIDI_DONE
};
/***************************************************************
*
* TYPE DEFINITIONS & FUNCTION DECLARATIONS
*/
/* From ctype.h */
#define fluid_isascii(c) (((c) & ~0x7f) == 0)
/*
* fluid_midi_event_t
*/
struct _fluid_midi_event_t {
fluid_midi_event_t* next; /* Don't use it, it will dissappear. Used in midi tracks. */
unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */
unsigned char type; /* MIDI event type */
unsigned char channel; /* MIDI channel */
unsigned int param1; /* First parameter */
unsigned int param2; /* Second parameter */
};
#endif /* _FLUID_MIDI_H */

View file

@ -0,0 +1,434 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_mod.h"
#include "fluid_chan.h"
#include "fluid_voice.h"
/*
* fluid_mod_clone
*/
void
fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src)
{
mod->dest = src->dest;
mod->src1 = src->src1;
mod->flags1 = src->flags1;
mod->src2 = src->src2;
mod->flags2 = src->flags2;
mod->amount = src->amount;
}
/*
* fluid_mod_set_source1
*/
void
fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags)
{
mod->src1 = src;
mod->flags1 = flags;
}
/*
* fluid_mod_set_source2
*/
void
fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags)
{
mod->src2 = src;
mod->flags2 = flags;
}
/*
* fluid_mod_set_dest
*/
void
fluid_mod_set_dest(fluid_mod_t* mod, int dest)
{
mod->dest = dest;
}
/*
* fluid_mod_set_amount
*/
void
fluid_mod_set_amount(fluid_mod_t* mod, double amount)
{
mod->amount = (double) amount;
}
int fluid_mod_get_source1(fluid_mod_t* mod)
{
return mod->src1;
}
int fluid_mod_get_flags1(fluid_mod_t* mod)
{
return mod->flags1;
}
int fluid_mod_get_source2(fluid_mod_t* mod)
{
return mod->src2;
}
int fluid_mod_get_flags2(fluid_mod_t* mod)
{
return mod->flags2;
}
int fluid_mod_get_dest(fluid_mod_t* mod)
{
return mod->dest;
}
double fluid_mod_get_amount(fluid_mod_t* mod)
{
return (fluid_real_t) mod->amount;
}
/*
* fluid_mod_get_value
*/
fluid_real_t
fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice)
{
fluid_real_t v1 = 0.0, v2 = 1.0;
fluid_real_t range1 = 127.0, range2 = 127.0;
if (chan == NULL) {
return 0.0f;
}
/* 'special treatment' for default controller
*
* Reference: SF2.01 section 8.4.2
*
* The GM default controller 'vel-to-filter cut off' is not clearly
* defined: If implemented according to the specs, the filter
* frequency jumps between vel=63 and vel=64. To maintain
* compatibility with existing sound fonts, the implementation is
* 'hardcoded', it is impossible to implement using only one
* modulator otherwise.
*
* I assume here, that the 'intention' of the paragraph is one
* octave (1200 cents) filter frequency shift between vel=127 and
* vel=64. 'amount' is (-2400), at least as long as the controller
* is set to default.
*
* Further, the 'appearance' of the modulator (source enumerator,
* destination enumerator, flags etc) is different from that
* described in section 8.4.2, but it matches the definition used in
* several SF2.1 sound fonts (where it is used only to turn it off).
* */
if ((mod->src2 == FLUID_MOD_VELOCITY) &&
(mod->src1 == FLUID_MOD_VELOCITY) &&
(mod->flags1 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR
| FLUID_MOD_NEGATIVE | FLUID_MOD_LINEAR)) &&
(mod->flags2 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR
| FLUID_MOD_POSITIVE | FLUID_MOD_SWITCH)) &&
(mod->dest == GEN_FILTERFC)) {
// S. Christian Collins' mod, to stop forcing velocity based filtering
/*
if (voice->vel < 64){
return (fluid_real_t) mod->amount / 2.0;
} else {
return (fluid_real_t) mod->amount * (127 - voice->vel) / 127;
}
*/
return 0; // (fluid_real_t) mod->amount / 2.0;
}
// end S. Christian Collins' mod
/* get the initial value of the first source */
if (mod->src1 > 0) {
if (mod->flags1 & FLUID_MOD_CC) {
v1 = fluid_channel_get_cc(chan, mod->src1);
} else { /* source 1 is one of the direct controllers */
switch (mod->src1) {
case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */
v1 = range1;
break;
case FLUID_MOD_VELOCITY:
v1 = voice->vel;
break;
case FLUID_MOD_KEY:
v1 = voice->key;
break;
case FLUID_MOD_KEYPRESSURE:
v1 = chan->key_pressure;
break;
case FLUID_MOD_CHANNELPRESSURE:
v1 = chan->channel_pressure;
break;
case FLUID_MOD_PITCHWHEEL:
v1 = chan->pitch_bend;
range1 = 0x4000;
break;
case FLUID_MOD_PITCHWHEELSENS:
v1 = chan->pitch_wheel_sensitivity;
break;
default:
v1 = 0.0;
}
}
/* transform the input value */
switch (mod->flags1 & 0x0f) {
case 0: /* linear, unipolar, positive */
v1 /= range1;
break;
case 1: /* linear, unipolar, negative */
v1 = 1.0f - v1 / range1;
break;
case 2: /* linear, bipolar, positive */
v1 = -1.0f + 2.0f * v1 / range1;
break;
case 3: /* linear, bipolar, negative */
v1 = 1.0f - 2.0f * v1 / range1;
break;
case 4: /* concave, unipolar, positive */
v1 = fluid_concave(v1);
break;
case 5: /* concave, unipolar, negative */
v1 = fluid_concave(127 - v1);
break;
case 6: /* concave, bipolar, positive */
v1 = (v1 > 64)? fluid_concave(2 * (v1 - 64)) : -fluid_concave(2 * (64 - v1));
break;
case 7: /* concave, bipolar, negative */
v1 = (v1 > 64)? -fluid_concave(2 * (v1 - 64)) : fluid_concave(2 * (64 - v1));
break;
case 8: /* convex, unipolar, positive */
v1 = fluid_convex(v1);
break;
case 9: /* convex, unipolar, negative */
v1 = fluid_convex(127 - v1);
break;
case 10: /* convex, bipolar, positive */
v1 = (v1 > 64)? fluid_convex(2 * (v1 - 64)) : -fluid_convex(2 * (64 - v1));
break;
case 11: /* convex, bipolar, negative */
v1 = (v1 > 64)? -fluid_convex(2 * (v1 - 64)) : fluid_convex(2 * (64 - v1));
break;
case 12: /* switch, unipolar, positive */
v1 = (v1 >= 64)? 1.0f : 0.0f;
break;
case 13: /* switch, unipolar, negative */
v1 = (v1 >= 64)? 0.0f : 1.0f;
break;
case 14: /* switch, bipolar, positive */
v1 = (v1 >= 64)? 1.0f : -1.0f;
break;
case 15: /* switch, bipolar, negative */
v1 = (v1 >= 64)? -1.0f : 1.0f;
break;
}
} else {
return 0.0;
}
/* no need to go further */
if (v1 == 0.0f) {
return 0.0f;
}
/* get the second input source */
if (mod->src2 > 0) {
if (mod->flags2 & FLUID_MOD_CC) {
v2 = fluid_channel_get_cc(chan, mod->src2);
} else {
switch (mod->src2) {
case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */
v2 = range2;
break;
case FLUID_MOD_VELOCITY:
v2 = voice->vel;
break;
case FLUID_MOD_KEY:
v2 = voice->key;
break;
case FLUID_MOD_KEYPRESSURE:
v2 = chan->key_pressure;
break;
case FLUID_MOD_CHANNELPRESSURE:
v2 = chan->channel_pressure;
break;
case FLUID_MOD_PITCHWHEEL:
v2 = chan->pitch_bend;
break;
case FLUID_MOD_PITCHWHEELSENS:
v2 = chan->pitch_wheel_sensitivity;
break;
default:
v1 = 0.0f;
}
}
/* transform the second input value */
switch (mod->flags2 & 0x0f) {
case 0: /* linear, unipolar, positive */
v2 /= range2;
break;
case 1: /* linear, unipolar, negative */
v2 = 1.0f - v2 / range2;
break;
case 2: /* linear, bipolar, positive */
v2 = -1.0f + 2.0f * v2 / range2;
break;
case 3: /* linear, bipolar, negative */
v2 = -1.0f + 2.0f * v2 / range2;
break;
case 4: /* concave, unipolar, positive */
v2 = fluid_concave(v2);
break;
case 5: /* concave, unipolar, negative */
v2 = fluid_concave(127 - v2);
break;
case 6: /* concave, bipolar, positive */
v2 = (v2 > 64)? fluid_concave(2 * (v2 - 64)) : -fluid_concave(2 * (64 - v2));
break;
case 7: /* concave, bipolar, negative */
v2 = (v2 > 64)? -fluid_concave(2 * (v2 - 64)) : fluid_concave(2 * (64 - v2));
break;
case 8: /* convex, unipolar, positive */
v2 = fluid_convex(v2);
break;
case 9: /* convex, unipolar, negative */
v2 = 1.0f - fluid_convex(v2);
break;
case 10: /* convex, bipolar, positive */
v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2));
break;
case 11: /* convex, bipolar, negative */
v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2));
break;
case 12: /* switch, unipolar, positive */
v2 = (v2 >= 64)? 1.0f : 0.0f;
break;
case 13: /* switch, unipolar, negative */
v2 = (v2 >= 64)? 0.0f : 1.0f;
break;
case 14: /* switch, bipolar, positive */
v2 = (v2 >= 64)? 1.0f : -1.0f;
break;
case 15: /* switch, bipolar, negative */
v2 = (v2 >= 64)? -1.0f : 1.0f;
break;
}
} else {
v2 = 1.0f;
}
/* it's as simple as that: */
return (fluid_real_t) mod->amount * v1 * v2;
}
/*
* fluid_mod_new
*/
fluid_mod_t*
fluid_mod_new()
{
fluid_mod_t* mod = FLUID_NEW(fluid_mod_t);
if (mod == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
return mod;
};
/*
* fluid_mod_delete
*/
void
fluid_mod_delete(fluid_mod_t * mod)
{
FLUID_FREE(mod);
};
/*
* fluid_mod_test_identity
*/
/* Purpose:
* Checks, if two modulators are identical.
* SF2.01 section 9.5.1 page 69, 'bullet' 3 defines 'identical'.
*/
int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2){
if (mod1->dest != mod2->dest){return 0;};
if (mod1->src1 != mod2->src1){return 0;};
if (mod1->src2 != mod2->src2){return 0;};
if (mod1->flags1 != mod2->flags1){return 0;}
if (mod1->flags2 != mod2->flags2){return 0;}
return 1;
};
/* debug function: Prints the contents of a modulator */
void fluid_dump_modulator(fluid_mod_t * mod){
int src1=mod->src1;
int dest=mod->dest;
int src2=mod->src2;
int flags1=mod->flags1;
int flags2=mod->flags2;
fluid_real_t amount=(fluid_real_t)mod->amount;
printf("Src: ");
if (flags1 & FLUID_MOD_CC){
printf("MIDI CC=%i",src1);
} else {
switch(src1){
case FLUID_MOD_NONE:
printf("None"); break;
case FLUID_MOD_VELOCITY:
printf("note-on velocity"); break;
case FLUID_MOD_KEY:
printf("Key nr"); break;
case FLUID_MOD_KEYPRESSURE:
printf("Poly pressure"); break;
case FLUID_MOD_CHANNELPRESSURE:
printf("Chan pressure"); break;
case FLUID_MOD_PITCHWHEEL:
printf("Pitch Wheel"); break;
case FLUID_MOD_PITCHWHEELSENS:
printf("Pitch Wheel sens"); break;
default:
printf("(unknown: %i)", src1);
}; /* switch src1 */
}; /* if not CC */
if (flags1 & FLUID_MOD_NEGATIVE){printf("- ");} else {printf("+ ");};
if (flags1 & FLUID_MOD_BIPOLAR){printf("bip ");} else {printf("unip ");};
printf("-> ");
switch(dest){
case GEN_FILTERQ: printf("Q"); break;
case GEN_FILTERFC: printf("fc"); break;
case GEN_VIBLFOTOPITCH: printf("VibLFO-to-pitch"); break;
case GEN_MODENVTOPITCH: printf("ModEnv-to-pitch"); break;
case GEN_MODLFOTOPITCH: printf("ModLFO-to-pitch"); break;
case GEN_CHORUSSEND: printf("Chorus send"); break;
case GEN_REVERBSEND: printf("Reverb send"); break;
case GEN_PAN: printf("pan"); break;
case GEN_ATTENUATION: printf("att"); break;
default: printf("dest %i",dest);
}; /* switch dest */
printf(", amount %f flags %i src2 %i flags2 %i\n",amount, flags1, src2, flags2);
};

View file

@ -0,0 +1,40 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_MOD_H
#define _FLUID_MOD_H
#include "fluidsynth_priv.h"
#include "fluid_conv.h"
void fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src);
fluid_real_t fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice);
void fluid_dump_modulator(fluid_mod_t * mod);
#define fluid_mod_has_source(mod,cc,ctrl) \
( ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) != 0) && (cc != 0)) \
|| ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) == 0) && (cc == 0)))) \
|| ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) != 0) && (cc != 0)) \
|| ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) == 0) && (cc == 0)))))
#define fluid_mod_has_dest(mod,gen) ((mod)->dest == gen)
#endif /* _FLUID_MOD_H */

View file

@ -0,0 +1,115 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_PHASE_H
#define _FLUID_PHASE_H
#include "fluid_config.h"
/*
* phase
*/
#define FLUID_INTERP_BITS 8
#define FLUID_INTERP_BITS_MASK 0xff000000
#define FLUID_INTERP_BITS_SHIFT 24
#define FLUID_INTERP_MAX 256
#define FLUID_FRACT_MAX ((double)4294967296.0)
/* fluid_phase_t
* Purpose:
* Playing pointer for voice playback
*
* When a sample is played back at a different pitch, the playing pointer in the
* source sample will not advance exactly one sample per output sample.
* This playing pointer is implemented using fluid_phase_t.
* It is a 64 bit number. The higher 32 bits contain the 'index' (number of
* the current sample), the lower 32 bits the fractional part.
*/
typedef unsigned long long fluid_phase_t;
/* Purpose:
* Set a to b.
* a: fluid_phase_t
* b: fluid_phase_t
*/
#define fluid_phase_set(a,b) a=b;
#define fluid_phase_set_int(a, b) ((a) = ((unsigned long long)(b)) << 32)
/* Purpose:
* Sets the phase a to a phase increment given in b.
* For example, assume b is 0.9. After setting a to it, adding a to
* the playing pointer will advance it by 0.9 samples. */
#define fluid_phase_set_float(a, b) \
(a) = (((unsigned long long)(b)) << 32) \
| (uint32) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX)
/* create a fluid_phase_t from an index and a fraction value */
#define fluid_phase_from_index_fract(index, fract) \
((((unsigned long long)(index)) << 32) + (fract))
/* Purpose:
* Return the index and the fractional part, respectively. */
#define fluid_phase_index(_x) \
((unsigned int)((_x) >> 32))
#define fluid_phase_fract(_x) \
((uint32)((_x) & 0xFFFFFFFF))
/* Get the phase index with fractional rounding */
#define fluid_phase_index_round(_x) \
((unsigned int)(((_x) + 0x80000000) >> 32))
/* Purpose:
* Takes the fractional part of the argument phase and
* calculates the corresponding position in the interpolation table.
* The fractional position of the playing pointer is calculated with a quite high
* resolution (32 bits). It would be unpractical to keep a set of interpolation
* coefficients for each possible fractional part...
*/
#define fluid_phase_fract_to_tablerow(_x) \
((unsigned int)(fluid_phase_fract(_x) & FLUID_INTERP_BITS_MASK) >> FLUID_INTERP_BITS_SHIFT)
#define fluid_phase_double(_x) \
((double)(fluid_phase_index(_x)) + ((double)fluid_phase_fract(_x) / FLUID_FRACT_MAX))
/* Purpose:
* Advance a by a step of b (both are fluid_phase_t).
*/
#define fluid_phase_incr(a, b) a += b
/* Purpose:
* Subtract b from a (both are fluid_phase_t).
*/
#define fluid_phase_decr(a, b) a -= b
/* Purpose:
* Subtract b samples from a.
*/
#define fluid_phase_sub_int(a, b) ((a) -= (unsigned long long)(b) << 32)
/* Purpose:
* Creates the expression a.index++. */
#define fluid_phase_index_plusplus(a) (((a) += 0x100000000LL)
#endif /* _FLUID_PHASE_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,114 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_RAMSFONT_H
#define _FLUID_RAMSFONT_H
#include "fluidlite.h"
#include "fluidsynth_priv.h"
#include "fluid_defsfont.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
Public interface
*/
int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont);
char* fluid_ramsfont_sfont_get_name(fluid_sfont_t* sfont);
fluid_preset_t* fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum);
void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont);
int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset);
int fluid_rampreset_preset_delete(fluid_preset_t* preset);
char* fluid_rampreset_preset_get_name(fluid_preset_t* preset);
int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset);
int fluid_rampreset_preset_get_num(fluid_preset_t* preset);
int fluid_rampreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
/*
* fluid_ramsfont_t
*/
struct _fluid_ramsfont_t
{
char name[21]; /* the name of the soundfont */
fluid_list_t* sample; /* the samples in this soundfont */
fluid_rampreset_t* preset; /* the presets of this soundfont */
fluid_preset_t iter_preset; /* preset interface used in the iteration */
fluid_rampreset_t* iter_cur; /* the current preset in the iteration */
};
/* interface */
fluid_ramsfont_t* new_fluid_ramsfont(void);
int delete_fluid_ramsfont(fluid_ramsfont_t* sfont);
char* fluid_ramsfont_get_name(fluid_ramsfont_t* sfont);
fluid_rampreset_t* fluid_ramsfont_get_preset(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int prenum);
void fluid_ramsfont_iteration_start(fluid_ramsfont_t* sfont);
int fluid_ramsfont_iteration_next(fluid_ramsfont_t* sfont, fluid_preset_t* preset);
/* specific */
/*
* fluid_preset_t
*/
struct _fluid_rampreset_t
{
fluid_rampreset_t* next;
fluid_ramsfont_t* sfont; /* the soundfont this preset belongs to */
char name[21]; /* the name of the preset */
unsigned int bank; /* the bank number */
unsigned int num; /* the preset number */
fluid_preset_zone_t* global_zone; /* the global zone of the preset */
fluid_preset_zone_t* zone; /* the chained list of preset zones */
fluid_list_t *presetvoices; /* chained list of used voices */
};
/* interface */
fluid_rampreset_t* new_fluid_rampreset(fluid_ramsfont_t* sfont);
int delete_fluid_rampreset(fluid_rampreset_t* preset);
fluid_rampreset_t* fluid_rampreset_next(fluid_rampreset_t* preset);
char* fluid_rampreset_get_name(fluid_rampreset_t* preset);
int fluid_rampreset_get_banknum(fluid_rampreset_t* preset);
int fluid_rampreset_get_num(fluid_rampreset_t* preset);
int fluid_rampreset_noteon(fluid_rampreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
#ifdef __cplusplus
}
#endif
#endif /* _FLUID_SFONT_H */

View file

@ -0,0 +1,561 @@
/*
Freeverb
Written by Jezar at Dreampoint, June 2000
http://www.dreampoint.co.uk
This code is public domain
Translated to C by Peter Hanappe, Mai 2001
*/
#include "fluid_rev.h"
/***************************************************************
*
* REVERB
*/
/* Denormalising:
*
* According to music-dsp thread 'Denormalise', Pentium processors
* have a hardware 'feature', that is of interest here, related to
* numeric underflow. We have a recursive filter. The output decays
* exponentially, if the input stops. So the numbers get smaller and
* smaller... At some point, they reach 'denormal' level. This will
* lead to drastic spikes in the CPU load. The effect was reproduced
* with the reverb - sometimes the average load over 10 s doubles!!.
*
* The 'undenormalise' macro fixes the problem: As soon as the number
* is close enough to denormal level, the macro forces the number to
* 0.0f. The original macro is:
*
* #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
*
* This will zero out a number when it reaches the denormal level.
* Advantage: Maximum dynamic range Disadvantage: We'll have to check
* every sample, expensive. The alternative macro comes from a later
* mail from Jon Watte. It will zap a number before it reaches
* denormal level. Jon suggests to run it once per block instead of
* every sample.
*/
# if defined(WITH_FLOATX)
# define zap_almost_zero(sample) (((*(unsigned int*)&(sample))&0x7f800000) < 0x08000000)?0.0f:(sample)
# else
/* 1e-20 was chosen as an arbitrary (small) threshold. */
#define zap_almost_zero(sample) fabs(sample)<1e-10 ? 0 : sample;
#endif
/* Denormalising part II:
*
* Another method fixes the problem cheaper: Use a small DC-offset in
* the filter calculations. Now the signals converge not against 0,
* but against the offset. The constant offset is invisible from the
* outside world (i.e. it does not appear at the output. There is a
* very small turn-on transient response, which should not cause
* problems.
*/
//#define DC_OFFSET 0
#define DC_OFFSET 1e-8
//#define DC_OFFSET 0.001f
typedef struct _fluid_allpass fluid_allpass;
typedef struct _fluid_comb fluid_comb;
struct _fluid_allpass {
fluid_real_t feedback;
fluid_real_t *buffer;
int bufsize;
int bufidx;
};
void fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size);
void fluid_allpass_init(fluid_allpass* allpass);
void fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val);
fluid_real_t fluid_allpass_getfeedback(fluid_allpass* allpass);
void
fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size)
{
allpass->bufidx = 0;
allpass->buffer = buf;
allpass->bufsize = size;
}
void
fluid_allpass_init(fluid_allpass* allpass)
{
int i;
int len = allpass->bufsize;
fluid_real_t* buf = allpass->buffer;
for (i = 0; i < len; i++) {
buf[i] = DC_OFFSET; /* this is not 100 % correct. */
}
}
void
fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val)
{
allpass->feedback = val;
}
fluid_real_t
fluid_allpass_getfeedback(fluid_allpass* allpass)
{
return allpass->feedback;
}
#define fluid_allpass_process(_allpass, _input) \
{ \
fluid_real_t output; \
fluid_real_t bufout; \
bufout = _allpass.buffer[_allpass.bufidx]; \
output = bufout-_input; \
_allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \
if (++_allpass.bufidx >= _allpass.bufsize) { \
_allpass.bufidx = 0; \
} \
_input = output; \
}
/* fluid_real_t fluid_allpass_process(fluid_allpass* allpass, fluid_real_t input) */
/* { */
/* fluid_real_t output; */
/* fluid_real_t bufout; */
/* bufout = allpass->buffer[allpass->bufidx]; */
/* undenormalise(bufout); */
/* output = -input + bufout; */
/* allpass->buffer[allpass->bufidx] = input + (bufout * allpass->feedback); */
/* if (++allpass->bufidx >= allpass->bufsize) { */
/* allpass->bufidx = 0; */
/* } */
/* return output; */
/* } */
struct _fluid_comb {
fluid_real_t feedback;
fluid_real_t filterstore;
fluid_real_t damp1;
fluid_real_t damp2;
fluid_real_t *buffer;
int bufsize;
int bufidx;
};
void fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size);
void fluid_comb_init(fluid_comb* comb);
void fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val);
fluid_real_t fluid_comb_getdamp(fluid_comb* comb);
void fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val);
fluid_real_t fluid_comb_getfeedback(fluid_comb* comb);
void
fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size)
{
comb->filterstore = 0;
comb->bufidx = 0;
comb->buffer = buf;
comb->bufsize = size;
}
void
fluid_comb_init(fluid_comb* comb)
{
int i;
fluid_real_t* buf = comb->buffer;
int len = comb->bufsize;
for (i = 0; i < len; i++) {
buf[i] = DC_OFFSET; /* This is not 100 % correct. */
}
}
void
fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val)
{
comb->damp1 = val;
comb->damp2 = 1 - val;
}
fluid_real_t
fluid_comb_getdamp(fluid_comb* comb)
{
return comb->damp1;
}
void
fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val)
{
comb->feedback = val;
}
fluid_real_t
fluid_comb_getfeedback(fluid_comb* comb)
{
return comb->feedback;
}
#define fluid_comb_process(_comb, _input, _output) \
{ \
fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \
_comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \
_comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \
if (++_comb.bufidx >= _comb.bufsize) { \
_comb.bufidx = 0; \
} \
_output += _tmp; \
}
/* fluid_real_t fluid_comb_process(fluid_comb* comb, fluid_real_t input) */
/* { */
/* fluid_real_t output; */
/* output = comb->buffer[comb->bufidx]; */
/* undenormalise(output); */
/* comb->filterstore = (output * comb->damp2) + (comb->filterstore * comb->damp1); */
/* undenormalise(comb->filterstore); */
/* comb->buffer[comb->bufidx] = input + (comb->filterstore * comb->feedback); */
/* if (++comb->bufidx >= comb->bufsize) { */
/* comb->bufidx = 0; */
/* } */
/* return output; */
/* } */
#define numcombs 8
#define numallpasses 4
#define fixedgain 0.015f
#define scalewet 3.0f
#define scaledamp 1.0f
#define scaleroom 0.28f
#define offsetroom 0.7f
#define initialroom 0.5f
#define initialdamp 0.2f
#define initialwet 1
#define initialdry 0
#define initialwidth 1
#define stereospread 23
/*
These values assume 44.1KHz sample rate
they will probably be OK for 48KHz sample rate
but would need scaling for 96KHz (or other) sample rates.
The values were obtained by listening tests.
*/
#define combtuningL1 1116
#define combtuningR1 1116 + stereospread
#define combtuningL2 1188
#define combtuningR2 1188 + stereospread
#define combtuningL3 1277
#define combtuningR3 1277 + stereospread
#define combtuningL4 1356
#define combtuningR4 1356 + stereospread
#define combtuningL5 1422
#define combtuningR5 1422 + stereospread
#define combtuningL6 1491
#define combtuningR6 1491 + stereospread
#define combtuningL7 1557
#define combtuningR7 1557 + stereospread
#define combtuningL8 1617
#define combtuningR8 1617 + stereospread
#define allpasstuningL1 556
#define allpasstuningR1 556 + stereospread
#define allpasstuningL2 441
#define allpasstuningR2 441 + stereospread
#define allpasstuningL3 341
#define allpasstuningR3 341 + stereospread
#define allpasstuningL4 225
#define allpasstuningR4 225 + stereospread
struct _fluid_revmodel_t {
fluid_real_t roomsize;
fluid_real_t damp;
fluid_real_t wet, wet1, wet2;
fluid_real_t width;
fluid_real_t gain;
/*
The following are all declared inline
to remove the need for dynamic allocation
with its subsequent error-checking messiness
*/
/* Comb filters */
fluid_comb combL[numcombs];
fluid_comb combR[numcombs];
/* Allpass filters */
fluid_allpass allpassL[numallpasses];
fluid_allpass allpassR[numallpasses];
/* Buffers for the combs */
fluid_real_t bufcombL1[combtuningL1];
fluid_real_t bufcombR1[combtuningR1];
fluid_real_t bufcombL2[combtuningL2];
fluid_real_t bufcombR2[combtuningR2];
fluid_real_t bufcombL3[combtuningL3];
fluid_real_t bufcombR3[combtuningR3];
fluid_real_t bufcombL4[combtuningL4];
fluid_real_t bufcombR4[combtuningR4];
fluid_real_t bufcombL5[combtuningL5];
fluid_real_t bufcombR5[combtuningR5];
fluid_real_t bufcombL6[combtuningL6];
fluid_real_t bufcombR6[combtuningR6];
fluid_real_t bufcombL7[combtuningL7];
fluid_real_t bufcombR7[combtuningR7];
fluid_real_t bufcombL8[combtuningL8];
fluid_real_t bufcombR8[combtuningR8];
/* Buffers for the allpasses */
fluid_real_t bufallpassL1[allpasstuningL1];
fluid_real_t bufallpassR1[allpasstuningR1];
fluid_real_t bufallpassL2[allpasstuningL2];
fluid_real_t bufallpassR2[allpasstuningR2];
fluid_real_t bufallpassL3[allpasstuningL3];
fluid_real_t bufallpassR3[allpasstuningR3];
fluid_real_t bufallpassL4[allpasstuningL4];
fluid_real_t bufallpassR4[allpasstuningR4];
};
void fluid_revmodel_update(fluid_revmodel_t* rev);
void fluid_revmodel_init(fluid_revmodel_t* rev);
fluid_revmodel_t*
new_fluid_revmodel()
{
fluid_revmodel_t* rev;
rev = FLUID_NEW(fluid_revmodel_t);
if (rev == NULL) {
return NULL;
}
/* Tie the components to their buffers */
fluid_comb_setbuffer(&rev->combL[0], rev->bufcombL1, combtuningL1);
fluid_comb_setbuffer(&rev->combR[0], rev->bufcombR1, combtuningR1);
fluid_comb_setbuffer(&rev->combL[1], rev->bufcombL2, combtuningL2);
fluid_comb_setbuffer(&rev->combR[1], rev->bufcombR2, combtuningR2);
fluid_comb_setbuffer(&rev->combL[2], rev->bufcombL3, combtuningL3);
fluid_comb_setbuffer(&rev->combR[2], rev->bufcombR3, combtuningR3);
fluid_comb_setbuffer(&rev->combL[3], rev->bufcombL4, combtuningL4);
fluid_comb_setbuffer(&rev->combR[3], rev->bufcombR4, combtuningR4);
fluid_comb_setbuffer(&rev->combL[4], rev->bufcombL5, combtuningL5);
fluid_comb_setbuffer(&rev->combR[4], rev->bufcombR5, combtuningR5);
fluid_comb_setbuffer(&rev->combL[5], rev->bufcombL6, combtuningL6);
fluid_comb_setbuffer(&rev->combR[5], rev->bufcombR6, combtuningR6);
fluid_comb_setbuffer(&rev->combL[6], rev->bufcombL7, combtuningL7);
fluid_comb_setbuffer(&rev->combR[6], rev->bufcombR7, combtuningR7);
fluid_comb_setbuffer(&rev->combL[7], rev->bufcombL8, combtuningL8);
fluid_comb_setbuffer(&rev->combR[7], rev->bufcombR8, combtuningR8);
fluid_allpass_setbuffer(&rev->allpassL[0], rev->bufallpassL1, allpasstuningL1);
fluid_allpass_setbuffer(&rev->allpassR[0], rev->bufallpassR1, allpasstuningR1);
fluid_allpass_setbuffer(&rev->allpassL[1], rev->bufallpassL2, allpasstuningL2);
fluid_allpass_setbuffer(&rev->allpassR[1], rev->bufallpassR2, allpasstuningR2);
fluid_allpass_setbuffer(&rev->allpassL[2], rev->bufallpassL3, allpasstuningL3);
fluid_allpass_setbuffer(&rev->allpassR[2], rev->bufallpassR3, allpasstuningR3);
fluid_allpass_setbuffer(&rev->allpassL[3], rev->bufallpassL4, allpasstuningL4);
fluid_allpass_setbuffer(&rev->allpassR[3], rev->bufallpassR4, allpasstuningR4);
/* Set default values */
fluid_allpass_setfeedback(&rev->allpassL[0], 0.5f);
fluid_allpass_setfeedback(&rev->allpassR[0], 0.5f);
fluid_allpass_setfeedback(&rev->allpassL[1], 0.5f);
fluid_allpass_setfeedback(&rev->allpassR[1], 0.5f);
fluid_allpass_setfeedback(&rev->allpassL[2], 0.5f);
fluid_allpass_setfeedback(&rev->allpassR[2], 0.5f);
fluid_allpass_setfeedback(&rev->allpassL[3], 0.5f);
fluid_allpass_setfeedback(&rev->allpassR[3], 0.5f);
/* set values manually, since calling set functions causes update
and all values should be initialized for an update */
rev->roomsize = initialroom * scaleroom + offsetroom;
rev->damp = initialdamp * scaledamp;
rev->wet = initialwet * scalewet;
rev->width = initialwidth;
rev->gain = fixedgain;
/* now its okay to update reverb */
fluid_revmodel_update(rev);
/* Clear all buffers */
fluid_revmodel_init(rev);
return rev;
}
void
delete_fluid_revmodel(fluid_revmodel_t* rev)
{
FLUID_FREE(rev);
}
void
fluid_revmodel_init(fluid_revmodel_t* rev)
{
int i;
for (i = 0; i < numcombs;i++) {
fluid_comb_init(&rev->combL[i]);
fluid_comb_init(&rev->combR[i]);
}
for (i = 0; i < numallpasses; i++) {
fluid_allpass_init(&rev->allpassL[i]);
fluid_allpass_init(&rev->allpassR[i]);
}
}
void
fluid_revmodel_reset(fluid_revmodel_t* rev)
{
fluid_revmodel_init(rev);
}
void
fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out)
{
int i, k = 0;
fluid_real_t outL, outR, input;
for (k = 0; k < FLUID_BUFSIZE; k++) {
outL = outR = 0;
/* The original Freeverb code expects a stereo signal and 'input'
* is set to the sum of the left and right input sample. Since
* this code works on a mono signal, 'input' is set to twice the
* input sample. */
input = (2 * in[k] + DC_OFFSET) * rev->gain;
/* Accumulate comb filters in parallel */
for (i = 0; i < numcombs; i++) {
fluid_comb_process(rev->combL[i], input, outL);
fluid_comb_process(rev->combR[i], input, outR);
}
/* Feed through allpasses in series */
for (i = 0; i < numallpasses; i++) {
fluid_allpass_process(rev->allpassL[i], outL);
fluid_allpass_process(rev->allpassR[i], outR);
}
/* Remove the DC offset */
outL -= DC_OFFSET;
outR -= DC_OFFSET;
/* Calculate output REPLACING anything already there */
left_out[k] = outL * rev->wet1 + outR * rev->wet2;
right_out[k] = outR * rev->wet1 + outL * rev->wet2;
}
}
void
fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out)
{
int i, k = 0;
fluid_real_t outL, outR, input;
for (k = 0; k < FLUID_BUFSIZE; k++) {
outL = outR = 0;
/* The original Freeverb code expects a stereo signal and 'input'
* is set to the sum of the left and right input sample. Since
* this code works on a mono signal, 'input' is set to twice the
* input sample. */
input = (2 * in[k] + DC_OFFSET) * rev->gain;
/* Accumulate comb filters in parallel */
for (i = 0; i < numcombs; i++) {
fluid_comb_process(rev->combL[i], input, outL);
fluid_comb_process(rev->combR[i], input, outR);
}
/* Feed through allpasses in series */
for (i = 0; i < numallpasses; i++) {
fluid_allpass_process(rev->allpassL[i], outL);
fluid_allpass_process(rev->allpassR[i], outR);
}
/* Remove the DC offset */
outL -= DC_OFFSET;
outR -= DC_OFFSET;
/* Calculate output MIXING with anything already there */
left_out[k] += outL * rev->wet1 + outR * rev->wet2;
right_out[k] += outR * rev->wet1 + outL * rev->wet2;
}
}
void
fluid_revmodel_update(fluid_revmodel_t* rev)
{
/* Recalculate internal values after parameter change */
int i;
rev->wet1 = rev->wet * (rev->width / 2 + 0.5f);
rev->wet2 = rev->wet * ((1 - rev->width) / 2);
for (i = 0; i < numcombs; i++) {
fluid_comb_setfeedback(&rev->combL[i], rev->roomsize);
fluid_comb_setfeedback(&rev->combR[i], rev->roomsize);
}
for (i = 0; i < numcombs; i++) {
fluid_comb_setdamp(&rev->combL[i], rev->damp);
fluid_comb_setdamp(&rev->combR[i], rev->damp);
}
}
/*
The following get/set functions are not inlined, because
speed is never an issue when calling them, and also
because as you develop the reverb model, you may
wish to take dynamic action when they are called.
*/
void
fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value)
{
/* fluid_clip(value, 0.0f, 1.0f); */
rev->roomsize = (value * scaleroom) + offsetroom;
fluid_revmodel_update(rev);
}
fluid_real_t
fluid_revmodel_getroomsize(fluid_revmodel_t* rev)
{
return (rev->roomsize - offsetroom) / scaleroom;
}
void
fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value)
{
/* fluid_clip(value, 0.0f, 1.0f); */
rev->damp = value * scaledamp;
fluid_revmodel_update(rev);
}
fluid_real_t
fluid_revmodel_getdamp(fluid_revmodel_t* rev)
{
return rev->damp / scaledamp;
}
void
fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value)
{
fluid_clip(value, 0.0f, 1.0f);
rev->wet = value * scalewet;
fluid_revmodel_update(rev);
}
fluid_real_t
fluid_revmodel_getlevel(fluid_revmodel_t* rev)
{
return rev->wet / scalewet;
}
void
fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value)
{
/* fluid_clip(value, 0.0f, 1.0f); */
rev->width = value;
fluid_revmodel_update(rev);
}
fluid_real_t
fluid_revmodel_getwidth(fluid_revmodel_t* rev)
{
return rev->width;
}

View file

@ -0,0 +1,67 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_REV_H
#define _FLUID_REV_H
#include "fluidsynth_priv.h"
typedef struct _fluid_revmodel_t fluid_revmodel_t;
/*
* reverb
*/
fluid_revmodel_t* new_fluid_revmodel(void);
void delete_fluid_revmodel(fluid_revmodel_t* rev);
void fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);
void fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);
void fluid_revmodel_reset(fluid_revmodel_t* rev);
void fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value);
void fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value);
void fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value);
void fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value);
void fluid_revmodel_setmode(fluid_revmodel_t* rev, fluid_real_t value);
fluid_real_t fluid_revmodel_getroomsize(fluid_revmodel_t* rev);
fluid_real_t fluid_revmodel_getdamp(fluid_revmodel_t* rev);
fluid_real_t fluid_revmodel_getlevel(fluid_revmodel_t* rev);
fluid_real_t fluid_revmodel_getwidth(fluid_revmodel_t* rev);
/*
* reverb preset
*/
typedef struct _fluid_revmodel_presets_t {
char* name;
fluid_real_t roomsize;
fluid_real_t damp;
fluid_real_t width;
fluid_real_t level;
} fluid_revmodel_presets_t;
#endif /* _FLUID_REV_H */

View file

@ -0,0 +1,827 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluidsynth_priv.h"
#include "fluid_sys.h"
#include "fluid_hash.h"
#include "fluid_synth.h"
#include "fluid_settings.h"
/* maximum allowed components of a settings variable (separated by '.') */
#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */
#define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */
static void fluid_settings_init(fluid_settings_t* settings);
static void fluid_settings_hash_delete(void* value, int type);
static int fluid_settings_tokenize(const char* s, char *buf, char** ptr);
typedef struct {
char* value;
char* def;
int hints;
fluid_list_t* options;
fluid_str_update_t update;
void* data;
} fluid_str_setting_t;
static fluid_str_setting_t*
new_fluid_str_setting(const char* value, char* def, int hints, fluid_str_update_t fun, void* data)
{
fluid_str_setting_t* str;
str = FLUID_NEW(fluid_str_setting_t);
str->value = value? FLUID_STRDUP(value) : NULL;
str->def = def? FLUID_STRDUP(def) : NULL;
str->hints = hints;
str->options = NULL;
str->update = fun;
str->data = data;
return str;
}
static void delete_fluid_str_setting(fluid_str_setting_t* str)
{
if (str) {
if (str->value) {
FLUID_FREE(str->value);
}
if (str->def) {
FLUID_FREE(str->def);
}
if (str->options) {
fluid_list_t* list = str->options;
while (list) {
FLUID_FREE (list->data);
list = fluid_list_next(list);
}
delete_fluid_list(str->options);
}
FLUID_FREE(str);
}
}
typedef struct {
double value;
double def;
double min;
double max;
int hints;
fluid_num_update_t update;
void* data;
} fluid_num_setting_t;
static fluid_num_setting_t*
new_fluid_num_setting(double min, double max, double def,
int hints, fluid_num_update_t fun, void* data)
{
fluid_num_setting_t* setting;
setting = FLUID_NEW(fluid_num_setting_t);
setting->value = def;
setting->def = def;
setting->min = min;
setting->max = max;
setting->hints = hints;
setting->update = fun;
setting->data = data;
return setting;
}
static void delete_fluid_num_setting(fluid_num_setting_t* setting)
{
if (setting) {
FLUID_FREE(setting);
}
}
typedef struct {
int value;
int def;
int min;
int max;
int hints;
fluid_int_update_t update;
void* data;
} fluid_int_setting_t;
static fluid_int_setting_t*
new_fluid_int_setting(int min, int max, int def,
int hints, fluid_int_update_t fun, void* data)
{
fluid_int_setting_t* setting;
setting = FLUID_NEW(fluid_int_setting_t);
setting->value = def;
setting->def = def;
setting->min = min;
setting->max = max;
setting->hints = hints;
setting->update = fun;
setting->data = data;
return setting;
}
static void delete_fluid_int_setting(fluid_int_setting_t* setting)
{
if (setting) {
FLUID_FREE(setting);
}
}
fluid_settings_t* new_fluid_settings()
{
fluid_settings_t* settings = new_fluid_hashtable(fluid_settings_hash_delete);
if (settings == NULL) {
return NULL;
}
fluid_settings_init(settings);
return settings;
}
void delete_fluid_settings(fluid_settings_t* settings)
{
delete_fluid_hashtable(settings);
}
void fluid_settings_hash_delete(void* value, int type)
{
switch (type) {
case FLUID_NUM_TYPE:
delete_fluid_num_setting((fluid_num_setting_t*) value);
break;
case FLUID_INT_TYPE:
delete_fluid_int_setting((fluid_int_setting_t*) value);
break;
case FLUID_STR_TYPE:
delete_fluid_str_setting((fluid_str_setting_t*) value);
break;
case FLUID_SET_TYPE:
delete_fluid_hashtable((fluid_hashtable_t*) value);
break;
}
}
void fluid_settings_init(fluid_settings_t* settings)
{
fluid_synth_settings(settings);
}
static int fluid_settings_tokenize(const char* s, char *buf, char** ptr)
{
char *tokstr, *tok;
int n = 0;
if (strlen (s) > MAX_SETTINGS_LABEL)
{
FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars",
MAX_SETTINGS_LABEL);
return 0;
}
FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */
tokstr = buf;
while ((tok = fluid_strtok (&tokstr, ".")))
{
if (n > MAX_SETTINGS_TOKENS)
{
FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d",
MAX_SETTINGS_TOKENS);
return 0;
}
ptr[n++] = tok;
}
return n;
}
/** returns 1 if the value exists, 0 otherwise */
static int fluid_settings_get(fluid_settings_t* settings,
char** name, int len,
void** value, int* type)
{
fluid_hashtable_t* table = settings;
int t;
void* v;
int n;
for (n = 0; n < len; n++) {
if (table == NULL) {
return 0;
}
if (!fluid_hashtable_lookup(table, name[n], &v, &t)) {
return 0;
}
table = (t == FLUID_SET_TYPE)? (fluid_hashtable_t*) v : NULL;
}
if (value) {
*value = v;
}
if (type) {
*type = t;
}
return 1;
}
/** returns 1 if the value has been set, zero otherwise */
static int fluid_settings_set(fluid_settings_t* settings,
char** name, int len,
void* value, int type)
{
fluid_hashtable_t* table = settings;
int t;
void* v;
int n, num = len - 1;
for (n = 0; n < num; n++) {
if (fluid_hashtable_lookup(table, name[n], &v, &t)) {
if (t == FLUID_SET_TYPE) {
table = (fluid_hashtable_t*) v;
} else {
/* path ends prematurely */
FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]);
return 0;
}
} else {
/* create a new node */
fluid_hashtable_t* tmp;
tmp = new_fluid_hashtable(fluid_settings_hash_delete);
fluid_hashtable_insert(table, name[n], tmp, FLUID_SET_TYPE);
table = tmp;
}
}
fluid_hashtable_replace(table, name[num], value, type);
return 1;
}
/** returns 1 if the value has been registered correctly, 0
otherwise */
int fluid_settings_register_str(fluid_settings_t* settings, const char* name, char* def, int hints,
fluid_str_update_t fun, void* data)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
fluid_str_setting_t* setting;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
setting = new_fluid_str_setting(def, def, hints, fun, data);
return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE);
} else {
/* if variable already exists, don't change its value. */
if (type == FLUID_STR_TYPE) {
setting = (fluid_str_setting_t*) value;
setting->update = fun;
setting->data = data;
setting->def = def? FLUID_STRDUP(def) : NULL;
setting->hints = hints;
return 1;
} else {
FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
return 1;
}
}
}
/** returns 1 if the value has been register correctly, zero
otherwise */
int fluid_settings_register_num(fluid_settings_t* settings, const char* name, double def,
double min, double max, int hints,
fluid_num_update_t fun, void* data)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
/* insert a new setting */
fluid_num_setting_t* setting;
setting = new_fluid_num_setting(min, max, def, hints, fun, data);
return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE);
} else {
if (type == FLUID_NUM_TYPE) {
/* update the existing setting but don't change its value */
fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
setting->update = fun;
setting->data = data;
setting->min = min;
setting->max = max;
setting->def = def;
setting->hints = hints;
return 1;
} else {
/* type mismatch */
FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
return 0;
}
}
}
/** returns 1 if the value has been register correctly, zero
otherwise */
int fluid_settings_register_int(fluid_settings_t* settings, const char* name, int def,
int min, int max, int hints,
fluid_int_update_t fun, void* data)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
/* insert a new setting */
fluid_int_setting_t* setting;
setting = new_fluid_int_setting(min, max, def, hints, fun, data);
return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE);
} else {
if (type == FLUID_INT_TYPE) {
/* update the existing setting but don't change its value */
fluid_int_setting_t* setting = (fluid_int_setting_t*) value;
setting->update = fun;
setting->data = data;
setting->min = min;
setting->max = max;
setting->def = def;
setting->hints = hints;
return 1;
} else {
/* type mismatch */
FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
return 0;
}
}
}
int fluid_settings_get_type(fluid_settings_t* settings, const char* name)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
return (fluid_settings_get(settings, tokens, ntokens, &value, &type))? type : FLUID_NO_TYPE;
}
int fluid_settings_get_hints(fluid_settings_t* settings, const char* name)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
if (type == FLUID_NUM_TYPE) {
fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
return setting->hints;
} else if (type == FLUID_STR_TYPE) {
fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
return setting->hints;
} else {
return 0;
}
} else {
return 0;
}
}
int fluid_settings_is_realtime(fluid_settings_t* settings, const char* name)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
if (type == FLUID_NUM_TYPE) {
fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
return setting->update != NULL;
} else if (type == FLUID_STR_TYPE) {
fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
return setting->update != NULL;
} else {
return 0;
}
} else {
return 0;
}
}
int fluid_settings_setstr(fluid_settings_t* settings, const char* name, const char* str)
{
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
int type;
void* value;
fluid_str_setting_t* setting;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
if (type != FLUID_STR_TYPE) {
return 0;
}
setting = (fluid_str_setting_t*) value;
if (setting->value) {
FLUID_FREE(setting->value);
}
setting->value = str? FLUID_STRDUP(str) : NULL;
if (setting->update) {
(*setting->update)(setting->data, name, setting->value);
}
return 1;
} else {
/* insert a new setting */
fluid_str_setting_t* setting;
setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL);
return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE);
}
}
int fluid_settings_getstr(fluid_settings_t* settings, const char* name, char** str)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_STR_TYPE)) {
fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
*str = setting->value;
return 1;
}
*str = NULL;
return 0;
}
int fluid_settings_str_equal(fluid_settings_t* settings, const char* name, char* s)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_STR_TYPE)) {
fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
return FLUID_STRCMP(setting->value, s) == 0;
}
return 0;
}
char*
fluid_settings_getstr_default(fluid_settings_t* settings, const char* name)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_STR_TYPE)) {
fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
return setting->def;
} else {
return NULL;
}
}
int fluid_settings_add_option(fluid_settings_t* settings, const char* name, char* s)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_STR_TYPE)) {
fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
char* copy = FLUID_STRDUP(s);
setting->options = fluid_list_append(setting->options, copy);
return 1;
} else {
return 0;
}
}
int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, char* s)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_STR_TYPE)) {
fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
fluid_list_t* list = setting->options;
while (list) {
char* option = (char*) fluid_list_get(list);
if (FLUID_STRCMP(s, option) == 0) {
FLUID_FREE (option);
setting->options = fluid_list_remove_link(setting->options, list);
return 1;
}
list = fluid_list_next(list);
}
return 0;
} else {
return 0;
}
}
int fluid_settings_setnum(fluid_settings_t* settings, const char* name, double val)
{
int type;
void* value;
fluid_num_setting_t* setting;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
if (type != FLUID_NUM_TYPE) {
return 0;
}
setting = (fluid_num_setting_t*) value;
if (val < setting->min) {
val = setting->min;
} else if (val > setting->max) {
val = setting->max;
}
setting->value = val;
if (setting->update) {
(*setting->update)(setting->data, name, val);
}
return 1;
} else {
/* insert a new setting */
fluid_num_setting_t* setting;
setting = new_fluid_num_setting(-1e10, 1e10, 0.0f, 0, NULL, NULL);
setting->value = val;
return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE);
}
}
int fluid_settings_getnum(fluid_settings_t* settings, const char* name, double* val)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_NUM_TYPE)) {
fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
*val = setting->value;
return 1;
}
return 0;
}
void fluid_settings_getnum_range(fluid_settings_t* settings, const char* name, double* min, double* max)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_NUM_TYPE)) {
fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
*min = setting->min;
*max = setting->max;
}
}
double
fluid_settings_getnum_default(fluid_settings_t* settings, const char* name)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_NUM_TYPE)) {
fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
return setting->def;
} else {
return 0.0f;
}
}
int fluid_settings_setint(fluid_settings_t* settings, const char* name, int val)
{
int type;
void* value;
fluid_int_setting_t* setting;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
if (type != FLUID_INT_TYPE) {
return 0;
}
setting = (fluid_int_setting_t*) value;
if (val < setting->min) {
val = setting->min;
} else if (val > setting->max) {
val = setting->max;
}
setting->value = val;
if (setting->update) {
(*setting->update)(setting->data, name, val);
}
return 1;
} else {
/* insert a new setting */
fluid_int_setting_t* setting;
setting = new_fluid_int_setting(INT_MIN, INT_MAX, 0, 0, NULL, NULL);
setting->value = val;
return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE);
}
}
int fluid_settings_getint(fluid_settings_t* settings, const char* name, int* val)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_INT_TYPE)) {
fluid_int_setting_t* setting = (fluid_int_setting_t*) value;
*val = setting->value;
return 1;
}
return 0;
}
void fluid_settings_getint_range(fluid_settings_t* settings, const char* name, int* min, int* max)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_INT_TYPE)) {
fluid_int_setting_t* setting = (fluid_int_setting_t*) value;
*min = setting->min;
*max = setting->max;
}
}
int
fluid_settings_getint_default(fluid_settings_t* settings, const char* name)
{
int type;
void* value;
char* tokens[MAX_SETTINGS_TOKENS];
char buf[MAX_SETTINGS_LABEL+1];
int ntokens;
ntokens = fluid_settings_tokenize(name, buf, tokens);
if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
&& (type == FLUID_INT_TYPE)) {
fluid_int_setting_t* setting = (fluid_int_setting_t*) value;
return setting->def;
} else {
return 0.0f;
}
}

View file

@ -0,0 +1,55 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_SETTINGS_H
#define _FLUID_SETTINGS_H
/** returns 1 if the option was added, 0 otherwise */
int fluid_settings_add_option(fluid_settings_t* settings, const char* name, char* s);
/** returns 1 if the option was added, 0 otherwise */
int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, char* s);
typedef int (*fluid_num_update_t)(void* data, const char* name, double value);
typedef int (*fluid_str_update_t)(void* data, const char* name, char* value);
typedef int (*fluid_int_update_t)(void* data, const char* name, int value);
/** returns 0 if the value has been resgister correctly, non-zero
otherwise */
int fluid_settings_register_str(fluid_settings_t* settings, const char* name, char* def, int hints,
fluid_str_update_t fun, void* data);
/** returns 0 if the value has been resgister correctly, non-zero
otherwise */
int fluid_settings_register_num(fluid_settings_t* settings, const char* name, double min, double max,
double def, int hints, fluid_num_update_t fun, void* data);
/** returns 0 if the value has been resgister correctly, non-zero
otherwise */
int fluid_settings_register_int(fluid_settings_t* settings, const char* name, int min, int max,
int def, int hints, fluid_int_update_t fun, void* data);
#endif /* _FLUID_SETTINGS_H */

View file

@ -0,0 +1,68 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _PRIV_FLUID_SFONT_H
#define _PRIV_FLUID_SFONT_H
/*
* Utility macros to access soundfonts, presets, and samples
*/
#define fluid_sfloader_delete(_loader) { if ((_loader) && (_loader)->free) (*(_loader)->free)(_loader); }
#define fluid_sfloader_load(_loader, _filename) (*(_loader)->load)(_loader, _filename)
#define delete_fluid_sfont(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0)
#define fluid_sfont_get_name(_sf) (*(_sf)->get_name)(_sf)
#define fluid_sfont_get_preset(_sf,_bank,_prenum) (*(_sf)->get_preset)(_sf,_bank,_prenum)
#define fluid_sfont_iteration_start(_sf) (*(_sf)->iteration_start)(_sf)
#define fluid_sfont_iteration_next(_sf,_pr) (*(_sf)->iteration_next)(_sf,_pr)
#define fluid_sfont_get_data(_sf) (_sf)->data
#define fluid_sfont_set_data(_sf,_p) { (_sf)->data = (void*) (_p); }
#define delete_fluid_preset(_preset) \
{ if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }}
#define fluid_preset_get_data(_preset) (_preset)->data
#define fluid_preset_set_data(_preset,_p) { (_preset)->data = (void*) (_p); }
#define fluid_preset_get_name(_preset) (*(_preset)->get_name)(_preset)
#define fluid_preset_get_banknum(_preset) (*(_preset)->get_banknum)(_preset)
#define fluid_preset_get_num(_preset) (*(_preset)->get_num)(_preset)
#define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \
(*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel)
#define fluid_preset_notify(_preset,_reason,_chan) \
{ if ((_preset) && (_preset)->notify) { (*(_preset)->notify)(_preset,_reason,_chan); }}
#define fluid_sample_incr_ref(_sample) { (_sample)->refcount++; }
#define fluid_sample_decr_ref(_sample) \
(_sample)->refcount--; \
if (((_sample)->refcount == 0) && ((_sample)->notify)) \
(*(_sample)->notify)(_sample, FLUID_SAMPLE_DONE);
#endif /* _PRIV_FLUID_SFONT_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,204 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_SYNTH_H
#define _FLUID_SYNTH_H
/***************************************************************
*
* INCLUDES
*/
#include "fluid_config.h"
#include "fluidsynth_priv.h"
#include "fluid_list.h"
#include "fluid_rev.h"
#include "fluid_voice.h"
#include "fluid_chorus.h"
#include "fluid_sys.h"
/***************************************************************
*
* DEFINES
*/
#define FLUID_NUM_PROGRAMS 128
#define DRUM_INST_BANK 128
#if defined(WITH_FLOAT)
#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_FLOAT
#else
#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_DOUBLE
#endif
/***************************************************************
*
* ENUM
*/
enum fluid_loop {
FLUID_UNLOOPED = 0,
FLUID_LOOP_DURING_RELEASE = 1,
FLUID_NOTUSED = 2,
FLUID_LOOP_UNTIL_RELEASE = 3
};
enum fluid_synth_status
{
FLUID_SYNTH_CLEAN,
FLUID_SYNTH_PLAYING,
FLUID_SYNTH_QUIET,
FLUID_SYNTH_STOPPED
};
typedef struct _fluid_bank_offset_t fluid_bank_offset_t;
struct _fluid_bank_offset_t {
int sfont_id;
int offset;
};
/*
* fluid_synth_t
*/
struct _fluid_synth_t
{
/* fluid_settings_old_t settings_old; the old synthesizer settings */
fluid_settings_t* settings; /** the synthesizer settings */
int polyphony; /** maximum polyphony */
char with_reverb; /** Should the synth use the built-in reverb unit? */
char with_chorus; /** Should the synth use the built-in chorus unit? */
char verbose; /** Turn verbose mode on? */
char dump; /** Dump events to stdout to hook up a user interface? */
double sample_rate; /** The sample rate */
int midi_channels; /** the number of MIDI channels (>= 16) */
int audio_channels; /** the number of audio channels (1 channel=left+right) */
int audio_groups; /** the number of (stereo) 'sub'groups from the synth.
Typically equal to audio_channels. */
int effects_channels; /** the number of effects channels (= 2) */
unsigned int state; /** the synthesizer state */
unsigned int ticks; /** the number of audio samples since the start */
fluid_list_t *loaders; /** the soundfont loaders */
fluid_list_t* sfont; /** the loaded soundfont */
unsigned int sfont_id;
fluid_list_t* bank_offsets; /** the offsets of the soundfont banks */
#if defined(MACOS9)
fluid_list_t* unloading; /** the soundfonts that need to be unloaded */
#endif
double gain; /** master gain */
fluid_channel_t** channel; /** the channels */
int num_channels; /** the number of channels */
int nvoice; /** the length of the synthesis process array */
fluid_voice_t** voice; /** the synthesis processes */
unsigned int noteid; /** the id is incremented for every new note. it's used for noteoff's */
unsigned int storeid;
int nbuf; /** How many audio buffers are used? (depends on nr of audio channels / groups)*/
fluid_real_t** left_buf;
fluid_real_t** right_buf;
fluid_real_t** fx_left_buf;
fluid_real_t** fx_right_buf;
fluid_revmodel_t* reverb;
fluid_chorus_t* chorus;
int cur; /** the current sample in the audio buffers to be output */
int dither_index; /* current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */
char outbuf[256]; /** buffer for message output */
fluid_tuning_t*** tuning; /** 128 banks of 128 programs for the tunings */
fluid_tuning_t* cur_tuning; /** current tuning in the iteration */
unsigned int min_note_length_ticks; /**< If note-offs are triggered just after a note-on, they will be delayed */
};
/** returns 1 if the value has been set, 0 otherwise */
int fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str);
/** returns 1 if the value exists, 0 otherwise */
int fluid_synth_getstr(fluid_synth_t* synth, char* name, char** str);
/** returns 1 if the value has been set, 0 otherwise */
int fluid_synth_setnum(fluid_synth_t* synth, char* name, double val);
/** returns 1 if the value exists, 0 otherwise */
int fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val);
/** returns 1 if the value has been set, 0 otherwise */
int fluid_synth_setint(fluid_synth_t* synth, char* name, int val);
/** returns 1 if the value exists, 0 otherwise */
int fluid_synth_getint(fluid_synth_t* synth, char* name, int* val);
int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num);
int fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out);
fluid_preset_t* fluid_synth_get_preset(fluid_synth_t* synth,
unsigned int sfontnum,
unsigned int banknum,
unsigned int prognum);
fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth,
unsigned int banknum,
unsigned int prognum);
//int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan);
//int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan);
int fluid_synth_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl);
int fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan);
int fluid_synth_damp_voices(fluid_synth_t* synth, int chan);
int fluid_synth_kill_voice(fluid_synth_t* synth, fluid_voice_t * voice);
void fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, fluid_voice_t* voice);
void fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key);
void fluid_synth_sfunload_macos9(fluid_synth_t* synth);
void fluid_synth_print_voice(fluid_synth_t* synth);
/** This function assures that every MIDI channels has a valid preset
* (NULL is okay). This function is called after a SoundFont is
* unloaded or reloaded. */
void fluid_synth_update_presets(fluid_synth_t* synth);
int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value);
int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value);
fluid_bank_offset_t* fluid_synth_get_bank_offset0(fluid_synth_t* synth, int sfont_id);
void fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id);
void fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin,
void* lout, int loff, int lincr,
void* rout, int roff, int rincr);
/*
* misc
*/
void fluid_synth_settings(fluid_settings_t* settings);
#endif /* _FLUID_SYNTH_H */

View file

@ -0,0 +1,364 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_sys.h"
static char fluid_errbuf[512]; /* buffer for error message */
static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL];
static void* fluid_log_user_data[LAST_LOG_LEVEL];
static int fluid_log_initialized = 0;
static char* fluid_libname = "fluidsynth";
void fluid_sys_config()
{
fluid_log_config();
}
unsigned int fluid_debug_flags = 0;
#if DEBUG
/*
* fluid_debug
*/
int fluid_debug(int level, char * fmt, ...)
{
if (fluid_debug_flags & level) {
fluid_log_function_t fun;
va_list args;
va_start (args, fmt);
vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
va_end (args);
fun = fluid_log_function[FLUID_DBG];
if (fun != NULL) {
(*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]);
}
}
return 0;
}
#endif
/**
* Installs a new log function for a specified log level.
* @param level Log level to install handler for.
* @param fun Callback function handler to call for logged messages
* @param data User supplied data pointer to pass to log function
* @return The previously installed function.
*/
fluid_log_function_t
fluid_set_log_function(int level, fluid_log_function_t fun, void* data)
{
fluid_log_function_t old = NULL;
if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
old = fluid_log_function[level];
fluid_log_function[level] = fun;
fluid_log_user_data[level] = data;
}
return old;
}
/**
* Default log function which prints to the stderr.
* @param level Log level
* @param message Log message
* @param data User supplied data (not used)
*/
void
fluid_default_log_function(int level, char* message, void* data)
{
FILE* out;
#if defined(WIN32)
out = stdout;
#else
out = stderr;
#endif
if (fluid_log_initialized == 0) {
fluid_log_config();
}
switch (level) {
case FLUID_PANIC:
FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message);
break;
case FLUID_ERR:
FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message);
break;
case FLUID_WARN:
FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message);
break;
case FLUID_INFO:
FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
break;
case FLUID_DBG:
#if DEBUG
FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message);
#endif
break;
default:
FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
break;
}
fflush(out);
}
/*
* fluid_init_log
*/
void
fluid_log_config(void)
{
if (fluid_log_initialized == 0) {
fluid_log_initialized = 1;
if (fluid_log_function[FLUID_PANIC] == NULL) {
fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL);
}
if (fluid_log_function[FLUID_ERR] == NULL) {
fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL);
}
if (fluid_log_function[FLUID_WARN] == NULL) {
fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL);
}
if (fluid_log_function[FLUID_INFO] == NULL) {
fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL);
}
if (fluid_log_function[FLUID_DBG] == NULL) {
fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL);
}
}
}
/**
* Print a message to the log.
* @param level Log level (#fluid_log_level).
* @param fmt Printf style format string for log message
* @param ... Arguments for printf 'fmt' message string
* @return Always returns -1
*/
int
fluid_log(int level, char* fmt, ...)
{
fluid_log_function_t fun = NULL;
va_list args;
va_start (args, fmt);
vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
va_end (args);
if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
fun = fluid_log_function[level];
if (fun != NULL) {
(*fun)(level, fluid_errbuf, fluid_log_user_data[level]);
}
}
return FLUID_FAILED;
}
/**
* An improved strtok, still trashes the input string, but is portable and
* thread safe. Also skips token chars at beginning of token string and never
* returns an empty token (will return NULL if source ends in token chars though).
* NOTE: NOT part of public API
* @internal
* @param str Pointer to a string pointer of source to tokenize. Pointer gets
* updated on each invocation to point to beginning of next token. Note that
* token char get's overwritten with a 0 byte. String pointer is set to NULL
* when final token is returned.
* @param delim String of delimiter chars.
* @return Pointer to the next token or NULL if no more tokens.
*/
char *fluid_strtok (char **str, char *delim)
{
char *s, *d, *token;
char c;
if (str == NULL || delim == NULL || !*delim)
{
FLUID_LOG(FLUID_ERR, "Null pointer");
return NULL;
}
s = *str;
if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */
/* skip delimiter chars at beginning of token */
do
{
c = *s;
if (!c) /* end of source string? */
{
*str = NULL;
return NULL;
}
for (d = delim; *d; d++) /* is source char a token char? */
{
if (c == *d) /* token char match? */
{
s++; /* advance to next source char */
break;
}
}
} while (*d); /* while token char match */
token = s; /* start of token found */
/* search for next token char or end of source string */
for (s = s+1; *s; s++)
{
c = *s;
for (d = delim; *d; d++) /* is source char a token char? */
{
if (c == *d) /* token char match? */
{
*s = '\0'; /* overwrite token char with zero byte to terminate token */
*str = s+1; /* update str to point to beginning of next token */
return token;
}
}
}
/* we get here only if source string ended */
*str = NULL;
return token;
}
/*
* fluid_error
*/
char*
fluid_error()
{
return fluid_errbuf;
}
/*
*
* fluid_is_midifile
*/
int
fluid_is_midifile(char* filename)
{
FILE* fp = fopen(filename, "rb");
char id[4];
if (fp == NULL) {
return 0;
}
if (fread((void*) id, 1, 4, fp) != 4) {
fclose(fp);
return 0;
}
fclose(fp);
return strncmp(id, "MThd", 4) == 0;
}
/*
* fluid_is_soundfont
*
*/
int
fluid_is_soundfont(char* filename)
{
FILE* fp = fopen(filename, "rb");
char id[4];
if (fp == NULL) {
return 0;
}
if (fread((void*) id, 1, 4, fp) != 4) {
fclose(fp);
return 0;
}
fclose(fp);
return strncmp(id, "RIFF", 4) == 0;
}
/*=============================================================*/
/* */
/* Win32 */
/* */
/*=============================================================*/
/***************************************************************
*
* Timer
*
*/
//timer disabled
/***************************************************************
*
* Floating point exceptions
*
* The floating point exception functions were taken from Ircam's
* jMax source code. http://www.ircam.fr/jmax
*
* FIXME: check in config for i386 machine
*
* Currently not used. I leave the code here in case we want to pick
* this up again some time later.
*/
/***************************************************************
*
* Profiling (Linux, i586 only)
*
*/
/***************************************************************
*
* Threads
*
*/
//thread disabled
/***************************************************************
*
* Sockets
*
*/
//socket disabled

View file

@ -0,0 +1,141 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
/**
This header contains a bunch of (mostly) system and machine
dependent functions:
- timers
- current time in milliseconds and microseconds
- debug logging
- profiling
- memory locking
- checking for floating point exceptions
*/
#ifndef _FLUID_SYS_H
#define _FLUID_SYS_H
#include "fluidsynth_priv.h"
void fluid_sys_config(void);
void fluid_log_config(void);
/*
* Utility functions
*/
char *fluid_strtok (char **str, char *delim);
/**
Additional debugging system, separate from the log system. This
allows to print selected debug messages of a specific subsystem.
*/
extern unsigned int fluid_debug_flags;
#if DEBUG
enum fluid_debug_level {
FLUID_DBG_DRIVER = 1
};
int fluid_debug(int level, char * fmt, ...);
#else
#define fluid_debug
#endif
//timer disabled
/**
Muteces
*/
/**
Threads
*/
/**
Sockets
*/
/**
Profiling
*/
/**
Profile numbers. List all the pieces of code you want to profile
here. Be sure to add an entry in the fluid_profile_data table in
fluid_sys.c
*/
enum {
FLUID_PROF_WRITE_S16,
FLUID_PROF_ONE_BLOCK,
FLUID_PROF_ONE_BLOCK_CLEAR,
FLUID_PROF_ONE_BLOCK_VOICE,
FLUID_PROF_ONE_BLOCK_VOICES,
FLUID_PROF_ONE_BLOCK_REVERB,
FLUID_PROF_ONE_BLOCK_CHORUS,
FLUID_PROF_VOICE_NOTE,
FLUID_PROF_VOICE_RELEASE,
FLUID_PROF_LAST
};
/* Profiling */
/**
Memory locking
Memory locking is used to avoid swapping of the large block of
sample data.
*/
/**
Floating point exceptions
fluid_check_fpe() checks for "unnormalized numbers" and other
exceptions of the floating point processsor.
*/
#endif /* _FLUID_SYS_H */

View file

@ -0,0 +1,144 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_tuning.h"
#include "fluidsynth_priv.h"
fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog)
{
fluid_tuning_t* tuning;
int i;
tuning = FLUID_NEW(fluid_tuning_t);
if (tuning == NULL) {
FLUID_LOG(FLUID_PANIC, "Out of memory");
return NULL;
}
tuning->name = NULL;
if (name != NULL) {
tuning->name = FLUID_STRDUP(name);
}
tuning->bank = bank;
tuning->prog = prog;
for (i = 0; i < 128; i++) {
tuning->pitch[i] = i * 100.0;
}
return tuning;
}
/* Duplicate a tuning */
fluid_tuning_t *
fluid_tuning_duplicate (fluid_tuning_t *tuning)
{
fluid_tuning_t *new_tuning;
int i;
new_tuning = FLUID_NEW (fluid_tuning_t);
if (!new_tuning) {
FLUID_LOG (FLUID_PANIC, "Out of memory");
return NULL;
}
if (tuning->name)
{
new_tuning->name = FLUID_STRDUP (tuning->name);
if (!new_tuning->name)
{
FLUID_FREE (new_tuning);
FLUID_LOG (FLUID_PANIC, "Out of memory");
return NULL;
}
}
else new_tuning->name = NULL;
new_tuning->bank = tuning->bank;
new_tuning->prog = tuning->prog;
for (i = 0; i < 128; i++)
new_tuning->pitch[i] = tuning->pitch[i];
return new_tuning;
}
void delete_fluid_tuning(fluid_tuning_t* tuning)
{
if (tuning == NULL) {
return;
}
if (tuning->name != NULL) {
FLUID_FREE(tuning->name);
}
FLUID_FREE(tuning);
}
void fluid_tuning_set_name(fluid_tuning_t* tuning, const char* name)
{
if (tuning->name != NULL) {
FLUID_FREE(tuning->name);
tuning->name = NULL;
}
if (name != NULL) {
tuning->name = FLUID_STRDUP(name);
}
}
char* fluid_tuning_get_name(fluid_tuning_t* tuning)
{
return tuning->name;
}
void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch)
{
tuning->pitch[key] = pitch;
}
void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv)
{
int i;
for (i = 0; i < 128; i++) {
tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12];
}
}
void fluid_tuning_set_all(fluid_tuning_t* tuning, double* pitch)
{
int i;
for (i = 0; i < 128; i++) {
tuning->pitch[i] = pitch[i];
}
}
void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch)
{
if ((key >= 0) && (key < 128)) {
tuning->pitch[key] = pitch;
}
}

View file

@ -0,0 +1,65 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
/*
More information about micro tuning can be found at:
http://www.midi.org/about-midi/tuning.htm
http://www.midi.org/about-midi/tuning-scale.htm
http://www.midi.org/about-midi/tuning_extens.htm
*/
#ifndef _FLUID_TUNING_H
#define _FLUID_TUNING_H
#include "fluidsynth_priv.h"
struct _fluid_tuning_t {
char* name;
int bank;
int prog;
double pitch[128]; /* the pitch of every key, in cents */
};
fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog);
fluid_tuning_t* fluid_tuning_duplicate(fluid_tuning_t *tuning);
void delete_fluid_tuning(fluid_tuning_t* tuning);
void fluid_tuning_set_name(fluid_tuning_t* tuning, const char* name);
char* fluid_tuning_get_name(fluid_tuning_t* tuning);
#define fluid_tuning_get_bank(_t) ((_t)->bank)
#define fluid_tuning_get_prog(_t) ((_t)->prog)
void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch);
#define fluid_tuning_get_pitch(_t, _key) ((_t)->pitch[_key])
void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv);
void fluid_tuning_set_all(fluid_tuning_t* tuning, double* pitch);
#define fluid_tuning_get_all(_t) (&(_t)->pitch[0])
#endif /* _FLUID_TUNING_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,291 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_VOICE_H
#define _FLUID_VOICE_H
#include "fluid_phase.h"
#include "fluid_gen.h"
#include "fluid_mod.h"
#define NO_CHANNEL 0xff
enum fluid_voice_status
{
FLUID_VOICE_CLEAN,
FLUID_VOICE_ON,
FLUID_VOICE_SUSTAINED,
FLUID_VOICE_OFF
};
/*
* envelope data
*/
struct _fluid_env_data_t {
unsigned int count;
fluid_real_t coeff;
fluid_real_t incr;
fluid_real_t min;
fluid_real_t max;
};
/* Indices for envelope tables */
enum fluid_voice_envelope_index_t{
FLUID_VOICE_ENVDELAY,
FLUID_VOICE_ENVATTACK,
FLUID_VOICE_ENVHOLD,
FLUID_VOICE_ENVDECAY,
FLUID_VOICE_ENVSUSTAIN,
FLUID_VOICE_ENVRELEASE,
FLUID_VOICE_ENVFINISHED,
FLUID_VOICE_ENVLAST
};
/*
* fluid_voice_t
*/
struct _fluid_voice_t
{
unsigned int id; /* the id is incremented for every new noteon.
it's used for noteoff's */
unsigned char status;
unsigned char chan; /* the channel number, quick access for channel messages */
unsigned char key; /* the key, quick acces for noteoff */
unsigned char vel; /* the velocity */
fluid_channel_t* channel;
fluid_gen_t gen[GEN_LAST];
fluid_mod_t mod[FLUID_NUM_MOD];
int mod_count;
int has_looped; /* Flag that is set as soon as the first loop is completed. */
fluid_sample_t* sample;
int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters
have to be checked. */
#if 0
/* Instead of keeping a pointer to a fluid_sample_t structure,
* I think it would be better to copy the sample data in the
* voice structure. SoundFont loader then do not have to
* allocate and maintain the fluid_sample_t structure. [PH]
*
* The notify callback may be used also for streaming samples.
*/
short* sample_data; /* pointer to the sample data */
int sample_data_offset; /* the offset of data[0] in the whole sample */
int sample_data_length; /* the length of the data array */
unsigned int sample_start;
unsigned int sample_end;
unsigned int sample_loopstart;
unsigned int sample_loopend;
unsigned int sample_rate;
int sample_origpitch;
int sample_pitchadj;
int sample_type;
int (*sample_notify)(fluid_voice_t* voice, int reason);
void* sample_userdata;
#endif
/* basic parameters */
fluid_real_t output_rate; /* the sample rate of the synthesizer */
unsigned int start_time;
unsigned int ticks;
unsigned int noteoff_ticks; /* Delay note-off until this tick */
fluid_real_t amp; /* current linear amplitude */
fluid_phase_t phase; /* the phase of the sample wave */
/* Temporary variables used in fluid_voice_write() */
fluid_real_t phase_incr; /* the phase increment for the next 64 samples */
fluid_real_t amp_incr; /* amplitude increment value */
fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */
/* End temporary variables */
/* basic parameters */
fluid_real_t pitch; /* the pitch in midicents */
fluid_real_t attenuation; /* the attenuation in centibels */
fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation
* during the lifetime of the voice */
fluid_real_t root_pitch;
/* sample and loop start and end points (offset in sample memory). */
int start;
int end;
int loopstart;
int loopend; /* Note: first point following the loop (superimposed on loopstart) */
/* master gain */
fluid_real_t synth_gain;
/* vol env */
fluid_env_data_t volenv_data[FLUID_VOICE_ENVLAST];
unsigned int volenv_count;
int volenv_section;
fluid_real_t volenv_val;
fluid_real_t amplitude_that_reaches_noise_floor_nonloop;
fluid_real_t amplitude_that_reaches_noise_floor_loop;
/* mod env */
fluid_env_data_t modenv_data[FLUID_VOICE_ENVLAST];
unsigned int modenv_count;
int modenv_section;
fluid_real_t modenv_val; /* the value of the modulation envelope */
fluid_real_t modenv_to_fc;
fluid_real_t modenv_to_pitch;
/* mod lfo */
fluid_real_t modlfo_val; /* the value of the modulation LFO */
unsigned int modlfo_delay; /* the delay of the lfo in samples */
fluid_real_t modlfo_incr; /* the lfo frequency is converted to a per-buffer increment */
fluid_real_t modlfo_to_fc;
fluid_real_t modlfo_to_pitch;
fluid_real_t modlfo_to_vol;
/* vib lfo */
fluid_real_t viblfo_val; /* the value of the vibrato LFO */
unsigned int viblfo_delay; /* the delay of the lfo in samples */
fluid_real_t viblfo_incr; /* the lfo frequency is converted to a per-buffer increment */
fluid_real_t viblfo_to_pitch;
/* resonant filter */
fluid_real_t fres; /* the resonance frequency, in cents (not absolute cents) */
fluid_real_t last_fres; /* Current resonance frequency of the IIR filter */
/* Serves as a flag: A deviation between fres and last_fres */
/* indicates, that the filter has to be recalculated. */
fluid_real_t q_lin; /* the q-factor on a linear scale */
fluid_real_t filter_gain; /* Gain correction factor, depends on q */
fluid_real_t hist1, hist2; /* Sample history for the IIR filter */
int filter_startup; /* Flag: If set, the filter will be set directly.
Else it changes smoothly. */
/* filter coefficients */
/* The coefficients are normalized to a0. */
/* b0 and b2 are identical => b02 */
fluid_real_t b02; /* b0 / a0 */
fluid_real_t b1; /* b1 / a0 */
fluid_real_t a1; /* a0 / a0 */
fluid_real_t a2; /* a1 / a0 */
fluid_real_t b02_incr;
fluid_real_t b1_incr;
fluid_real_t a1_incr;
fluid_real_t a2_incr;
int filter_coeff_incr_count;
/* pan */
fluid_real_t pan;
fluid_real_t amp_left;
fluid_real_t amp_right;
/* reverb */
fluid_real_t reverb_send;
fluid_real_t amp_reverb;
/* chorus */
fluid_real_t chorus_send;
fluid_real_t amp_chorus;
/* interpolation method, as in fluid_interp in fluidlite.h */
int interp_method;
/* for debugging */
int debug;
};
fluid_voice_t* new_fluid_voice(fluid_real_t output_rate);
int delete_fluid_voice(fluid_voice_t* voice);
void fluid_voice_start(fluid_voice_t* voice);
int fluid_voice_write(fluid_voice_t* voice,
fluid_real_t* left, fluid_real_t* right,
fluid_real_t* reverb_buf, fluid_real_t* chorus_buf);
int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
fluid_channel_t* channel, int key, int vel,
unsigned int id, unsigned int time, fluid_real_t gain);
int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl);
int fluid_voice_modulate_all(fluid_voice_t* voice);
/** Set the NRPN value of a generator. */
int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t value, int abs);
/** Set the gain. */
int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain);
/** Update all the synthesis parameters, which depend on generator
'gen'. This is only necessary after changing a generator of an
already operating voice. Most applications will not need this
function.*/
void fluid_voice_update_param(fluid_voice_t* voice, int gen);
int fluid_voice_noteoff(fluid_voice_t* voice);
int fluid_voice_off(fluid_voice_t* voice);
int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice);
fluid_channel_t* fluid_voice_get_channel(fluid_voice_t* voice);
int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
int gen_key2base, int is_decay);
int fluid_voice_kill_excl(fluid_voice_t* voice);
fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice);
fluid_real_t fluid_voice_determine_amplitude_that_reaches_noise_floor_for_sample(fluid_voice_t* voice);
void fluid_voice_check_sample_sanity(fluid_voice_t* voice);
#define fluid_voice_set_id(_voice, _id) { (_voice)->id = (_id); }
#define fluid_voice_get_chan(_voice) (_voice)->chan
#define _PLAYING(voice) (((voice)->status == FLUID_VOICE_ON) || ((voice)->status == FLUID_VOICE_SUSTAINED))
/* A voice is 'ON', if it has not yet received a noteoff
* event. Sending a noteoff event will advance the envelopes to
* section 5 (release). */
#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && (voice)->volenv_section < FLUID_VOICE_ENVRELEASE)
#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED)
#define _AVAILABLE(voice) (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF))
#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL)
#define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val)
fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num);
#define _GEN(_voice, _n) \
((fluid_real_t)(_voice)->gen[_n].val \
+ (fluid_real_t)(_voice)->gen[_n].mod \
+ (fluid_real_t)(_voice)->gen[_n].nrpn)
#define FLUID_SAMPLESANITY_CHECK (1 << 0)
#define FLUID_SAMPLESANITY_STARTUP (1 << 1)
/* defined in fluid_dsp_float.c */
void fluid_dsp_float_config (void);
int fluid_dsp_float_interpolate_none (fluid_voice_t *voice);
int fluid_dsp_float_interpolate_linear (fluid_voice_t *voice);
int fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice);
int fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice);
#endif /* _FLUID_VOICE_H */

View file

@ -0,0 +1,710 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include "fluid_config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "fluidsynth_priv.h"
#if !defined(WIN32) && !defined(MACINTOSH)
#define _GNU_SOURCE
#include <getopt.h>
#endif
#if defined(WIN32)
#include <windows.h>
#endif
#include "fluidsynth.h"
#if defined(WIN32) && !defined(MINGW32)
#include "config_win32.h"
#endif
#ifdef HAVE_SIGNAL_H
#include "signal.h"
#endif
#include "fluid_lash.h"
#ifndef WITH_MIDI
#define WITH_MIDI 1
#endif
/* default audio fragment count (if none specified) */
#ifdef WIN32
#define DEFAULT_FRAG_COUNT 32
#else
#define DEFAULT_FRAG_COUNT 16
#endif
void print_usage(void);
void print_help(void);
void print_welcome(void);
static fluid_cmd_handler_t* newclient(void* data, char* addr);
/*
* the globals
*/
fluid_cmd_handler_t* cmd_handler = NULL;
int option_help = 0; /* set to 1 if "-o help" is specified */
/*
* support for the getopt function
*/
#if !defined(WIN32) && !defined(MACINTOSH)
#define GETOPT_SUPPORT 1
int getopt(int argc, char * const argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
#endif
/* process_o_cmd_line_option
*
* Purpose:
* Process a command line option -o setting=value,
* for example: -o synth.polyhony=16
*/
void process_o_cmd_line_option(fluid_settings_t* settings, char* optarg){
char* val;
for (val = optarg; *val != '\0'; val++) {
if (*val == '=') {
*val++ = 0;
break;
}
}
/* did user request list of settings */
if (strcmp (optarg, "help") == 0)
{
option_help = 1;
return;
}
/* At this point:
* optarg => "synth.polyphony"
* val => "16"
*/
switch(fluid_settings_get_type(settings, optarg)){
case FLUID_NUM_TYPE:
if (fluid_settings_setnum(settings, optarg, atof(val))){
break;
};
case FLUID_INT_TYPE:
if (fluid_settings_setint(settings, optarg, atoi(val))){
break;
};
case FLUID_STR_TYPE:
if (fluid_settings_setstr(settings, optarg, val)){
break;
};
default:
fprintf (stderr, "Settings argument on command line: Failed to set \"%s\" to \"%s\".\n"
"Most likely the parameter \"%s\" does not exist.\n", optarg, val, optarg);
}
}
static void
print_pretty_int (int i)
{
if (i == INT_MAX) printf ("MAXINT");
else if (i == INT_MIN) printf ("MININT");
else printf ("%d", i);
}
/* fluid_settings_foreach function for displaying option help "-o help" */
static void
settings_foreach_func (void *data, char *name, int type)
{
fluid_settings_t *settings = (fluid_settings_t *)data;
double dmin, dmax, ddef;
int imin, imax, idef;
char *defstr;
switch (type)
{
case FLUID_NUM_TYPE:
fluid_settings_getnum_range (settings, name, &dmin, &dmax);
ddef = fluid_settings_getnum_default (settings, name);
printf ("%-24s FLOAT [min=%0.3f, max=%0.3f, def=%0.3f]\n",
name, dmin, dmax, ddef);
break;
case FLUID_INT_TYPE:
fluid_settings_getint_range (settings, name, &imin, &imax);
idef = fluid_settings_getint_default (settings, name);
printf ("%-24s INT [min=", name);
print_pretty_int (imin);
printf (", max=");
print_pretty_int (imax);
printf (", def=");
print_pretty_int (idef);
printf ("]\n");
break;
case FLUID_STR_TYPE:
defstr = fluid_settings_getstr_default (settings, name);
printf ("%-24s STR", name);
if (defstr) printf (" [def='%s']\n", defstr);
else printf ("\n");
break;
case FLUID_SET_TYPE:
printf ("%-24s SET\n", name);
break;
}
}
#ifdef HAVE_SIGNAL_H
/*
* handle_signal
*/
void handle_signal(int sig_num)
{
}
#endif
/*
* main
*/
int main(int argc, char** argv)
{
fluid_settings_t* settings;
int arg1 = 1;
char buf[512];
int c, i, fragcount = DEFAULT_FRAG_COUNT;
int interactive = 1;
int midi_in = 1;
fluid_player_t* player = NULL;
fluid_midi_router_t* router = NULL;
fluid_midi_driver_t* mdriver = NULL;
fluid_audio_driver_t* adriver = NULL;
fluid_synth_t* synth = NULL;
fluid_server_t* server = NULL;
char* midi_id = NULL;
char* midi_driver = NULL;
char* midi_device = NULL;
char* config_file = NULL;
int audio_groups = 0;
int audio_channels = 0;
int with_server = 0;
int dump = 0;
int connect_lash = 1;
char *optchars = "a:C:c:df:G:g:hijK:L:lm:no:p:R:r:sVvz:";
#ifdef LASH_ENABLED
int enabled_lash = 0; /* set to TRUE if lash gets enabled */
fluid_lash_args_t *lash_args;
lash_args = fluid_lash_extract_args (&argc, &argv);
#endif
settings = new_fluid_settings();
#ifdef GETOPT_SUPPORT /* pre section of GETOPT supported argument handling */
opterr = 0;
while (1) {
int option_index = 0;
static struct option long_options[] = {
{"audio-bufcount", 1, 0, 'c'},
{"audio-bufsize", 1, 0, 'z'},
{"audio-channels", 1, 0, 'L'},
{"audio-driver", 1, 0, 'a'},
{"audio-groups", 1, 0, 'G'},
{"chorus", 1, 0, 'C'},
{"connect-jack-outputs", 0, 0, 'j'},
{"disable-lash", 0, 0, 'l'},
{"dump", 0, 0, 'd'},
{"gain", 1, 0, 'g'},
{"help", 0, 0, 'h'},
{"load-config", 1, 0, 'f'},
{"midi-channels", 1, 0, 'K'},
{"midi-driver", 1, 0, 'm'},
{"no-midi-in", 0, 0, 'n'},
{"no-shell", 0, 0, 'i'},
{"option", 1, 0, 'o'},
{"portname", 1, 0, 'p'},
{"reverb", 1, 0, 'R'},
{"sample-rate", 1, 0, 'r'},
{"server", 0, 0, 's'},
{"verbose", 0, 0, 'v'},
{"version", 0, 0, 'V'},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, optchars, long_options, &option_index);
if (c == -1) {
break;
}
#else /* "pre" section to non getopt argument handling */
for (i = 1; i < argc; i++) {
char *optarg;
/* Skip non switch arguments (assume they are file names) */
if ((argv[i][0] != '-') || (argv[i][1] == '\0')) break;
c = argv[i][1];
optarg = strchr (optchars, c); /* find the option character in optchars */
if (optarg && optarg[1] == ':') /* colon follows if switch argument expected */
{
if (++i >= argc)
{
printf ("Option -%c requires an argument\n", c);
print_usage();
exit(0);
}
else
{
optarg = argv[i];
if (optarg[0] == '-')
{
printf ("Expected argument to option -%c found switch instead\n", c);
print_usage();
exit(0);
}
}
}
else optarg = "";
#endif
switch (c) {
#ifdef GETOPT_SUPPORT
case 0: /* shouldn't normally happen, a long option's flag is set to NULL */
printf ("option %s", long_options[option_index].name);
if (optarg) {
printf (" with arg %s", optarg);
}
printf ("\n");
break;
#endif
case 'a':
fluid_settings_setstr(settings, "audio.driver", optarg);
break;
case 'C':
if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) {
fluid_settings_setstr(settings, "synth.chorus.active", "no");
} else {
fluid_settings_setstr(settings, "synth.chorus.active", "yes");
}
break;
case 'c':
fluid_settings_setint(settings, "audio.periods", atoi(optarg));
break;
case 'd':
fluid_settings_setstr(settings, "synth.dump", "yes");
dump = 1;
break;
case 'f':
config_file = optarg;
break;
case 'G':
audio_groups = atoi(optarg);
break;
case 'g':
fluid_settings_setnum(settings, "synth.gain", atof(optarg));
break;
case 'h':
print_help();
break;
case 'i':
interactive = 0;
break;
case 'j':
fluid_settings_setint(settings, "audio.jack.autoconnect", 1);
break;
case 'K':
fluid_settings_setint(settings, "synth.midi-channels", atoi(optarg));
break;
case 'L':
audio_channels = atoi(optarg);
fluid_settings_setint(settings, "synth.audio-channels", audio_channels);
break;
case 'l': /* disable LASH */
connect_lash = 0;
break;
case 'm':
fluid_settings_setstr(settings, "midi.driver", optarg);
break;
case 'n':
midi_in = 0;
break;
case 'o':
process_o_cmd_line_option(settings, optarg);
break;
case 'p' :
fluid_settings_setstr(settings, "midi.portname", optarg);
break;
case 'R':
if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) {
fluid_settings_setstr(settings, "synth.reverb.active", "no");
} else {
fluid_settings_setstr(settings, "synth.reverb.active", "yes");
}
break;
case 'r':
fluid_settings_setnum(settings, "synth.sample-rate", atof(optarg));
break;
case 's':
with_server = 1;
break;
case 'V':
printf("FluidSynth %s\n", VERSION);
exit (0);
break;
case 'v':
fluid_settings_setstr(settings, "synth.verbose", "yes");
break;
case 'z':
fluid_settings_setint(settings, "audio.period-size", atoi(optarg));
break;
#ifdef GETOPT_SUPPORT
case '?':
printf ("Unknown option %c\n", optopt);
print_usage();
exit(0);
break;
default:
printf ("?? getopt returned character code 0%o ??\n", c);
break;
#else /* Non getopt default case */
default:
printf ("Unknown switch '%c'\n", c);
print_usage();
exit(0);
break;
#endif
} /* end of switch statement */
} /* end of loop */
#ifdef GETOPT_SUPPORT
arg1 = optind;
#else
arg1 = i;
#endif
/* option help requested? "-o help" */
if (option_help)
{
print_welcome ();
printf ("FluidSynth settings:\n");
fluid_settings_foreach (settings, settings, settings_foreach_func);
exit (0);
}
#ifdef WIN32
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
#endif
#ifdef LASH_ENABLED
/* connect to the lash server */
if (connect_lash)
{
enabled_lash = fluid_lash_connect (lash_args);
fluid_settings_setint (settings, "lash.enable", enabled_lash ? 1 : 0);
}
#endif
/* The 'groups' setting is only relevant for LADSPA operation
* If not given, set number groups to number of audio channels, because
* they are the same (there is nothing between synth output and 'sound card')
*/
if ((audio_groups == 0) && (audio_channels != 0)) {
audio_groups = audio_channels;
}
fluid_settings_setint(settings, "synth.audio-groups", audio_groups);
/* create the synthesizer */
synth = new_fluid_synth(settings);
if (synth == NULL) {
fprintf(stderr, "Failed to create the synthesizer\n");
exit(-1);
}
cmd_handler = new_fluid_cmd_handler(synth);
if (cmd_handler == NULL) {
fprintf(stderr, "Failed to create the command handler\n");
goto cleanup;
}
/* try to load the user or system configuration */
if (config_file != NULL) {
fluid_source(cmd_handler, config_file);
} else if (fluid_get_userconf(buf, 512) != NULL) {
fluid_source(cmd_handler, buf);
} else if (fluid_get_sysconf(buf, 512) != NULL) {
fluid_source(cmd_handler, buf);
}
/* load the soundfonts (check that all non options are SoundFont or MIDI files) */
for (i = arg1; i < argc; i++) {
if (fluid_is_soundfont(argv[i]))
{
if (fluid_synth_sfload(synth, argv[i], 1) == -1)
fprintf(stderr, "Failed to load the SoundFont %s\n", argv[i]);
}
else if (!fluid_is_midifile(argv[i]))
fprintf (stderr, "Parameter '%s' not a SoundFont or MIDI file or error occurred identifying it.\n",
argv[i]);
}
#ifdef HAVE_SIGNAL_H
/* signal(SIGINT, handle_signal); */
#endif
/* start the synthesis thread */
adriver = new_fluid_audio_driver(settings, synth);
if (adriver == NULL) {
fprintf(stderr, "Failed to create the audio driver\n");
goto cleanup;
}
/* start the midi router and link it to the synth */
#if WITH_MIDI
if (midi_in) {
/* In dump mode, text output is generated for events going into and out of the router.
* The example dump functions are put into the chain before and after the router..
*/
router = new_fluid_midi_router(
settings,
dump ? fluid_midi_dump_postrouter : fluid_synth_handle_midi_event,
(void*)synth);
if (router == NULL) {
fprintf(stderr, "Failed to create the MIDI input router; no MIDI input\n"
"will be available. You can access the synthesizer \n"
"through the console.\n");
} else {
fluid_synth_set_midi_router(synth, router); /* Fixme, needed for command handler */
mdriver = new_fluid_midi_driver(
settings,
dump ? fluid_midi_dump_prerouter : fluid_midi_router_handle_midi_event,
(void*) router);
if (mdriver == NULL) {
fprintf(stderr, "Failed to create the MIDI thread; no MIDI input\n"
"will be available. You can access the synthesizer \n"
"through the console.\n");
}
}
}
#endif
/* play the midi files, if any */
for (i = arg1; i < argc; i++) {
if ((argv[i][0] != '-') && fluid_is_midifile(argv[i])) {
if (player == NULL) {
player = new_fluid_player(synth);
if (player == NULL) {
fprintf(stderr, "Failed to create the midifile player.\n"
"Continuing without a player.\n");
break;
}
}
fluid_player_add(player, argv[i]);
}
}
if (player != NULL) {
fluid_player_play(player);
}
/* run the server, if requested */
#if !defined(MACINTOSH) && !defined(WIN32)
if (with_server) {
server = new_fluid_server(settings, newclient, synth);
if (server == NULL) {
fprintf(stderr, "Failed to create the server.\n"
"Continuing without it.\n");
}
}
#endif
#ifdef LASH_ENABLED
if (enabled_lash)
fluid_lash_create_thread (synth);
#endif
/* run the shell */
if (interactive) {
print_welcome();
printf ("Type 'help' for information on commands and 'help help' for help topics.\n\n");
/* In dump mode we set the prompt to "". The UI cannot easily
* handle lines, which don't end with CR. Changing the prompt
* cannot be done through a command, because the current shell
* does not handle empty arguments. The ordinary case is dump ==
* 0.
*/
fluid_settings_setstr(settings, "shell.prompt", dump ? "" : "> ");
fluid_usershell(settings, cmd_handler);
}
cleanup:
#if !defined(MACINTOSH) && !defined(WIN32)
if (server != NULL) {
/* if the user typed 'quit' in the shell, kill the server */
if (!interactive) {
fluid_server_join(server);
}
delete_fluid_server(server);
}
#endif
if (cmd_handler != NULL) {
delete_fluid_cmd_handler(cmd_handler);
}
if (player != NULL) {
/* if the user typed 'quit' in the shell, stop the player */
if (interactive) {
fluid_player_stop(player);
}
fluid_player_join(player);
delete_fluid_player(player);
}
if (router) {
#if WITH_MIDI
if (mdriver) {
delete_fluid_midi_driver(mdriver);
}
delete_fluid_midi_router(router);
#endif
}
if (adriver) {
delete_fluid_audio_driver(adriver);
}
if (synth) {
delete_fluid_synth(synth);
}
if (settings) {
delete_fluid_settings(settings);
}
return 0;
}
static fluid_cmd_handler_t* newclient(void* data, char* addr)
{
fluid_synth_t* synth = (fluid_synth_t*) data;
return new_fluid_cmd_handler(synth);
}
/*
* print_usage
*/
void
print_usage()
{
print_welcome ();
fprintf(stderr, "Usage: fluidsynth [options] [soundfonts]\n");
fprintf(stderr, "Try -h for help.\n");
exit(0);
}
/*
* print_welcome
*/
void
print_welcome()
{
printf("FluidSynth version %s\n"
"Copyright (C) 2000-2006 Peter Hanappe and others.\n"
"Distributed under the LGPL license.\n"
"SoundFont(R) is a registered trademark of E-mu Systems, Inc.\n\n",
FLUIDSYNTH_VERSION);
}
/*
* print_help
*/
void
print_help()
{
print_welcome ();
printf("Usage: \n");
printf(" fluidsynth [options] [soundfonts] [midifiles]\n");
printf("Possible options:\n");
printf(" -a, --audio-driver=[label]\n"
" The audio driver [alsa,jack,oss,dsound,...]\n");
printf(" -C, --chorus\n"
" Turn the chorus on or off [0|1|yes|no, default = on]\n");
printf(" -c, --audio-bufcount=[count]\n"
" Number of audio buffers\n");
printf(" -d, --dump\n"
" Dump incoming and outgoing MIDI events to stdout\n");
printf(" -f, --load-config\n"
" Load command configuration file (shell commands)\n");
printf(" -G, --audio-groups\n"
" Defines the number of LADSPA audio nodes\n");
printf(" -g, --gain\n"
" Set the master gain [0 < gain < 10, default = 0.2]\n");
printf(" -h, --help\n"
" Print out this help summary\n");
printf(" -i, --no-shell\n"
" Don't read commands from the shell [default = yes]\n");
printf(" -j, --connect-jack-outputs\n"
" Attempt to connect the jack outputs to the physical ports\n");
printf(" -K, --midi-channels=[num]\n"
" The number of midi channels [default = 16]\n");
printf(" -L, --audio-channels=[num]\n"
" The number of stereo audio channels [default = 1]\n");
#ifdef LASH_ENABLED
printf(" -l, --disable-lash\n"
" Don't connect to LASH server\n");
#endif
printf(" -m, --midi-driver=[label]\n"
" The name of the midi driver to use [oss,alsa,alsa_seq,...]\n");
printf(" -n, --no-midi-in\n"
" Don't create a midi driver to read MIDI input events [default = yes]\n");
printf(" -p, --portname=[label]\n"
" Set MIDI port name (alsa_seq, coremidi drivers)\n");
printf(" -o\n"
" Define a setting, -o name=value (\"-o help\" to dump current values)\n");
printf(" -R, --reverb\n"
" Turn the reverb on or off [0|1|yes|no, default = on]\n");
printf(" -r, --sample-rate\n"
" Set the sample rate\n");
printf(" -s, --server\n"
" Start FluidSynth as a server process\n");
printf(" -V, --version\n"
" Show version of program\n");
printf(" -v, --verbose\n"
" Print out verbose messages about midi events\n");
printf(" -z, --audio-bufsize=[size]\n"
" Size of each audio buffer\n");
exit(0);
}

View file

@ -0,0 +1,220 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUIDSYNTH_PRIV_H
#define _FLUIDSYNTH_PRIV_H
#include "fluid_config.h"
#if HAVE_STRING_H
#include <string.h>
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_STDIO_H
#include <stdio.h>
#endif
#if HAVE_MATH_H
#include <math.h>
#endif
#if HAVE_STDARG_H
#include <stdarg.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if HAVE_LIMITS_H
#include <limits.h>
#endif
#include "fluidlite.h"
/***************************************************************
*
* BASIC TYPES
*/
#if defined(WITH_FLOAT)
typedef float fluid_real_t;
#else
typedef double fluid_real_t;
#endif
typedef enum {
FLUID_OK = 0,
FLUID_FAILED = -1
} fluid_status;
//socket disabled
/** Integer types */
typedef signed char sint8;
typedef unsigned char uint8;
typedef signed short sint16;
typedef unsigned short uint16;
typedef signed int sint32;
typedef unsigned int uint32;
//#if defined(MINGW32)
///* Windows using MinGW32 */
//typedef int8_t sint8;
//typedef uint8_t uint8;
//typedef int16_t sint16;
//typedef uint16_t uint16;
//typedef int32_t sint32;
//typedef uint32_t uint32;
//typedef int64_t sint64;
//typedef uint64_t uint64;
//#elif defined(_WIN32)
///* Windows */
//typedef signed __int8 sint8;
//typedef unsigned __int8 uint8;
//typedef signed __int16 sint16;
//typedef unsigned __int16 uint16;
//typedef signed __int32 sint32;
//typedef unsigned __int32 uint32;
//typedef signed __int64 sint64;
//typedef unsigned __int64 uint64;
//#elif defined(MACOS9)
///* Macintosh */
//typedef signed char sint8;
//typedef unsigned char uint8;
//typedef signed short sint16;
//typedef unsigned short uint16;
//typedef signed int sint32;
//typedef unsigned int uint32;
///* FIXME: needs to be verified */
//typedef long long sint64;
//typedef unsigned long long uint64;
//#else
///* Linux & Darwin */
//typedef int8_t sint8;
//typedef u_int8_t uint8;
//typedef int16_t sint16;
//typedef u_int16_t uint16;
//typedef int32_t sint32;
//typedef u_int32_t uint32;
//typedef int64_t sint64;
//typedef u_int64_t uint64;
//#endif
/***************************************************************
*
* FORWARD DECLARATIONS
*/
typedef struct _fluid_env_data_t fluid_env_data_t;
typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t;
typedef struct _fluid_channel_t fluid_channel_t;
typedef struct _fluid_tuning_t fluid_tuning_t;
typedef struct _fluid_hashtable_t fluid_hashtable_t;
typedef struct _fluid_client_t fluid_client_t;
/***************************************************************
*
* CONSTANTS
*/
#define FLUID_BUFSIZE 64
#ifndef PI
#define PI 3.141592654
#endif
/***************************************************************
*
* SYSTEM INTERFACE
*/
typedef FILE* fluid_file;
#define FLUID_MALLOC(_n) malloc(_n)
#define FLUID_REALLOC(_p,_n) realloc(_p,_n)
#define FLUID_NEW(_t) (_t*)malloc(sizeof(_t))
#define FLUID_ARRAY(_t,_n) (_t*)malloc((_n)*sizeof(_t))
#define FLUID_FREE(_p) free(_p)
#define FLUID_FOPEN(_f,_m) fopen(_f,_m)
#define FLUID_FCLOSE(_f) fclose(_f)
#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f)
#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set)
#define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n)
#define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n)
#define FLUID_STRLEN(_s) strlen(_s)
#define FLUID_STRCMP(_s,_t) strcmp(_s,_t)
#define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n)
#define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src)
#define FLUID_STRCHR(_s,_c) strchr(_s,_c)
#ifdef strdup
#define FLUID_STRDUP(s) strdup(s)
#else
#define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s)
#endif
#define FLUID_SPRINTF sprintf
#define FLUID_FPRINTF fprintf
#define fluid_clip(_val, _min, _max) \
{ (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); }
#if WITH_FTS
#define FLUID_PRINTF post
#define FLUID_FLUSH()
#else
#define FLUID_PRINTF printf
#define FLUID_FLUSH() fflush(stdout)
#endif
#define FLUID_LOG fluid_log
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
#define FLUID_ASSERT(a,b)
#define FLUID_ASSERT_P(a,b)
char* fluid_error(void);
/* Internationalization */
#define _(s) s
#endif /* _FLUIDSYNTH_PRIV_H */

View file

@ -0,0 +1,48 @@
#=============================================================================
# MuseScore
# Music Composition & Notation
#
# Copyright (C) 2020 MuseScore BVBA and others
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#=============================================================================
set(MODULE audio_midi)
include(${CMAKE_CURRENT_LIST_DIR}/3rdparty/fluidlite.cmake)
set(MODULE_SRC
${FLUIDSYNTH_SRC}
${CMAKE_CURRENT_LIST_DIR}/midimodule.cpp
${CMAKE_CURRENT_LIST_DIR}/midimodule.h
${CMAKE_CURRENT_LIST_DIR}/isynthesizer.h
${CMAKE_CURRENT_LIST_DIR}/isequencer.h
${CMAKE_CURRENT_LIST_DIR}/isoundfontfileprovider.h
${CMAKE_CURRENT_LIST_DIR}/miditypes.h
${CMAKE_CURRENT_LIST_DIR}/internal/fluidlitesynth.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/fluidlitesynth.h
${CMAKE_CURRENT_LIST_DIR}/internal/sequencer.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/sequencer.h
${CMAKE_CURRENT_LIST_DIR}/internal/sffproviderlocalfile.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/sffproviderlocalfile.h
)
set (MODULE_INCLUDE
${FLUIDSYNTH_INC}
)
set (MODULE_HAS_C_CODE 1)
include(${PROJECT_SOURCE_DIR}/build/module.cmake)

View file

@ -0,0 +1,388 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "fluidlitesynth.h"
#include <thread>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <fluidlite.h>
#include "log.h"
namespace {
static double GLOBAL_VOLUME_GAIN{ 1.8 };
}
enum fluid_status {
FLUID_OK = 0,
FLUID_FAILED = -1
};
template<class T>
static const T& clamp(const T& v, const T& lo, const T& hi)
{
return (v < lo) ? lo : (hi < v) ? hi : v;
}
using namespace mu::audio::midi;
struct mu::audio::midi::Fluid {
fluid_settings_t* settings{ nullptr };
fluid_synth_t* synth{ nullptr };
~Fluid()
{
delete_fluid_synth(synth);
delete_fluid_settings(settings);
}
};
FluidLiteSynth::FluidLiteSynth()
{
m_fluid = std::make_shared<Fluid>();
}
FluidLiteSynth::~FluidLiteSynth()
{
}
void FluidLiteSynth::loadSF(const Programs& programs,
const std::string& overridden_sf,
const OnLoadingChanged& onloading)
{
m_sf.programs = programs;
m_sf.loaded = false;
if (!overridden_sf.empty()) {
m_sf.loaded = true;
m_sf.file_path = overridden_sf;
if (onloading) {
onloading(100);
}
if (m_sf.onLoaded) {
m_sf.onLoaded();
}
} else {
sfprovider()->loadSF(programs,
[onloading](uint16_t percent) {
if (onloading) {
onloading(percent);
}
},
[this](bool success, const std::string& sf_path, const std::vector<Program>& programs) {
if (!success) {
LOGE() << "failed load sound font\n";
} else {
LOGD() << "success load sound font: " << sf_path << "\n";
}
m_sf.loaded = success;
m_sf.file_path = sf_path;
m_sf.programs = programs;
if (m_sf.onLoaded) {
m_sf.onLoaded();
}
});
}
}
void FluidLiteSynth::init(float samplerate, float gain, const OnInited& oninited)
{
if (m_sf.loaded) {
doInit(samplerate, gain, oninited);
} else {
m_sf.onLoaded = [this, samplerate, gain, oninited]() {
doInit(samplerate, gain, oninited);
};
}
}
void FluidLiteSynth::doInit(float samplerate, float gain, const OnInited& oninited)
{
bool success = m_sf.loaded;
if (!success) {
LOGE() << "failed make sound font\n";
} else {
LOGD() << "success make sound font\n";
success = init_synth(m_sf.file_path, samplerate, gain);
}
if (success) {
fluid_synth_program_reset(m_fluid->synth);
fluid_synth_system_reset(m_fluid->synth);
for (const Program& prog : m_sf.programs) {
fluid_synth_reset_tuning(m_fluid->synth, prog.ch);
fluid_synth_bank_select(m_fluid->synth, prog.ch, prog.bank);
fluid_synth_program_change(m_fluid->synth, prog.ch, prog.prog);
fluid_synth_set_interp_method(m_fluid->synth, prog.ch, FLUID_INTERP_DEFAULT);
fluid_synth_pitch_wheel_sens(m_fluid->synth, prog.ch, 12);
//LOGD() << "ch: " << prog.ch << ", prog: " << prog.prog << ", bank: " << prog.bank << "\n";
}
}
if (oninited) {
oninited(success);
}
if (success) {
m_sampleRate = samplerate;
// preallocated buffer size must be at least (sample rate) * (channels number)
m_preallocated.resize(int(m_sampleRate) * 2);
LOGI() << "success inited synth (fluid)\n";
} else {
LOGE() << "failed inited synth (fluid)\n";
}
}
void FluidLiteSynth::setGain(float gain)
{
m_gain = gain;
if (!m_fluid->synth) {
return;
}
fluid_synth_set_gain(m_fluid->synth, m_gain);
}
bool FluidLiteSynth::init_synth(const std::string& sf_path, float samplerate, float gain)
{
auto fluid_log_out = [](int level, char* message, void*) {
switch (level) {
case FLUID_PANIC:
case FLUID_ERR: {
LOGE() << message;
} break;
case FLUID_WARN: {
LOGW() << message;
} break;
case FLUID_INFO: {
LOGI() << message;
} break;
case FLUID_DBG: {
LOGD() << message;
} break;
}
if (level < FLUID_DBG) {
bool debugme = true;
(void)debugme;
}
};
fluid_set_log_function(FLUID_PANIC, fluid_log_out, nullptr);
fluid_set_log_function(FLUID_ERR, fluid_log_out, nullptr);
fluid_set_log_function(FLUID_WARN, fluid_log_out, nullptr);
fluid_set_log_function(FLUID_INFO, fluid_log_out, nullptr);
fluid_set_log_function(FLUID_DBG, fluid_log_out, nullptr);
m_gain = gain;
m_fluid->settings = new_fluid_settings();
fluid_settings_setnum(m_fluid->settings, "synth.gain", GLOBAL_VOLUME_GAIN * m_gain);
fluid_settings_setint(m_fluid->settings, "synth.audio-channels", 1);
fluid_settings_setint(m_fluid->settings, "synth.lock-memory", 0);
fluid_settings_setint(m_fluid->settings, "synth.threadsafe-api", 0);
fluid_settings_setnum(m_fluid->settings, "synth.sample-rate", static_cast<double>(samplerate));
fluid_settings_setint(m_fluid->settings, "synth.midi-channels", 80);
//fluid_settings_setint(_fluid->settings, "synth.min-note-length", 50);
//fluid_settings_setint(_fluid->settings, "synth.polyphony", conf.polyphony);
fluid_settings_setstr(m_fluid->settings, "synth.chorus.active", "no");
fluid_settings_setnum(m_fluid->settings, "synth.chorus.depth", 8);
fluid_settings_setnum(m_fluid->settings, "synth.chorus.level", 10);
fluid_settings_setint(m_fluid->settings, "synth.chorus.nr", 4);
fluid_settings_setnum(m_fluid->settings, "synth.chorus.speed", 1);
/*
https://github.com/FluidSynth/fluidsynth/wiki/UserManual
rev_preset
num:0 roomsize:0.2 damp:0.0 width:0.5 level:0.9
num:1 roomsize:0.4 damp:0.2 width:0.5 level:0.8
num:2 roomsize:0.6 damp:0.4 width:0.5 level:0.7
num:3 roomsize:0.8 damp:0.7 width:0.5 level:0.6
num:4 roomsize:0.8 damp:1.0 width:0.5 level:0.5
*/
fluid_settings_setstr(m_fluid->settings, "synth.reverb.active", "no");
fluid_settings_setnum(m_fluid->settings, "synth.reverb.room-size", 0.8);
fluid_settings_setnum(m_fluid->settings, "synth.reverb.damp", 1.0);
fluid_settings_setnum(m_fluid->settings, "synth.reverb.width", 0.5);
fluid_settings_setnum(m_fluid->settings, "synth.reverb.level", 0.5);
fluid_settings_setstr(m_fluid->settings, "audio.sample-format", "float");
m_fluid->synth = new_fluid_synth(m_fluid->settings);
int sfont_id = fluid_synth_sfload(m_fluid->synth, sf_path.c_str(), 0);
if (sfont_id == FLUID_FAILED) {
LOGE() << "failed load soundfont: " << sf_path;
return false;
} else {
LOGI() << "success load soundfont: " << sf_path;
}
LOGD() << "synth inited\n";
return true;
}
const Program& FluidLiteSynth::program(uint16_t chan) const
{
for (const Program& p : m_sf.programs) {
if (p.ch == chan) {
return p;
}
}
static Program dummy;
return dummy;
}
bool FluidLiteSynth::handleEvent(uint16_t chan, const Event& e)
{
if (m_isLoggingSynthEvents) {
const Program& p = program(chan);
LOGD() << "chan: " << chan << " bank: " << p.bank << " prog: " << p.prog << " " << e.to_string();
}
int ret = FLUID_OK;
switch (e.type) {
case ME_NOTEON: {
ret = fluid_synth_noteon(m_fluid->synth, chan, e.a, e.b);
} break;
case ME_NOTEOFF: { //msb << 7 | lsb
ret = fluid_synth_noteoff(m_fluid->synth, chan, e.b << 7 | e.a);
} break;
case ME_CONTROLLER: {
ret = fluid_synth_cc(m_fluid->synth, chan, e.a, e.b);
} break;
case ME_PROGRAMCHANGE: {
fluid_synth_program_change(m_fluid->synth, chan, e.b);
} break;
case ME_PITCHBEND: {
int pitch = e.b << 7 | e.a;
ret = fluid_synth_pitch_bend(m_fluid->synth, chan, pitch);
} break;
case META_TEMPO: {
// noop
} break;
default: {
LOGW() << "not supported event type: " << static_cast<int>(e.type) << "\n";
ret = FLUID_FAILED;
}
}
return ret == FLUID_OK;
}
void FluidLiteSynth::allSoundsOff()
{
IF_ASSERT_FAILED(m_fluid->synth) {
return;
}
fluid_synth_all_notes_off(m_fluid->synth, -1);
fluid_synth_all_sounds_off(m_fluid->synth, -1);
}
void FluidLiteSynth::flushSound()
{
IF_ASSERT_FAILED(m_fluid->synth) {
return;
}
fluid_synth_all_notes_off(m_fluid->synth, -1);
fluid_synth_all_sounds_off(m_fluid->synth, -1);
int size = int(m_sampleRate);
fluid_synth_write_float(m_fluid->synth, size, &m_preallocated[0], 0, 1, &m_preallocated[0], size, 1);
}
void FluidLiteSynth::channelSoundsOff(uint16_t chan)
{
IF_ASSERT_FAILED(m_fluid->synth) {
return;
}
fluid_synth_all_sounds_off(m_fluid->synth, chan);
}
bool FluidLiteSynth::channelVolume(uint16_t chan, float volume)
{
IF_ASSERT_FAILED(m_fluid->synth) {
return false;
}
int val = static_cast<int>(volume * 100.f);
val = clamp(val, 0, 127);
int ret = fluid_synth_cc(m_fluid->synth, chan, VOLUME_MSB, val);
return ret == FLUID_OK;
}
bool FluidLiteSynth::channelBalance(uint16_t chan, float balance)
{
IF_ASSERT_FAILED(m_fluid->synth) {
return false;
}
balance = clamp(balance, -1.f, 1.f);
float normalized = (balance < 0 ? 63 : 64) + 63 * balance;
int val = static_cast<int>(std::lround(normalized));
val = clamp(val, 0, 127);
int ret = fluid_synth_cc(m_fluid->synth, chan, PAN_MSB, val);
return ret == FLUID_OK;
}
bool FluidLiteSynth::channelPitch(uint16_t chan, int16_t pitch)
{
// 0-16383 with 8192 being center
IF_ASSERT_FAILED(m_fluid->synth) {
return false;
}
pitch = clamp(pitch, static_cast<int16_t>(-12), static_cast<int16_t>(12));
int32_t val = (8192 * pitch) / 12;
val = 8192 + val;
val = clamp(val, 0, 16383);
int ret = fluid_synth_pitch_bend(m_fluid->synth, chan, val);
return ret == FLUID_OK;
}
void FluidLiteSynth::writeBuf(float* stream, unsigned int len)
{
IF_ASSERT_FAILED(len > 0) {
return;
}
fluid_synth_write_float(m_fluid->synth, static_cast<int>(len), stream, 0, 1, stream, static_cast<int>(len), 1);
}

View file

@ -0,0 +1,103 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_FLUIDLITESYNTH_H
#define MU_AUDIO_FLUIDLITESYNTH_H
#include <memory>
#include <vector>
#include <cstdint>
#include <functional>
#include "../isynthesizer.h"
#include "../miditypes.h"
#include "modularity/ioc.h"
#include "isoundfontfileprovider.h"
//! NOTE Used for the test, the main synthesizer will not be this one.
namespace mu {
namespace audio {
namespace midi {
struct Fluid;
class FluidLiteSynth : public ISynthesizer
{
INJECT(midi, ISoundFontFileProvider, sfprovider)
public:
FluidLiteSynth();
~FluidLiteSynth() override;
void loadSF(const Programs& programs,const std::string& overridden_sf,const OnLoadingChanged& onloading) override;
void init(float samplerate, float gain, const OnInited& oninited) override;
void setGain(float gain) override;
bool handleEvent(uint16_t chan, const Event& e) override;
void allSoundsOff() override; // all channels
void flushSound() override;
void channelSoundsOff(uint16_t chan) override;
bool channelVolume(uint16_t chan, float val) override; // 0. - 1.
bool channelBalance(uint16_t chan, float val) override; // -1. - 1.
bool channelPitch(uint16_t chan, int16_t pitch) override; // -12 - 12
void writeBuf(float* stream, unsigned int len) override;
private:
void doInit(float samplerate, float gain, const OnInited& oninited);
bool init_synth(const std::string& sf_path, float samplerate, float gain);
const Program& program(uint16_t chan) const;
enum midi_control
{
BANK_SELECT_MSB = 0x00,
VOLUME_MSB = 0x07,
BALANCE_MSB = 0x08,
PAN_MSB = 0x0A
};
struct SF {
bool loaded{ false };
std::vector<Program> programs;
std::string file_path;
std::function<void()> onLoaded;
};
SF m_sf;
std::shared_ptr<Fluid> m_fluid{ nullptr };
float m_gain = 1.0f;
int16_t m_generalPitch = 0;
bool m_isLoggingSynthEvents = false;
std::vector<float> m_preallocated; // used to flush a sound
float m_sampleRate = 44100.0f;
int m_sfontID = -1;
};
}
}
}
#endif //MU_AUDIO_FLUIDLITESYNTH_H

View file

@ -0,0 +1,410 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "sequencer.h"
#include <limits>
#include "log.h"
#include "realfn.h"
using namespace mu::audio::midi;
Sequencer::~Sequencer()
{
if (m_status == Running) {
stop();
}
}
void Sequencer::loadMIDI(const std::shared_ptr<MidiData>& midi)
{
m_midi = midi;
buildTempoMap();
synth()->loadSF(m_midi->programs(), "", [this](uint16_t percent) {
LOGI() << "sf loading: " << percent;
});
}
void Sequencer::init(float samplerate, float gain)
{
reset();
m_sampleRate = samplerate;
synth()->init(samplerate, gain, [this, samplerate](bool success) {
if (!success) {
m_status = Error;
LOGE() << "failed init sequencer (failed init synth)\n";
} else {
LOGD() << "success init sequencer, samplerate: " << samplerate << "\n";
}
});
}
void Sequencer::changeGain(float gain)
{
synth()->setGain(gain);
}
void Sequencer::process(float sec)
{
if (!m_internalRunning) {
return;
}
uint64_t msec = static_cast<uint64_t>(sec * 1000);
uint64_t delta = msec - m_lastTimerMsec;
if (delta < 1) {
return;
}
player_callback(delta);
m_lastTimerMsec = msec;
}
float Sequencer::getAudio(float sec, float* buf, unsigned int len)
{
process(sec);
synth()->writeBuf(buf, len);
float cur_sec = static_cast<float>(m_curMsec) / 1000.f;
return cur_sec;
}
bool Sequencer::hasEnded() const
{
for (const Track& t : m_midi->tracks) {
for (const Channel& c : t.channels) {
if (!channel_eot(c)) {
return false;
}
}
}
return true;
}
Sequencer::Status Sequencer::status() const
{
return m_status;
}
bool Sequencer::run(float init_sec)
{
if (m_status == Running) {
return true;
}
if (m_status == Error) {
return false;
}
IF_ASSERT_FAILED(m_midi) {
return false;
}
m_lastTimerMsec = static_cast<uint64_t>(init_sec * 1000);
doRun();
m_status = Running;
return true;
}
void Sequencer::stop()
{
LOGI() << "Sequencer::stop\n";
doStop();
reset();
m_status = Stoped;
}
void Sequencer::reset()
{
m_curMsec = 0;
m_seekMsec = 0;
for (auto it = m_chanStates.begin(); it != m_chanStates.end(); ++it) {
it->second.eventIndex = 0;
}
}
bool Sequencer::doRun()
{
m_curMsec = m_seekMsec;
m_internalRunning = true;
return true;
}
void Sequencer::doStop()
{
m_internalRunning = false;
synth()->flushSound();
}
void Sequencer::doSeekChan(uint32_t seek_ticks, const Channel& c)
{
ChanState& state = m_chanStates[c.num];
state.eventIndex = 0;
for (size_t i = 0; i < c.events.size(); ++i) {
state.eventIndex = i;
const Event& event = c.events.at(i);
if (event.tick >= seek_ticks) {
break;
}
}
}
void Sequencer::doSeek(uint64_t seek_msec)
{
m_internalRunning = false;
m_seekMsec = seek_msec;
uint32_t seek_ticks = ticks(m_seekMsec);
for (const Track& t : m_midi->tracks) {
for (const Channel& c : t.channels) {
doSeekChan(seek_ticks, c);
}
}
m_curMsec = m_seekMsec;
m_internalRunning = true;
}
float Sequencer::playbackPosition() const
{
return m_curMsec / 1000.f;
}
void Sequencer::seek(float sec)
{
doSeek(static_cast<uint64_t>(sec * 1000.f));
synth()->flushSound();
}
uint64_t Sequencer::max_ticks(const std::vector<Track>& tracks) const
{
uint64_t maxTicks = 0;
auto findMaxTicks = [&maxTicks](const Channel& c) {
for (const Event& e : c.events) {
if (e.tick > maxTicks) {
maxTicks = e.tick;
}
}
};
for (const Track& t : tracks) {
for (const Channel& c : t.channels) {
findMaxTicks(c);
}
}
return maxTicks;
}
bool Sequencer::channel_eot(const Channel& chan) const
{
const ChanState& s = m_chanStates[chan.num];
if (s.eventIndex >= chan.events.size()) {
return true;
}
return false;
}
void Sequencer::buildTempoMap()
{
IF_ASSERT_FAILED(m_midi) {
return;
}
m_tempoMap.clear();
std::vector<std::pair<uint32_t, uint32_t> > tempos;
for (const auto& it : m_midi->tempomap) {
tempos.push_back({ it.first, it.second });
}
if (tempos.empty()) {
//! NOTE If temp is not set, then set the default temp to 120
tempos.push_back({ 0, 500000 });
}
uint64_t msec{ 0 };
for (size_t i = 0; i < tempos.size(); ++i) {
TempoItem t;
t.tempo = tempos.at(i).second;
t.startTicks = tempos.at(i).first;
t.startMsec = msec;
t.onetickMsec = static_cast<double>(t.tempo)
/ static_cast<double>(m_midi->division)
/ 1000.;
uint32_t end_ticks = ((i + 1) < tempos.size()) ? tempos.at(i + 1).first : std::numeric_limits<uint32_t>::max();
uint32_t delta_ticks = end_ticks - t.startTicks;
msec += static_cast<uint64_t>(delta_ticks * t.onetickMsec);
m_tempoMap.insert({ msec, std::move(t) });
// LOGI() << "TempoItem t.tick: " << t.start_ticks
// << ", t.tempo: " << t.tempo
// << ", t.onetick_msec: " << t.onetick_msec
// << ", end_msec: " << msec;
}
}
uint32_t Sequencer::ticks(uint64_t msec) const
{
auto it = m_tempoMap.lower_bound(msec);
const TempoItem& t = it->second;
uint64_t delta = msec - t.startMsec;
uint32_t ticks = static_cast<uint32_t>(delta / t.onetickMsec);
return t.startTicks + ticks;
}
bool Sequencer::player_callback(uint64_t timer_msec)
{
// static uint64_t last_msec{0};
// LOGI() << "msec: " << timer_msec << ", delta: " << (timer_msec - last_msec) << "\n";
// last_msec = timer_msec;
m_curMsec += (timer_msec * m_playSpeed);
uint32_t cur_ticks = ticks(m_curMsec);
// LOGI() << "timer_msec: " << timer_msec
// << ", cur_msec: " << _cur_msec
// << ", cur_ticks: " << cur_ticks
// << "\n";
auto sendEvents = [this, cur_ticks](const Channel& c) {
if (!channel_eot(c)) {
if (!send_chan_events(c, cur_ticks)) {
LOGE() << "failed send events\n";
}
}
};
for (const Track& t : m_midi->tracks) {
for (const Channel& c : t.channels) {
sendEvents(c);
}
}
return true;
}
bool Sequencer::send_chan_events(const Channel& chan, uint32_t ticks)
{
bool ret = true;
ChanState& state = m_chanStates[chan.num];
while (1)
{
if (channel_eot(chan)) {
return ret;
}
const Event& event = chan.events.at(state.eventIndex);
if (event.tick > ticks) {
return ret;
}
if (state.muted || event.type == MIDI_EOT || event.type == META_TEMPO) {
// noop
} else {
synth()->handleEvent(chan.num, event);
}
++state.eventIndex;
}
return ret;
}
float Sequencer::playbackSpeed() const
{
return m_playSpeed;
}
void Sequencer::setPlaybackSpeed(float speed)
{
m_playSpeed = speed;
}
bool Sequencer::isHasTrack(uint16_t ti) const
{
if (!m_midi) {
return false;
}
if (ti < m_midi->tracks.size()) {
return true;
}
return false;
}
void Sequencer::setIsTrackMuted(int ti, bool mute)
{
IF_ASSERT_FAILED(isHasTrack(ti)) {
return;
}
auto setMuted = [this, mute](const Channel& c) {
ChanState& state = m_chanStates[c.num];
state.muted = mute;
synth()->channelSoundsOff(c.num);
};
const Track& track = m_midi->tracks[ti];
for (const Channel& c : track.channels) {
setMuted(c);
}
}
void Sequencer::setTrackVolume(int ti, float volume)
{
IF_ASSERT_FAILED(isHasTrack(ti)) {
return;
}
const Track& track = m_midi->tracks[ti];
for (const Channel& c : track.channels) {
synth()->channelVolume(c.num, volume);
}
}
void Sequencer::setTrackBalance(int ti, float balance)
{
IF_ASSERT_FAILED(isHasTrack(ti)) {
return;
}
const Track& track = m_midi->tracks[ti];
for (const Channel& c : track.channels) {
synth()->channelBalance(c.num, balance);
}
}

View file

@ -0,0 +1,130 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_SEQUENCER_H
#define MU_AUDIO_SEQUENCER_H
#include <memory>
#include <vector>
#include <map>
#include <cstdint>
#include <functional>
#include <chrono>
#include <mutex>
#include "../isequencer.h"
#include "../miditypes.h"
#include "modularity/ioc.h"
#include "../isynthesizer.h"
namespace mu {
namespace audio {
namespace midi {
class ISynthesizer;
class Sequencer : public ISequencer
{
INJECT(midi, ISynthesizer, synth)
public:
Sequencer() = default;
~Sequencer() override;
enum Status {
Stoped = 0,
Running,
Error
};
Status status() const;
void loadMIDI(const std::shared_ptr<MidiData>& midi);
void init(float samplerate, float gain = 1);
void changeGain(float gain);
bool run(float init_sec) override;
void seek(float sec) override;
void stop() override;
float getAudio(float sec, float* buf, unsigned int len) override;
bool hasEnded() const override;
float playbackPosition() const;
float playbackSpeed() const override;
void setPlaybackSpeed(float speed) override;
void setIsTrackMuted(int t, bool mute) override;
void setTrackVolume(int ti, float volume) override;
void setTrackBalance(int ti, float balance) override;
private:
void process(float sec);
void reset();
uint64_t max_ticks(const std::vector<Track>& tracks) const;
bool channel_eot(const Channel& chan) const;
bool player_callback(uint64_t msec);
bool send_chan_events(const Channel& chan, uint32_t ticks);
void buildTempoMap();
uint32_t ticks(uint64_t msec) const;
bool isHasTrack(uint16_t num) const;
bool doRun();
void doStop();
void doSeek(uint64_t seek_msec);
void doSeekChan(uint32_t seek_ticks, const Channel& c);
struct TempoItem {
uint32_t tempo = 500000;
uint32_t startTicks = 0;
uint64_t startMsec = 0;
double onetickMsec = 0.0;
};
std::map<uint64_t /*msec*/, TempoItem> m_tempoMap;
Status m_status = Stoped;
bool m_internalRunning = false;
std::shared_ptr<MidiData> m_midi;
double m_oneTickMsec = 1;
float m_sampleRate = 44100.0f;
float m_playSpeed = 1.0;
uint64_t m_lastTimerMsec = 0;
uint64_t m_curMsec = 0;
uint64_t m_seekMsec = 0;
struct ChanState {
bool muted = false;
size_t eventIndex = 0;
};
mutable std::map<uint16_t, ChanState> m_chanStates;
};
}
}
}
#endif // MU_AUDIO_SEQUENCER_H

View file

@ -0,0 +1,35 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "sffproviderlocalfile.h"
#include "log.h"
using namespace mu::audio::midi;
void SFFProviderLocalFile::loadSF(const midi::Programs& programs, const OnLoading& onloading, const OnLoaded& onloaded)
{
//! NOTE For tests
io::path sffilePath = globalConfiguration()->dataPath() + "/sound/GeneralUser GS v1.471.sf2";
onloading(100);
onloaded(true, sffilePath, programs);
}

View file

@ -0,0 +1,44 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_SFFPROVIDER_LOCALFILE_H
#define MU_SFFPROVIDER_LOCALFILE_H
#include <string>
#include "../isoundfontfileprovider.h"
#include "modularity/ioc.h"
#include "iglobalconfiguration.h"
namespace mu {
namespace audio {
namespace midi {
class SFFProviderLocalFile : public ISoundFontFileProvider
{
INJECT(midi, framework::IGlobalConfiguration, globalConfiguration)
public:
void loadSF(const midi::Programs& programs, const OnLoading& onloading, const OnLoaded& onloaded) override;
};
}
}
}
#endif // MU_SFFPROVIDER_LOCALFILE_H

View file

@ -0,0 +1,61 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_ISEQUENCER_H
#define MU_AUDIO_ISEQUENCER_H
#include <string>
#include <functional>
#include <memory>
#include "modularity/imoduleexport.h"
#include "miditypes.h"
#include "async/channel.h"
namespace mu {
namespace audio {
namespace midi {
class ISequencer : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(ISequencer)
public:
virtual ~ISequencer() = default;
virtual void loadMIDI(const std::shared_ptr<MidiData>& midi) = 0;
virtual void init(float samplerate, float gain = 1) = 0;
virtual bool run(float init_sec) = 0;
virtual void seek(float sec) = 0;
virtual void stop() = 0;
virtual float getAudio(float sec, float* buf, unsigned int len) = 0;
virtual bool hasEnded() const = 0;
virtual float playbackSpeed() const = 0;
virtual void setPlaybackSpeed(float speed) = 0;
virtual void setIsTrackMuted(int t, bool mute) = 0;
virtual void setTrackVolume(int ti, float volume) = 0;
virtual void setTrackBalance(int ti, float balance) = 0;
};
}
}
}
#endif // MU_AUDIO_ISEQUENCER_H

View file

@ -0,0 +1,48 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_ISOUNDFONTFILEPROVIDER_H
#define MU_AUDIO_ISOUNDFONTFILEPROVIDER_H
#include <vector>
#include <functional>
#include "modularity/imoduleexport.h"
#include "miditypes.h"
namespace mu {
namespace audio {
namespace midi {
class ISoundFontFileProvider : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(ISoundFontFileProvider)
public:
virtual ~ISoundFontFileProvider() = default;
using OnLoading = std::function<void (uint16_t percent)>;
using OnLoaded = std::function<void (bool, const std::string& sf_path, const std::vector<midi::Program>& progs)>;
virtual void loadSF(const midi::Programs& programs, const OnLoading& onloading, const OnLoaded& onloaded) = 0;
};
}
}
}
#endif // MU_AUDIO_ISOUNDFONTFILEPROVIDER_H

View file

@ -0,0 +1,64 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_ISYNTHESIZER_H
#define MU_AUDIO_ISYNTHESIZER_H
#include <functional>
#include "modularity/imoduleexport.h"
#include "miditypes.h"
namespace mu {
namespace audio {
namespace midi {
class ISynthesizer : MODULE_EXPORT_INTERFACE
{
INTERFACE_ID(ISynthesizer)
public:
virtual ~ISynthesizer() = default;
using OnLoadingChanged = std::function<void (uint16_t percent)>;
using OnInited = std::function<void (bool success)>;
virtual void loadSF(const Programs& programs, const std::string& overridden_sf,
const OnLoadingChanged& onloading) = 0;
virtual void init(float samplerate, float gain, const OnInited& oninited) = 0;
virtual void setGain(float gain) = 0;
virtual bool handleEvent(uint16_t chan, const Event& e) = 0;
virtual void allSoundsOff() = 0; // all channels
virtual void flushSound() = 0;
virtual void channelSoundsOff(uint16_t chan) = 0;
virtual bool channelVolume(uint16_t chan, float val) = 0; // 0. - 1.
virtual bool channelBalance(uint16_t chan, float val) = 0; // -1. - 1.
virtual bool channelPitch(uint16_t chan, int16_t val) = 0; // -12 - 12
virtual void writeBuf(float* stream, unsigned int len) = 0;
};
}
}
}
#endif // MU_AUDIO_ISYNTHESIZER_H

View file

@ -0,0 +1,38 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "midimodule.h"
#include "modularity/ioc.h"
#include "internal/fluidlitesynth.h"
#include "internal/sequencer.h"
#include "internal/sffproviderlocalfile.h"
using namespace mu::audio::midi;
std::string MidiModule::moduleName() const
{
return "audio_midi";
}
void MidiModule::registerExports()
{
framework::ioc()->registerExport<ISynthesizer>(moduleName(), new FluidLiteSynth());
framework::ioc()->registerExport<ISequencer>(moduleName(), new Sequencer());
framework::ioc()->registerExport<ISoundFontFileProvider>(moduleName(), new SFFProviderLocalFile());
}

View file

@ -0,0 +1,39 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_MIDIMODULE_H
#define MU_AUDIO_MIDIMODULE_H
#include "modularity/imodulesetup.h"
namespace mu {
namespace audio {
namespace midi {
class MidiModule : public framework::IModuleSetup
{
public:
std::string moduleName() const override;
void registerExports() override;
};
}
}
}
#endif // MU_AUDIO_MIDIMODULE_H

View file

@ -0,0 +1,213 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_AUDIO_MIDITYPES_H
#define MU_AUDIO_MIDITYPES_H
#include <string>
#include <sstream>
#include <cstdint>
#include <vector>
#include <map>
#include <functional>
namespace mu {
namespace audio {
namespace midi {
enum EventType {
ME_INVALID = 0,
ME_NOTEOFF,
ME_NOTEON,
ME_CONTROLLER,
ME_PITCHBEND,
ME_META,
META_TEMPO,
ME_PROGRAMCHANGE,
ME_ALLNOTESOFF,
MIDI_EOT
};
enum CntrType {
CTRL_INVALID = 0,
CTRL_PROGRAM
};
struct Event {
uint32_t tick{ 0 };
EventType type{ ME_INVALID };
int a{ 0 };
int b{ 0 };
Event() = default;
Event(uint32_t tick, EventType type, int a, int b)
: tick(tick), type(type), a(a), b(b) {}
bool operator ==(const Event& other) const
{
return tick == other.tick && type == other.type
&& a == other.a && b == other.b;
}
bool operator !=(const Event& other) const
{
return !operator==(other);
}
static std::string type_to_string(EventType t)
{
switch (t) {
case EventType::ME_INVALID: return "INVALID";
case EventType::ME_NOTEOFF: return "NOTEOFF";
case EventType::ME_NOTEON: return "NOTEON";
case EventType::ME_CONTROLLER: return "CONTROLLER";
case EventType::ME_PITCHBEND: return "PITCHBEND";
case EventType::ME_META: return "META";
case EventType::META_TEMPO: return "TEMPO";
case EventType::ME_PROGRAMCHANGE: return "PROGRAMCHANGE";
case EventType::ME_ALLNOTESOFF: return "ALLNOTESOFF";
case EventType::MIDI_EOT: return "EOT";
}
return std::string();
}
static std::string cc_to_string(int cc)
{
switch (cc) {
case 2: return "BREATH_MSB";
default: return std::to_string(cc);
}
}
std::string to_string() const
{
std::string str;
str += "tick: " + std::to_string(tick);
str += ", type: " + type_to_string(type);
switch (type) {
case EventType::ME_NOTEON: {
str += ", key: " + std::to_string(a);
str += ", vel: " + std::to_string(b);
} break;
case EventType::ME_NOTEOFF: {
str += ", key: " + std::to_string(a);
} break;
case EventType::ME_CONTROLLER: {
str += ", cc: " + cc_to_string(a);
str += ", val: " + std::to_string(b);
} break;
case EventType::ME_PITCHBEND: {
int pitch = b << 7 | a;
str += ", pitch: " + std::to_string(pitch);
} break;
default:
str += ", a: " + std::to_string(a);
str += ", b: " + std::to_string(b);
}
return str;
}
};
struct Channel {
uint16_t num{ 0 };
uint16_t bank{ 0 };
uint16_t program{ 0 };
std::vector<Event> events;
};
struct Track {
std::vector<Channel> channels;
};
struct Program {
uint16_t ch{ 0 };
uint16_t prog{ 0 };
uint16_t bank{ 0 };
};
using Programs = std::vector<midi::Program>;
struct MidiData {
uint16_t division{ 480 };
std::map<uint32_t /*tick*/, uint32_t /*tempo*/> tempomap;
std::vector<Track> tracks;
Programs programs() const
{
Programs progs;
for (const Track& t : tracks) {
for (const Channel& ch : t.channels) {
Program p;
p.ch = ch.num;
p.bank = ch.bank;
p.prog = ch.program;
progs.push_back(std::move(p));
}
}
return progs;
}
uint16_t channelsCount() const
{
uint16_t c = 0;
for (const Track& t : tracks) {
c += t.channels.size();
}
return c;
}
std::string dump(bool withEvents = false)
{
std::stringstream ss;
ss << "division: " << division << "\n";
ss << "tempo changes: " << tempomap.size() << "\n";
for (const auto& it : tempomap) {
ss << " tick: " << it.first << ", tempo: " << it.second << "\n";
}
ss << "\n";
ss << "tracks count: " << tracks.size() << "\n";
ss << "channels count: " << channelsCount() << "\n";
for (size_t ti = 0; ti < tracks.size(); ++ti) {
ss << "track: " << ti << ", channels: " << tracks.at(ti).channels.size() << "\n";
for (const Channel& ch : tracks.at(ti).channels) {
ss << " ch num: " << ch.num
<< ", bank: " << ch.bank
<< ", prog: " << ch.program
<< ", events: " << ch.events.size()
<< "\n";
if (withEvents) {
for (const Event& e : ch.events) {
ss << e.to_string() << "\n";
}
}
}
}
ss.flush();
return ss.str();
}
};
}
}
}
#endif // MU_AUDIO_MIDITYPES_H

View file

@ -88,6 +88,7 @@ if (BUILD_UI_MU4)
shortcuts
workspace
audio_engine
audio_midi
scores
extensions
importexport

View file

@ -27,6 +27,7 @@
#include "framework/shortcuts/shortcutsmodule.h"
#include "framework/workspace/workspacemodule.h"
#include "framework/audio/engine/audioenginemodule.h"
#include "framework/audio/midi/midimodule.h"
#include "mu4/appshell/appshellmodule.h"
#include "mu4/cloud/cloudmodule.h"
#include "mu4/context/contextmodule.h"
@ -63,6 +64,7 @@ ModulesSetup::ModulesSetup()
<< new mu::shortcuts::ShortcutsModule()
<< new mu::workspace::WorkspaceModule()
<< new mu::audio::engine::AudioEngineModule()
<< new mu::audio::midi::MidiModule()
<< new mu::scores::ScoresModule()
<< new mu::extensions::ExtensionsModule()
<< new mu::domain::notation::NotationDomainModule()

View file

@ -10,20 +10,61 @@ Rectangle {
id: devtools
}
Row {
anchors.left: parent.left
anchors.right: parent.right
height: 40
FlatButton {
text: "Play"
width: 80
onClicked: devtools.playSine()
Column {
anchors.fill: parent
Row {
anchors.left: parent.left
anchors.right: parent.right
height: 40
spacing: 8
FlatButton {
text: "Play Sine"
width: 120
onClicked: devtools.playSine()
}
FlatButton {
text: "Stop Sine"
width: 120
onClicked: devtools.stopSine()
}
}
FlatButton {
text: "Stop"
width: 80
onClicked: devtools.stopSine()
Row {
anchors.left: parent.left
anchors.right: parent.right
height: 40
spacing: 8
FlatButton {
text: "Play Source Midi"
width: 120
onClicked: devtools.playSourceMidi()
}
FlatButton {
text: "Stop Source Midi"
width: 120
onClicked: devtools.stopSourceMidi()
}
}
Row {
anchors.left: parent.left
anchors.right: parent.right
height: 40
spacing: 8
FlatButton {
text: "Play Player Midi"
width: 120
onClicked: devtools.playPlayerMidi()
}
FlatButton {
text: "Stop Player Midi"
width: 120
onClicked: devtools.stopPlayerMidi()
}
}
}
}