Compare commits

..

83 Commits

Author SHA1 Message Date
Tobias Doerffel
427e0d6727 Made 0.4.5 release
Bumped version number to 0.4.5 in CMakeLists.txt and README.
2009-08-11 18:21:56 +02:00
Tobias Doerffel
d9490f9203 TODO list: moved over items from master branch TODO list
Moved over all items that concern 0.4.x series from TODO list in
master branch and removed deprecated items.
2009-08-11 14:49:13 +02:00
Tobias Doerffel
57c79759c8 MidiWinMM: added missing break directive after case-block
A break directive was missing in MidiWinMM::handleInputEvent(), leading
to inappropriate calls to qWarning() on pitch bend.
(cherry picked from commit 58023cf2cf)
2009-08-10 13:53:54 +02:00
Tobias Doerffel
ad1124cde9 Added CALF LADSPA plugins
As per popular demand I added CALF LADSPA plugins to be shipped with
LMMS. After some minor modifications the plugins compile and work on
win32 platform too.
(cherry picked from commit 35ca0aab69)
2009-08-10 13:50:20 +02:00
Tobias Doerffel
3e62853de5 FlpImport: fixed crash on invalid notes
Added a range check for channel parameter when adding notes. This fixes
a crash when for example importing the project posted on lmms-devel
by Andew Kelley lately.
(cherry picked from commit ee9d88e2d4)
2009-08-09 11:23:26 +02:00
Tobias Doerffel
9a4cfedf67 FlpImport: changed most printf()s to qDebug()s and qWarning()s
This improves structure of messages when importing FLP files.
(cherry picked from commit 20eda9a756)
2009-08-09 11:21:57 +02:00
Tobias Doerffel
e714a68fe5 Updated German localization files
There have been some changes to strings in the program which weren't
translated to German lately. Furthermore improved existing
translations and fixed mis-spellings.
2009-08-09 00:37:55 +02:00
Janne Matti Sinisalo
a4f9682069 Added cheese_enthusiast's new piano keys
Added cheese_enthusiast's <bdreed@woh.rr.com> new, better-looking piano keys. Also includes pressed piano-roll keys.
(cherry picked from commit 367d8360fc)
2009-08-09 00:29:33 +02:00
Paul Giblock
5138ed6722 Rename all Controller-family classes to new style
Adjust capitialization on all Controller-related classes to new
standards and update all calling code
(cherry picked from commit f1d60958f0)
2009-08-09 00:29:16 +02:00
Paul Giblock
9eed60c068 Hotfix for resizable controller rack
Apparently I didn't compile the latest version of my changes and there
was a lurking error.  This has been fixed.
(cherry picked from commit a9a3c796f7)
2009-08-08 18:26:22 +02:00
Paul Giblock
e109f3e550 Make Controller-Rack resizable
Per request, make the Controller-rack vertically sizable.  This is also
part of our effort to slowly deprecate old fixed-positioning code in
favor of layouts.
(cherry picked from commit bb76ba5121)
2009-08-08 10:30:10 +02:00
Tobias Doerffel
93a456c67a MainWindow: removed HQ mode button
Switching to HQ mode in standard workflow doesn't make much sense and
is likely to cause problems. Therefore remove the HQ mode button for
for time being. One can still export projects in high(er) quality.
(cherry picked from commit d66c71ebf8)
2009-08-07 18:58:38 +02:00
Tobias Doerffel
d7df990e6c MidiWinMM: use qWarning() rather than printf()
Using printf() is really old C-style, therefore changed all printf()s
to qWarning()s.
(cherry picked from commit 94c5c5a46f)
2009-08-06 13:48:47 +02:00
Tobias Doerffel
5891a37635 Win32Toolchain: define MINGW_PREFIX and QT_HEADERS_DIR
MINGW_PREFIX is a better variable name than CC_PREFIX (cross copmling
prefix), therefore rename it. Furthermore define QT_HEADERS_DIR due to
changed logic in CMake's FindQt4 module.
(cherry picked from commit 94ebcf30bc)
2009-08-06 13:48:30 +02:00
Tobias Doerffel
8485cf3157 ZynAddSubFX/FLTK: updated to SVN revision 6826
Updated FLTK to SVN revision 6841 of branch-1.3:

- Fixed glibc 2.10 compiler problems (Fedora 11 and others)
  with scandir() and strchr() (STR #2222)
- Fl_Scroll mods for global scrollbar size control
- various other minor stuff (see SVN log)
(cherry picked from commit 803fd68a56)
2009-08-05 19:11:30 +02:00
Tobias Doerffel
84e0dc1b81 Sf2Player: removed trailing spaces in code
There were some unsolicited trailing spaces in the code. Removed them.
(cherry picked from commit 964c6532f2)
2009-08-05 18:09:42 +02:00
Tobias Doerffel
99791ddf25 Sf2Player: fixed crash and race conditions
Do not crash if no fluid_voice could be determined in
sf2Instrument::playNote() (which for example happens if no soundfont
is loaded). Furthermore protect the FluidSynth API calls in the
envelope and panning code with global synth mutex.
(cherry picked from commit 144f0c6c80)
2009-08-05 18:04:06 +02:00
Tobias Doerffel
31b2b00ba4 Sf2Player: added panning and volume envelope support
Finally there's proper support for panning and volume envelopes. This
is achieved by changing parameters of individual voices (one voice is
being created for each note). The new code also replaces the old
panning hacks which played notes on different channels and changed
panning of individual channels.

Closes #2010818.

(cherry picked from commit 4baf459da2)

stable-0.4 branch: forward-ported various trivial changes from master.
2009-08-05 17:38:50 +02:00
Tobias Doerffel
d536d851a4 PianoRoll: removed duplicate function call in testPlayNote()
There's no need to send a MidiNotePanning event twice in
PianoRoll::testPlayNote(). Removed the duplicate function call.
(cherry picked from commit 4cdcd1a670)
2009-08-05 17:19:59 +02:00
Tobias Doerffel
852976ed74 Panning: fixed wrong type-conversion in panningToMidi()
Casting to panning_t in calculation in panningToMidi() leads to
integer overflows and thus to miscalculations. This resulted for
example in wrong panning during note preview when editing panning
of a note in PianoRoll. Casting to int instead fixes the issue.
(cherry picked from commit 6e3e1513c7)
2009-08-05 17:19:53 +02:00
Tobias Doerffel
edf2d4d104 Whole code base: various cleanups, removed SINGLE_SOURCE_COMPILE relicts
* cleaned up code and improved coding style
* removed old SINGLE_SOURCE_COMPILE macro relicts
* use QString::toUtf8() instead of QString::toAscii() wherever possible

(cherry picked from commit 0bb54199b0)
2009-08-05 17:17:57 +02:00
Tobias Doerffel
f969b7d892 Mixer: fixed wrong macro logic leading to potential performance loss
The macro logic for defining the SPINLOCK_PAUSE macro tested against
LMMS_HOST_X86_64 twice instead of testing against LMMS_HOST_X86_64 and
LMMS_HOST_X86. This caused the SPINLOCK_PAUSE macro not being set on
x86.

(cherry picked from commit bf8f823751)
2009-08-03 18:57:33 +02:00
Tobias Doerffel
6d6bbe3b32 CAPS: updated to version 0.4.3
Updated CAPS plugins to version 0.4.3 - changes:

  * basics.h cleanup / comments
  * minor Makefile cleanup
  * comment cosmetics
  * Eq and Eq2x2 per-band Q changed to 1.414 (= 1 octave)
  * Eq lowest band default value fixed to read 0
  * Niclas' fix for the bessel function implemented
  * uninitialised plugin states eliminated thanks to Damon
  * linker options for OSX added to the Makefile

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit e0df82056c)
2009-08-03 18:56:17 +02:00
Tobias Doerffel
7bddad1895 ZynAddSubFX: really fix compilation error
I should always try to compile before committing... Works now!
2009-08-03 18:51:55 +02:00
Tobias Doerffel
3504e49119 ZynAddSubFX: fixed compilation error
Fixed compilation error introduced by commit
f3e13c4427.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 27d9c17e3f)
2009-08-02 18:24:06 +02:00
Tobias Doerffel
42b1584cbe RemotePlugin: added QSTR_TO_STDSTR macro and use it in LVSL/ZASF
QString::toStdString() is not aware of locale specific characters (it
just converts to ASCII). Therefore added new macro QSTR_TO_STDSTR which
converts a QString to std::string with UTF8 characters.

Use this new macro in LMMS VST Support Layer and ZynAddSubFX plugin.
This fixes for example VST plugins not being loaded when the path to
the DLL contains non-ASCII (e.g. cyrillic) characters.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit f3e13c4427)
2009-07-29 17:13:47 +02:00
Tobias Doerffel
54dadfcf48 MidiTime/Pattern: fixed divisions by zero with time sigs 1/16+
Setting time signatures 1/16+ lead to divisions by zero in two places.
Fixed this by adding according qMax() call (closes #2818125).

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit cb72bf2260)
2009-07-10 17:50:55 +02:00
Tobias Doerffel
29d0d2b43a RemoteVstPlugin: fixed typo in 4ad5add745
Commit 4ad5add745 introduced a typo which
lead to compilation failure. This commit fixes it.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit c4c94985a6)
2009-07-09 12:54:59 +02:00
Tobias Doerffel
6883e0479c RemoteVstPlugin: typedef VstMidiEventList
Added a typedef for VstMidiEventList so actual type does not have
to be hard-coded each time in the code.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 4ad5add745)
2009-07-09 12:54:59 +02:00
Tobias Doerffel
598d1dc816 RemoteVstPlugin: don't process MIDI events in GUI thread
Don't process MIDI events in GUI thread as this might corrupt MIDI
event array if both GUI and processing thread access it. Fixes
possible crashes.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit e7ab8e5670)
2009-07-09 12:54:59 +02:00
Tobias Doerffel
9fc0d8962d RemoteVstPlugin: encapsulate locking in separate inline functions
Locking the plugin is now achieved by calling RemoteVstPlugin::lock()
and RemoteVstPlugin::unlock().

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 2240d6644d)
2009-07-09 12:54:59 +02:00
Tobias Doerffel
91912e143a MidiPort: added realOutputChannel() returning zero-based MIDI channel
MidiPort::outputChannelModel is ranged from 1 to 16 for displaying
user-friendly values. However internally MIDI channels are ranged from
0 to 15. Therefore added realOutputChannel() which returns zero-based
MIDI channel which should be used everywhere except for the GUI.

Fixes MIDI events being sent to VST plugins on channel 2 instead of
channel 1. Makes some more VST plugins actually usable.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 4f249400ac)
2009-07-09 12:54:14 +02:00
Tobias Doerffel
00ca278f2f RemoteVstPlugin: removed different threading models
Different threading models aren't used anymore as SplittedThreadingModel
after all recent improvements works fine for all plugins tested so far.
It even shows that a lot more VST plugins are running now properly.
Therefore all support for TraditionalThreadingModel has been removed
which greatly simplifies code and makes it much more maintainable.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 41c9318be8)
2009-07-09 12:43:57 +02:00
Tobias Doerffel
0424c5cec8 RemoteVstPlugin: prefer chunks over parameters for settings
When saving or restoring settings, prefer chunks over parameters.
This fixes some problems with plugins which get confused e.g. if you
restore individual parameters rather than the whole chunk.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit dffd9ecfbe)
2009-07-07 21:22:28 +02:00
Tobias Doerffel
9b2e3fc262 TrackContentWidget: fix graphical glitches with Qt 4.5
Starting with Qt 4.5 there were minor graphical glitches in
TrackContentWidget. Fix this by not setting Qt::WA_OpaquePaintEvent
attribute.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 1015868e1f)
2009-07-07 21:22:20 +02:00
Tobias Doerffel
c66fa25dfe RemoteVstPlugin: define O_BINARY to 0 on Linux
O_BINARY flag is not supported on Linux, therefore define it to 0.
Fixes compilation failure on Linux.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit d6a451bdf5)
2009-07-07 18:37:32 +02:00
Tobias Doerffel
c1f5075e16 RemoteVstPlugin: open file with O_BINARY when reading/writing chunks
When writing chunk to file or read it back from file, open file with
O_BINARY flag. Otherwise on win32 for example line endings (\n) are
converted (\r\n) and with real binary data this screws up things.

Thanks to Oleg Sharonov for pointing this out.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 48a3d4ebe4)
2009-07-07 17:09:41 +02:00
Tobias Doerffel
d555db6109 PianoRoll: switch back to draw note after pasting notes
When pasing notes via Ctrl+V somehow the edit-tool was left in an
undefined state. Therefore explicitely switch back to draw mode so
user can move pasted notes (closes #2808607).

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit cf7539caaf)
2009-07-07 10:49:09 +02:00
Tobias Doerffel
a0b198fb38 ZynAddSubFX/FLTK: updated to SVN revision 6826
Updated FLTK to SVN revision 6826 of branch-1.3:

- Corrected const methods of Fl_Text_{Buffer|Display|Selection}
  to be declared const, corrected an Fl_Text_Buffer attrib. typo
- Fixed OpenGL shared context handling (STR #2135)
- Fixed gray-scale images with alpha channel (STR #2105)
- Fixed unexpected shortcut behavior for Win32 (STR #2199)
- Fixed documentation for Fl_Progress (STR #2209)

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 0de2949aed)
2009-07-07 02:13:05 +02:00
Tobias Doerffel
c0f221e984 ZynAddSubFX: now build on OS X as well
Now that we fixed RemotePlugin support on OS X, we can enable build of
ZynAddSubFX on OS X. Some GUI threading issues still have to be solved
though.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit ea376e90fe)
2009-07-07 01:59:22 +02:00
Tobias Doerffel
f50d5ce943 RemotePlugin: refactored system feature configuration
Variuos features (native/Qt semaphores/shared memory) are now
configured by macros like USE_QT_SHMEM and USE_QT_SEMAPHORES. This
allows central and individual configuration of features according to
the platform to build for at the beginning of the file.

Finally makes RemotePlugin work on OS X by not using native semaphores
rather than QSystemSemaphore's.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 2ef5dffec0)
2009-07-07 01:59:14 +02:00
Tobias Doerffel
3c80a079a2 InstrumentTrack: fixed muting of frozen patterns in BB tracks
In InstrumentTrack::play(...) the local variable "bb_track" always
was either undefined or NULL due to a lost call (probably during M/V
architecture switch) to bbTrack::findBBTrack(...). This could lead to
crashes and/or misbehaviour when playing frozen patterns in BB tracks.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit fb58dc00ab)
2009-07-07 01:59:04 +02:00
Tobias Doerffel
fb56b6757d Track.h: removed unused class declaration "bbTrack"
The class forward declaration for "bbTrack" is neither used nor
required, therefore remove it.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 913aa5a6e8)
2009-07-07 01:58:54 +02:00
Tobias Doerffel
8fd507bcff RemotePlugin: added DEBUG_REMOTE_PLUGIN macro
Added new DEBUG_REMOTE_PLUGIN macro. If it is set, the process won't
be launched. Instead important information are printed allowing the
developer to run the process with according parameters in a debugger.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 33ce491caf)
2009-07-07 01:58:44 +02:00
Tobias Doerffel
7c2b3e6240 RemoteVstPlugin: print number of inputs/outputs as debugg message
When input/output count changes, always print new number of inputs and
outputs as debugg message.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 5838f63d0b)
2009-07-07 01:58:09 +02:00
Tobias Doerffel
d8c616afcf RemoteVstPlugin: reset m_inputs and m_outputs in updateInOutCount()
Reset m_inputs and m_outputs to NULL after deleting the memory they're
pointing to. Fixes possible crash if new input or output count is 0
and the count changes again later.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 206d906c10)
2009-07-07 01:58:00 +02:00
Tobias Doerffel
4e81299729 RemoteVstPlugin: workaround for early host callbacks by some plugins
Some plugins call host callback from within their main function,
causing RemoteVstPlugin::m_plugin not to be set yet. Therefore
explicitely set RemoteVstPlugin::m_plugin if it's NULL. Makes plugins
by Wallander Instruments (and possibly others as well) work.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 38f7552ce4)
2009-07-07 01:57:50 +02:00
Tobias Doerffel
7ef61b04d7 RemoteVstPlugin: thread-safe plugin dispatching
Plugin dispatcher was called without any protection from various threads
leading to regular crashes or deadlocks within the plugin. The plugin
dispatching is now encapsulated into one central function which protects
the dispatcher by a mutex.

Furthermore improved handling of audioMasterIdle and
audioMasterUpdateDisplay in host callback by posting a message to GUI
thread asking for idle processing instead of directly calling plugin
dispatcher.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit e65b282166)
2009-07-07 01:57:42 +02:00
Tobias Doerffel
33917e4340 RemoteVstPlugin: coding style fixes
Renamed class remoteVstPlugin to RemoteVstPlugin + other minor coding
style fixes.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit cca39513d0)
2009-07-07 01:57:32 +02:00
Tobias Doerffel
1e0acbef85 RemoteVstPlugin: fixed threading issues with saving/restoring chunks
Saving and restoring chunks from/to plugin seems to work now after a
few tweaks regarding memory-allocation and threading (according
functions need to be called from withing GUI thread). However needs the
new code some testing (especially whether it breaks previously working
plugins).

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit db878b664d)
2009-07-07 01:57:23 +02:00
Tobias Doerffel
798c179b58 RemoteVstPlugin: added more NULL checks
Check for m_plugin being NULL in RemoteVstPlugin::inputCount(),
RemoteVstPlugin::outputCount() and RemoteVstPlugin::pluginVersion().
Makes various VST's load properly now.

Thanks to Oleg Sharonov for pointing this out.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 9b5c0d19f8)
2009-07-07 01:57:12 +02:00
Tobias Doerffel
12aaffe149 VstBase: initial support for saving/restoring chunks
Some VST plugins save their state as chunks rather than parameters.
According code to save/restore those chunks has been taken from FST 1.9.
However restore currently is disabled as it mostly fails for whatever
reason.
(cherry picked from commit 0abd4aad70)
2009-07-07 01:56:39 +02:00
Tobias Doerffel
cfe9c35f41 Sf2Player: allocate temporary buffer on stack if possible
When compiling with GCC always allocate a temporary buffer for
resampling on stack rather than doing an expensive heap allocation.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit d1353247d9)
2009-07-05 16:43:03 +02:00
Tobias Doerffel
fa21cf10a9 Do not build ZynAddSubFX on OS X
Do not build the ZynAddSubFX plugin on OS X until a proper replacement
for unnamed semaphores (which for whatever reason are not supported on
OS X) has been implemented. Using the win32 code (QSystemSemaphore)
could be an option but needs testing.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit fd9d2b0e61)
2009-06-28 23:43:50 +02:00
Tobias Doerffel
35eeb55704 Various fixes for properly building on OS X
There were various (trivial) problems with settings of the build system
which prevented LMMS from being built on OS X.

Thanks to Daniel Klaffenbach for providing his Mac for development
purposes.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit ed30985aa4)
2009-06-28 23:43:44 +02:00
Tobias Doerffel
facab0c2d8 AutomationEditor: set pen for painter outside inner loop
Set pen for drawing automation pattern outside of inner loop. This
might save some CPU cycles.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit ddf3d3dc66)
2009-06-22 22:14:03 +02:00
Tobias Doerffel
c222c062c5 PianoRoll: fixed crash with detune tool
When clicking at an invalid position with the detune tool, LMMS could
crash as an invalid iterator was referenced (closes #2808589).

Furthermore renamed PianoRoll::ModeOpen to PianoRoll::ModeEditDetuning.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 9ca93040de)
2009-06-18 23:33:21 +02:00
Tobias Doerffel
7a632277b8 Renamed type noteVector to NoteVector
Renamed type noteVector to NoteVector as well as ::iterator to
::Iterator and ::const_iterator to ::ConstIterator.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit e12476f79c)
2009-06-18 23:32:32 +02:00
Tobias Doerffel
87e44aa241 Pattern: properly advance iterator when removing notes
We must not keep the iterator at the same position when erasing notes
from the m_notes array. This usually is not a problem when using QVector
but we might switch to QList somewhen later.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 2d5dfc7b15)
2009-06-18 23:07:43 +02:00
Tobias Doerffel
0098ff874a NotePlayHandle: fixed crash at playback of subnotes
Commit 08ea133aa2 uncovered a flaw in
NotePlayHandle::play(...). When iterating over subnotes for each subnote
after playing it, we check whether it is finished. If this is true, the
according subnote gets erased from the m_subNotes array. However we have
to set the subnote iterator to what QList::erase(...) returns instead of
keeping it where it was before. This accidentally worked when using
QVector as type for NotePlayHandle arrays but caused a crash now that
we're using QList.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 0d6ba6291b)
2009-06-18 23:07:31 +02:00
Tobias Doerffel
621512a7b7 Song: reset window title after creating new project from template
Reset window title when creating new project from template (after
actually loading template file). Furthermore do not reset window title
at the beginning of load process anymore.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit cf0a91637b)
2009-06-18 23:07:22 +02:00
Tobias Doerffel
0faaab4521 PlayHandle: use QList instead of QVector for PlayHandle array
Use QList instead of QVector when using a set of PlayHandle's. QList
has faster insert/remove operations and in most cases we iterate through
the array using iterators so there's no performance drop.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 2cc49b8010)
2009-06-17 10:43:51 +02:00
Tobias Doerffel
cafb24fe9a NotePlayHandle: cleanups, header dependency reductions etc.
* do not include instrument_track.h in note_play_handle.h by making
  two functions non-inline
* renamed notePlayHandleVector / constNotePlayHandleVector to
  NotePlayHandleList / ConstNotePlayHandleList and changed typedef
  to QList (which has faster insert- and remove-operations).
* removed unused method willFinishThisPeriod()

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 08ea133aa2)
2009-06-15 16:40:22 +02:00
Tobias Doerffel
33dd77f55c MMP: detect compressed files rather than guessing by extension
Detect whether a given file is compressed by first trying to parse
its content as XML data. If it failed, try to uncompress and
parse again. This is more flexible than just looking whether the
filename extension is "mmpz".

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit c0794d0c41)
2009-06-15 16:28:09 +02:00
Tobias Doerffel
93a19e43f1 InstrumentTrack: new method silenceAllNotes()
Added new method silenceAllNotes() which is a replacement for
invalidateAllMyNPH() and also resets m_runningMidiNotes array.

silenceAllNotes() is now used in destructor as well.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 3c9859da9f)
2009-06-11 01:19:33 +02:00
Tobias Doerffel
57c4a5abc5 MultimediaProject: splitted constructor for loading either file or data
The old constructor treated the string argument either as filename or
as raw XML data, depending on 2nd parameter. This is a mess and quickly
leads to confusion.

Now we have two constructors taking either a filename as string or a
QByteArray with XML data. Loading actual data has been separated into
MultimediaProject::loadData( const QByteArray & ).

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 5a00ebd360)
2009-06-11 01:16:30 +02:00
Tobias Doerffel
3d9dc25f0b InstrumentTrack: presetPreviewPlayHandle no longer a friend class
For a long time presetPreviewPlayHandle has been a friend class of
InstrumentTrack as it had to modify MidiPort settings. However the
InstrumentTrack's MidiPort is now accessible via getMidiPort() so
there's no need for keeping presetPreviewPlayHandle a friend class
anymore.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit a6090ebe23)
2009-06-11 00:57:10 +02:00
Tobias Doerffel
5bb9af15ab RemoteVstPlugin: use SplittedThreading model for Proteus VX
Proteus VX requires SplittedThreading model in order to run properly.
Issues with actually working UI interaction still need to be worked
out although it rather looks like a bug in WINE.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit b3c5d498e2)
2009-06-02 00:51:29 +02:00
Tobias Doerffel
66147bebf0 ZynAddSubFX: fixed compilation failure of FLTK if glibc >= 2.10
Fixed compilation failure of FLTK on systems with glibc newer than
version 2.9 where the signature of scandir(...) has changed.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit dc539d88de)
2009-06-01 00:56:27 +02:00
Tobias Doerffel
6b71db0640 ZynAddSubFX: fixed compilation failure of FLTK
Fixed compilation failure of FLTK on broken systems where strchr()
takes a non-const char pointer.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 3003163e9b)
2009-06-01 00:56:20 +02:00
Tobias Doerffel
0c8fdec27c MinGW cross compiling script: fixed invocation of cmake
Fixed invocation of cmake in the MinGW cross compiling script so it
works properly when doing an out-of-tree build (which should be the
default way of building LMMS).

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 8c50e0ef9a)
2009-05-31 18:57:32 +02:00
Tobias Doerffel
ca5ba2971b ZynAddSubFX: processed UI files with Fluid from FLTK 1.3 branch
Processed all Fluid UI files with Fluid from FLTK 1.3 branch so the
generated code is up to date.

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 5b1900c4d0)
2009-05-31 18:56:01 +02:00
Tobias Doerffel
e0bb2cd043 ZynAddSubFX: integrated FLTK 1.3.0 branch from SVN
Integrated FLTK 1.3.0 branch from SVN which brings UTF8 support and
fully integrated Xft support (SVN rev. 6791).

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
(cherry picked from commit 8affa66c61)
2009-05-31 18:55:49 +02:00
Tobias Doerffel
41efb51518 TrackView: do not set Qt::WA_OpaquePaintEvent attribute
Due to optimizations in Qt 4.5 setting Qt::WA_OpaquePaintEvent attribute
for TrackView widgets caused a line of distorted pixels at the bottom of
the left part so do not set this attribute anymore (closes #2795861).
(cherry picked from commit cdcc158c03)
2009-05-25 13:48:27 +02:00
Tobias Doerffel
168a68c1b3 ControllerConnection: fixed segfault when finalizing invalid controller
In ControllerConnection::finalizeConnections() we did not check whether
the current controller ID really exists in Song's controller array.
Fixes segfault when loading projects with screwed controller settings.
(cherry picked from commit 6d77f83ae9)
2009-05-19 18:37:11 +02:00
Tobias Doerffel
6066209ef3 lmms.rc.in: advanced copyright notice
Advanced copyright notice in lmms.rc.in which is used as resource file
when building win32 executable.
(cherry picked from commit 34e607f5bb)
2009-05-18 14:35:36 +02:00
Tobias Doerffel
5cd54b3f20 PianoRoll: removed unused variable
Removed unused variable volumeHandles in
PianoRoll::computeSelectedNotes(bool) which as a side-effect also fixes

/usr/include/qt4/QtCore/qvector.h: In member function ‘void PianoRoll::computeSelectedNotes(bool)’:
/usr/include/qt4/QtCore/qvector.h:421: warning: dereferencing pointer ‘<anonymous>’ does break strict-aliasing rules
/usr/include/qt4/QtCore/qvector.h:114: note: initialized from here
(cherry picked from commit 24353ca248)
2009-05-16 17:02:02 +02:00
Tobias Doerffel
1c38473255 NotePlayHandle: use public method for accessing pitch model
Do not use private m_pitchModel member variable of InstrumentTrack even
if NotePlayHandle is a friend class. Instead use the already existing
InstrumentTrack::pitchModel() method.
(cherry picked from commit 9dcb62c630)
2009-05-16 17:01:57 +02:00
Tobias Doerffel
a6f8e11128 Fixed linker flags when building for OS X
Linker flag "-E" is not supported on OS X so drop it if LMMS_BUILD_APPLE
is defined.
(cherry picked from commit 582393cdba)
2009-05-16 02:50:54 +02:00
Andrew Kelley
27a828e579 don't coerce the installer to use /usr when cmaking.
(cherry picked from commit c53df545ae)

Conflicts:

	INSTALL
2009-05-06 20:22:12 -07:00
Achim Settelmeier
2f5dd60fd9 MainWindow: fixed problems with maximized window-state when using FVWM2
Added a workaround for FVWM2 as showMaximized() does not work with it
unless the window is already visible.
(cherry picked from commit 515f0243b9)
2009-05-05 23:36:25 +02:00
Frederik
03ecb86494 Allow compilation with -Werror=format-security
This commit fixes some calls to functions that are taking a format
string and an optional set of parameters as arguments. At some places
no format string was specified if only a simple C string was to be
sprintf()ed.  However for security reasons this is bad and was replaced
by code like

	sprintf( dest, "%s", str );
(cherry picked from commit af284e980f)
2009-05-05 23:25:35 +02:00
Andrew Kelley
1bc94beae6 fixed dependency on libfluidsynth which did not check LMMS_HAVE_FLUIDSYNTH in 0.4.4 2009-05-05 04:25:08 -07:00
637 changed files with 68991 additions and 10467 deletions

View File

@@ -15,7 +15,7 @@ INCLUDE(FindPkgConfig)
SET(VERSION_MAJOR "0")
SET(VERSION_MINOR "4")
SET(VERSION_PATCH "4")
SET(VERSION_PATCH "5")
#SET(VERSION_SUFFIX "")
SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
IF(VERSION_SUFFIX)
@@ -33,6 +33,7 @@ ELSE(LMMS_HOST_X86_64)
ENDIF(LMMS_HOST_X86_64)
OPTION(WANT_ALSA "Include ALSA (Advanced Linux Sound Architecture) support" ON)
OPTION(WANT_CALF "Include CALF LADSPA plugins" ON)
OPTION(WANT_CAPS "Include C* Audio Plugin Suite (LADSPA plugins)" ON)
OPTION(WANT_CMT "Include Computer Music Toolkit LADSPA plugins" ON)
OPTION(WANT_FFTW3F "Include SpectrumAnalyzer and ZynAddSubFX plugin" ON)
@@ -118,6 +119,13 @@ IF(NOT SNDFILE_FOUND)
MESSAGE(FATAL_ERROR "LMMS requires libsndfile1 and libsndfile1-dev >= 1.0.11 - please install, remove CMakeCache.txt and try again!")
ENDIF(NOT SNDFILE_FOUND)
IF(WANT_CALF)
SET(LMMS_HAVE_CALF TRUE)
SET(STATUS_CALF "OK")
ELSE(WANT_CALF)
SET(STATUS_CALF "not built as requested")
ENDIF(WANT_CALF)
IF(WANT_CAPS)
SET(LMMS_HAVE_CAPS TRUE)
SET(STATUS_CAPS "OK")
@@ -446,7 +454,9 @@ IF(LMMS_BUILD_WIN32)
ENDIF(LMMS_BUILD_WIN64)
ELSE(LMMS_BUILD_WIN32)
SET_TARGET_PROPERTIES(lmms PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,-E")
IF(NOT LMMS_BUILD_APPLE)
SET_TARGET_PROPERTIES(lmms PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,-E")
ENDIF(NOT LMMS_BUILD_APPLE)
INSTALL(TARGETS lmms RUNTIME DESTINATION bin)
INSTALL(FILES ${CMAKE_BINARY_DIR}/lmms.1.gz DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1/ PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
@@ -602,6 +612,7 @@ MESSAGE(
"* VST-instrument hoster : ${STATUS_VST}\n"
"* VST-effect hoster : ${STATUS_VST}\n"
"* SpectrumAnalyzer : ${STATUS_FFTW3F}\n"
"* CALF LADSPA plugins : ${STATUS_CALF}\n"
"* CAPS LADSPA plugins : ${STATUS_CAPS}\n"
"* CMT LADSPA plugins : ${STATUS_CMT}\n"
"* TAP LADSPA plugins : ${STATUS_TAP}\n"

View File

@@ -5,11 +5,10 @@ then run
mkdir build
cd build
cmake ../ -DCMAKE_INSTALL_PREFIX=/usr
cmake ../
make
sudo make install
This way an out-of-tree build is performed. You can also run "cmake ." directly
in the root of source-tree although this is not recommended. When performing an
out-of-tree build after there's already an in-tree build, make sure to run
@@ -23,3 +22,9 @@ that are going to be built into LMMS or built as plugins. Install the
according libraries and development files if a certain feature is not enabled.
Then remove CMakeCache.txt and run cmake again.
If you want to supply an install prefix to cmake, add the flag:
-DCMAKE_INSTALL_PREFIX=<prefix>
Where <prefix> can be /usr, /usr/local, /opt, etc. The default is /usr/local.

2
README
View File

@@ -1,4 +1,4 @@
Linux MultiMedia Studio 0.4.4
Linux MultiMedia Studio 0.4.5
==============================
Copyright (c) 2004-2009 by LMMS-developers

72
TODO
View File

@@ -1,52 +1,32 @@
- do not process effects when playing frozen patterns
- select number of channels in export-project-dialog
- try to make vestige-plugin-dlls relative
- do song-editor-tempo-connection to vst-plugin inside remoteVSTPlugin
- add/remove-steps button in bb-editor for adding/removing according number of steps to/from all patterns of visible beat/baseline
- replace rest of wizard by simple directory-selection-dialog for working-dir when running the first time
- correctly load steps/dots from FLP-files
- convert FL-Plucked!-parameters to Vibed-parameters
- in flp-import-filter: merge play-list-items if possible
- integrated sample-browser in context-menu of sample-track/-tco
- make note able of journalling
- before calling undoStep/redoStep from journallingObject, save journalling-state-context and disabled journalling, restore afterwards
- intelligent journal-entry-merging
- undo/redo-support in note/track etc.
- save tco-settings in trackContentWidget::saveSettings() etc. instead of track::...
- restore stacking-order of windows when loading project
- bristol-bindings?
- resample sample-track-tcos when using hq-mode
- add support for panes-interface (like blender) (instead of MDI etc.)
Version 0.4.x
=============
- save tco-settings in trackContentWidget::saveSettings() etc. instead of
track::...
- resample sample-track-tcos when exporting at different samplerate
- message to user when importing unsupported MIDI-file (track-count = 0)
- AMS/OMS-bindings
- remove binary-embed-system (Qt4-resource-system?)
- recording-functionality
- do not hang when saving while loading VST-plugin (because then we call dispatcher while the load-process is still going on)
- tempo-recogn. and sync of beat-samples
- make color-scheme switchable: LMMS / user
- piano roll: mouse cursor isn't updated correctly in selection mode
(from resizing note edit area)
- when you add vestige, have it automatically pop the find VST plugin dialog
- try to make vestige-plugin-dlls relative
- select all MIDI devices by default when you bring up the "connect to controller"
window and wait for first event - then uncheck all other MIDI devices that no
events were detected from
- load asdlol.mmpz. if you render it without playing it, or if you play it
the first time, you hear unwanted artifacts. (solution: apply automation
before playing)
- autosave every 30s (configurable!) and offer recovery at startup after crash
- make piano-roll use rubberband instead of implementing a simple one on it's own
- level-meters in output-graph and instrument-track
- MIDI-program/MIDI-mapping/process program-/channel-change-events from MIDI-files
- DSSI-support
- use drawLineF() for drawing notes in pattern::paintEvent() in qt4-version
- only redraw region given by paint-event in pattern, bbTCO, sampleTCO etc.
- pre-listen when opening sample with QFileDialog
- panning-editing in piano-roll
- speed up painting of sampleTCO
- panning env+lfo
- rewrite export-project-dialog using layout-mechanism
- make piano-roll use the global clipboard??
- add more localizations:
- Swedish
- Norwegian
- Greece
- ...
- do not process effects when playing frozen patterns
- copy-pasted automation patterns have to be manually linked back to
their knob for some reason
- improve TrackLabelButton: split 80%-20% (80%=name, 20%=button showing a popup
menu with track operations, make the midi input a top-level menu item)
- when you click and drag a mixer bar, it doesn't click and drag, it sets
absolutely. this is annoying
- effect-board -> live-fx from input
- chord-editor?
- WAVE/OGG/MP3-Import -> FFT-analysis -> write notes
- classical note-edit-window -> also ability of printing and maybe later scanning & recognition of notes
- add FLAC as export-format?
See TODO file in master branch and/or the TODO list in the Wiki for details
regarding the development series.

View File

@@ -1,7 +1,4 @@
MINGW=/opt/mingw
MINGW=/opt/mingw32
export PATH=$PATH:$MINGW/bin
#./configure --prefix=$MINGW --host=i586-mingw32 CC=$MINGW/bin/i586-mingw32-gcc CXX=$MINGW/bin/i586-mingw32-g++ DLLTOOL=$MINGW/bin/i586-mingw32-dlltool OBJDUMP=$MINGW/bin/i586-mingw32-objdump RANLIB=$MINGW/bin/i586-mingw32-ranlib AR=$MINGW/bin/i586-mingw32-ar AS=$MINGW/bin/i586-mingw32-as STRIP=$MINGW/bin/i586-mingw32-strip --with-qtdir=$MINGW --with-win32 CFLAGS="-mtune=generic -mmmx -fno-strict-aliasing" CXXFLAGS="-mtune=generic -mmmx -fno-strict-aliasing"
#./configure --prefix=$MINGW --host=i586-mingw32 --build=i586-mingw32 CC=$MINGW/bin/i586-mingw32-gcc CXX=$MINGW/bin/i586-mingw32-g++ --with-qtdir=$MINGW --with-win32
cmake . -DCMAKE_TOOLCHAIN_FILE=cmake/modules/Win32Toolchain.cmake
cmake .. -DCMAKE_TOOLCHAIN_FILE=cmake/modules/Win32Toolchain.cmake

View File

@@ -1,26 +1,28 @@
SET(MINGW_PREFIX /opt/mingw32)
# this one is important
SET(CMAKE_SYSTEM_NAME Windows)
#this one not so much
SET(CMAKE_SYSTEM_VERSION 1)
SET(CMAKE_SYSTEM_PROCESSOR i686)
SET(CMAKE_INSTALL_PREFIX /opt/mingw32)
SET(CMAKE_INSTALL_PREFIX ${MINGW_PREFIX})
SET(CC_PREFIX /opt/mingw32)
# specify the cross compiler
SET(CMAKE_C_COMPILER ${CC_PREFIX}/bin/i586-pc-mingw32-gcc)
SET(CMAKE_CXX_COMPILER ${CC_PREFIX}/bin/i586-pc-mingw32-g++)
SET(CMAKE_C_COMPILER ${MINGW_PREFIX}/bin/i586-pc-mingw32-gcc)
SET(CMAKE_CXX_COMPILER ${MINGW_PREFIX}/bin/i586-pc-mingw32-g++)
# where is the target environment
SET(CMAKE_FIND_ROOT_PATH /opt/mingw32)
SET(QT_BINARY_DIR ${CC_PREFIX}/bin)
SET(QT_LIBRARY_DIR ${CC_PREFIX}/lib)
SET(QT_QTCORE_LIBRARY ${CC_PREFIX}/lib/libQtCore4.a)
SET(QT_INCLUDE_DIR ${CC_PREFIX}/include/qt4)
SET(QT_QTCORE_INCLUDE_DIR ${CC_PREFIX}/include/qt4/QtCore)
SET(QT_MKSPECS_DIR ${CC_PREFIX}/share/qt4/mkspecs)
SET(QT_BINARY_DIR ${MINGW_PREFIX}/bin)
SET(QT_LIBRARY_DIR ${MINGW_PREFIX}/lib)
SET(QT_QTCORE_LIBRARY ${MINGW_PREFIX}/lib/libQtCore4.a)
SET(QT_INCLUDE_DIR ${MINGW_PREFIX}/include/qt4)
SET(QT_HEADERS_DIR ${MINGW_PREFIX}/include/qt4)
SET(QT_QTCORE_INCLUDE_DIR ${MINGW_PREFIX}/include/qt4/QtCore)
SET(QT_MKSPECS_DIR ${MINGW_PREFIX}/share/qt4/mkspecs)
SET(QT_MOC_EXECUTABLE ${QT_BINARY_DIR}/moc.exe)
SET(QT_RCC_EXECUTABLE ${QT_BINARY_DIR}/rcc.exe)
SET(QT_QMAKE_EXECUTABLE /usr/bin/qmake)
@@ -32,8 +34,8 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
SET(PKG_CONFIG_EXECUTABLE ${CC_PREFIX}/bin/pkg-config)
SET(PKG_CONFIG_EXECUTABLE ${MINGW_PREFIX}/bin/pkg-config)
INCLUDE_DIRECTORIES(${CC_PREFIX}/include)
LINK_DIRECTORIES(${CC_PREFIX}/lib ${CC_PREFIX}/bin)
INCLUDE_DIRECTORIES(${MINGW_PREFIX}/include)
LINK_DIRECTORIES(${MINGW_PREFIX}/lib ${MINGW_PREFIX}/bin)

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 B

After

Width:  |  Height:  |  Size: 305 B

View File

@@ -1,8 +1,8 @@
/*
* controller.h - declaration of class controller, which provides a
* Controller.h - declaration of class controller, which provides a
* standard for all controllers and controller plugins
*
* Copyright (c) 2008 Paul Giblock <pgllama/at/gmail.com>
* Copyright (c) 2008-2009 Paul Giblock <pgllama/at/gmail.com>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -32,13 +32,13 @@
#include "mv_base.h"
#include "journalling_object.h"
class controllerDialog;
class controller;
class ControllerDialog;
class Controller;
typedef QVector<controller *> controllerVector;
typedef QVector<Controller *> ControllerVector;
class controller : public model, public journallingObject
class Controller : public model, public journallingObject
{
Q_OBJECT
public:
@@ -55,10 +55,10 @@ public:
NumControllerTypes
} ;
controller( ControllerTypes _type, model * _parent,
Controller( ControllerTypes _type, model * _parent,
const QString & _display_name );
virtual ~controller();
virtual ~Controller();
virtual float currentValue( int _offset );
@@ -103,8 +103,8 @@ public:
virtual void loadSettings( const QDomElement & _this );
virtual QString nodeName( void ) const;
static controller * create( ControllerTypes _tt, model * _parent );
static controller * create( const QDomElement & _this,
static Controller * create( ControllerTypes _tt, model * _parent );
static Controller * create( const QDomElement & _this,
model * _parent );
inline static float fittedValue( float _val )
@@ -120,7 +120,7 @@ public:
public slots:
virtual controllerDialog * createDialog( QWidget * _parent );
virtual ControllerDialog * createDialog( QWidget * _parent );
virtual void setName( const QString & _new_name )
{
@@ -140,7 +140,7 @@ protected:
QString m_name;
ControllerTypes m_type;
static controllerVector s_controllers;
static ControllerVector s_controllers;
static unsigned int s_frames;
@@ -149,7 +149,7 @@ signals:
// The value changed while the mixer isn't running (i.e: MIDI CC)
void valueChanged( void );
friend class controllerDialog;
friend class ControllerDialog;
} ;

View File

@@ -1,5 +1,5 @@
/*
* controller_connection.h - declaration of a controller connect, which
* ControllerConnection.h - declaration of a controller connect, which
* provides a definition of the link between a controller and
* model, also handles deferred creation of links while
* loading project
@@ -32,30 +32,30 @@
#include <QtCore/QObject>
#include <QtCore/QVector>
#include "controller.h"
#include "Controller.h"
#include "journalling_object.h"
class controllerConnection;
class ControllerConnection;
typedef QVector<controllerConnection *> controllerConnectionVector;
typedef QVector<ControllerConnection *> ControllerConnectionVector;
class EXPORT controllerConnection : public QObject, public journallingObject
class EXPORT ControllerConnection : public QObject, public journallingObject
{
Q_OBJECT
public:
controllerConnection( controller * _controller );
controllerConnection( int _controllerId );
ControllerConnection( Controller * _controller );
ControllerConnection( int _controllerId );
virtual ~controllerConnection();
virtual ~ControllerConnection();
inline controller * getController( void )
inline Controller * getController( void )
{
return m_controller;
}
void setController( controller * _controller );
void setController( Controller * _controller );
inline void setController( int _controllerId );
@@ -87,19 +87,19 @@ public slots:
protected:
//virtual controllerDialog * createDialog( QWidget * _parent );
controller * m_controller;
Controller * m_controller;
QString m_targetName;
int m_controllerId;
bool m_ownsController;
static controllerConnectionVector s_connections;
static ControllerConnectionVector s_connections;
signals:
// The value changed while the mixer isn't running (i.e: MIDI CC)
void valueChanged( void );
friend class controllerConnectionDialog;
friend class ControllerConnectionDialog;
};
#endif

View File

@@ -1,5 +1,5 @@
/*
* controller_connection_dialog.h - dialog allowing the user to create and
* ControllerConnectionDialog.h - dialog allowing the user to create and
* modify links between controllers and models
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
@@ -31,14 +31,14 @@
#include <QtGui/QSortFilterProxyModel>
#include <QtGui/QStandardItemModel>
#include "controller.h"
#include "Controller.h"
#include "automatable_model.h"
class QLineEdit;
class QListView;
class QScrollArea;
class autoDetectMidiController;
class AutoDetectMidiController;
class comboBox;
class groupBox;
class tabWidget;
@@ -48,15 +48,15 @@ class midiPortMenu;
class controllerConnectionDialog : public QDialog
class ControllerConnectionDialog : public QDialog
{
Q_OBJECT
public:
controllerConnectionDialog( QWidget * _parent,
ControllerConnectionDialog( QWidget * _parent,
const automatableModel * _target_model );
virtual ~controllerConnectionDialog();
virtual ~ControllerConnectionDialog();
controller * chosenController( void )
Controller * chosenController( void )
{
return m_controller;
}
@@ -91,11 +91,11 @@ private:
tabWidget * m_mappingBox;
QLineEdit * m_mappingFunction;
controller * m_controller;
Controller * m_controller;
const automatableModel * m_targetModel;
// Temporary midiController
autoDetectMidiController * m_midiController;
AutoDetectMidiController * m_midiController;
} ;
#endif

View File

@@ -1,8 +1,8 @@
/*
* controller_dialog.h - per-controller-specific view for changing a
* ControllerDialog.h - per-controller-specific view for changing a
* controller's settings
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
* Copyright (c) 2008-2009 Paul Giblock <drfaygo/at/gmail.com>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -30,25 +30,22 @@
#include "mv_base.h"
class controller;
class Controller;
class controllerDialog : public QWidget, public modelView
class ControllerDialog : public QWidget, public modelView
{
Q_OBJECT
public:
controllerDialog( controller * _controller, QWidget * _parent );
ControllerDialog( Controller * _controller, QWidget * _parent );
virtual ~controllerDialog();
virtual ~ControllerDialog();
signals:
void closed();
protected:
/* virtual void contextMenuEvent( QContextMenuEvent * _me ) {};
virtual void paintEvent( QPaintEvent * _pe ) {};
virtual void modelChanged( void ) {};*/
virtual void closeEvent( QCloseEvent * _ce );
} ;

View File

@@ -1,7 +1,7 @@
/*
* controller_rack_view.h - view for song's controllers
* ControllerRackView.h - view for song's controllers
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
* Copyright (c) 2008-2009 Paul Giblock <drfaygo/at/gmail.com>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -33,29 +33,28 @@
class QPushButton;
class QScrollArea;
//class QVBoxLayout;
class controllerView;
class ControllerView;
class controllerRackView : public QWidget, public serializingObject
class ControllerRackView : public QWidget, public serializingObject
{
Q_OBJECT
public:
controllerRackView();
virtual ~controllerRackView();
ControllerRackView();
virtual ~ControllerRackView();
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
virtual void loadSettings( const QDomElement & _this );
inline virtual QString nodeName( void ) const
{
return( "controllerrackview" );
return( "ControllerRackView" );
}
public slots:
void deleteController( controllerView * _view );
void deleteController( ControllerView * _view );
private slots:
@@ -64,7 +63,7 @@ private slots:
private:
QVector<controllerView *> m_controllerViews;
QVector<ControllerView *> m_controllerViews;
QVBoxLayout * m_mainLayout;
QScrollArea * m_scrollArea;

View File

@@ -1,5 +1,5 @@
/*
* controller_view.h - view-component for an control
* ControllerView.h - view-component for an control
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
*
@@ -28,7 +28,7 @@
#include <QtGui/QWidget>
#include "automatable_model.h"
#include "controller.h"
#include "Controller.h"
#include "mv_base.h"
class QGroupBox;
@@ -39,21 +39,21 @@ class QMdiSubWindow;
class ledCheckBox;
class controllerView : public QWidget, public modelView
class ControllerView : public QWidget, public modelView
{
Q_OBJECT
public:
controllerView( controller * _controller, QWidget * _parent );
virtual ~controllerView();
ControllerView( Controller * _controller, QWidget * _parent );
virtual ~ControllerView();
inline controller * getController( void )
inline Controller * getController( void )
{
return( castModel<controller>() );
return( castModel<Controller>() );
}
inline const controller * getController( void ) const
inline const Controller * getController( void ) const
{
return( castModel<controller>() );
return( castModel<Controller>() );
}
@@ -65,7 +65,7 @@ public slots:
signals:
void deleteController( controllerView * _view );
void deleteController( ControllerView * _view );
protected:
@@ -79,7 +79,7 @@ private:
QPixmap m_bg;
ledCheckBox * m_bypass;
QMdiSubWindow * m_subWindow;
controllerDialog * m_controllerDlg;
ControllerDialog * m_controllerDlg;
bool m_show;
} ;

View File

@@ -1,5 +1,5 @@
/*
* lfo_controller.h - A LFO-based controller and dialog
* LfoController.h - A LFO-based controller and dialog
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
*
@@ -29,8 +29,8 @@
#include "mv_base.h"
#include "automatable_model.h"
#include "controller.h"
#include "controller_dialog.h"
#include "Controller.h"
#include "ControllerDialog.h"
#include "tempo_sync_knob.h"
#include "oscillator.h"
@@ -42,13 +42,13 @@ class tempoSyncKnob;
class pixmapButton;
class oscillator;
class lfoController : public controller
class LfoController : public Controller
{
Q_OBJECT
public:
lfoController( model * _parent );
LfoController( model * _parent );
virtual ~lfoController();
virtual ~LfoController();
virtual void saveSettings( QDomDocument & _doc, QDomElement & _this );
virtual void loadSettings( const QDomElement & _this );
@@ -56,7 +56,7 @@ public:
public slots:
virtual controllerDialog * createDialog( QWidget * _parent );
virtual ControllerDialog * createDialog( QWidget * _parent );
protected:
@@ -80,24 +80,24 @@ protected:
protected slots:
void updateSampleFunction( void );
friend class lfoControllerDialog;
friend class LfoControllerDialog;
} ;
class lfoControllerDialog : public controllerDialog
class LfoControllerDialog : public ControllerDialog
{
Q_OBJECT
public:
lfoControllerDialog( controller * _controller, QWidget * _parent );
virtual ~lfoControllerDialog();
LfoControllerDialog( Controller * _controller, QWidget * _parent );
virtual ~LfoControllerDialog();
protected:
virtual void contextMenuEvent( QContextMenuEvent * _me );
virtual void modelChanged( void );
lfoController * m_lfo;
LfoController * m_lfo;
knob * m_baseKnob;
tempoSyncKnob * m_speedKnob;

View File

@@ -1,5 +1,5 @@
/*
* midi_controller.h - A controller to receive MIDI control-changes
* MidiController.h - A controller to receive MIDI control-changes
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
*
@@ -28,7 +28,7 @@
#include <QtGui/QWidget>
#include "automatable_model.h"
#include "controller.h"
#include "Controller.h"
#include "midi_event_processor.h"
#include "midi_port.h"
@@ -36,12 +36,12 @@
class midiPort;
class midiController : public controller, public midiEventProcessor
class MidiController : public Controller, public MidiEventProcessor
{
Q_OBJECT
public:
midiController( model * _parent );
virtual ~midiController();
MidiController( model * _parent );
virtual ~MidiController();
virtual void processInEvent( const midiEvent & _me,
const midiTime & _time );
@@ -61,7 +61,7 @@ public:
public slots:
virtual controllerDialog * createDialog( QWidget * _parent );
virtual ControllerDialog * createDialog( QWidget * _parent );
void updateName( void );
@@ -75,8 +75,8 @@ protected:
float m_lastValue;
friend class controllerConnectionDialog;
friend class autoDetectMidiController;
friend class ControllerConnectionDialog;
friend class AutoDetectMidiController;
} ;

View File

@@ -1,7 +1,7 @@
/*
* peak_controller.h - peak-controller class
* PeakController.h - peak-controller class
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
* Copyright (c) 2008-2009 Paul Giblock <drfaygo/at/gmail.com>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -29,8 +29,8 @@
#include "mv_base.h"
#include "automatable_model.h"
#include "controller.h"
#include "controller_dialog.h"
#include "Controller.h"
#include "ControllerDialog.h"
class automatableButtonGroup;
class knob;
@@ -39,15 +39,15 @@ class peakControllerEffect;
typedef QVector<peakControllerEffect *> peakControllerEffectVector;
class EXPORT peakController : public controller
class EXPORT PeakController : public Controller
{
Q_OBJECT
public:
peakController( model * _parent,
PeakController( model * _parent,
peakControllerEffect *_peak_effect = NULL );
virtual ~peakController();
virtual ~PeakController();
virtual void saveSettings( QDomDocument & _doc, QDomElement & _this );
virtual void loadSettings( const QDomElement & _this );
@@ -58,7 +58,7 @@ public:
public slots:
virtual controllerDialog * createDialog( QWidget * _parent );
virtual ControllerDialog * createDialog( QWidget * _parent );
void handleDestroyedEffect( );
protected:
@@ -67,24 +67,24 @@ protected:
peakControllerEffect * m_peakEffect;
friend class peakControllerDialog;
friend class PeakControllerDialog;
} ;
class peakControllerDialog : public controllerDialog
class PeakControllerDialog : public ControllerDialog
{
Q_OBJECT
public:
peakControllerDialog( controller * _controller, QWidget * _parent );
virtual ~peakControllerDialog();
PeakControllerDialog( Controller * _controller, QWidget * _parent );
virtual ~PeakControllerDialog();
protected:
virtual void contextMenuEvent( QContextMenuEvent * _me );
virtual void paintEvent( QPaintEvent * _pe );
virtual void modelChanged( void );
peakController * m_peakController;
PeakController * m_peakController;
} ;

View File

@@ -118,7 +118,7 @@ const int kVstTempoValid = 1 << 10;
const int kVstTransportPlaying = 1 << 1;
class remoteVstPlugin;
class RemoteVstPlugin;
class VstMidiEvent
@@ -221,7 +221,7 @@ public:
// flags 24-27
int flags;
// Fill somewhere 28-2b
remoteVstPlugin * user;
RemoteVstPlugin * user;
// Zeroes 2c-2f 30-33 34-37 38-3b
char empty3[4 + 4 + 4 + 4];
// 1.0f 3c-3f

View File

@@ -59,7 +59,7 @@
class controllerConnection;
class ControllerConnection;
class EXPORT automatableModel : public model, public journallingObject
@@ -94,13 +94,13 @@ public:
bool isAutomated( void ) const;
inline controllerConnection * getControllerConnection( void ) const
inline ControllerConnection * getControllerConnection( void ) const
{
return m_controllerConnection;
}
void setControllerConnection( controllerConnection * _c );
void setControllerConnection( ControllerConnection * _c );
template<class T>
@@ -250,7 +250,7 @@ private:
bool m_hasLinkedModels;
controllerConnection * m_controllerConnection;
ControllerConnection * m_controllerConnection;
static float __copiedValue;

View File

@@ -46,7 +46,7 @@ class projectNotes;
class song;
class songEditor;
class ladspa2LMMS;
class controllerRackView;
class ControllerRackView;
class EXPORT engine
@@ -142,7 +142,7 @@ public:
return( s_dummyTC );
}
static controllerRackView * getControllerRackView( void )
static ControllerRackView * getControllerRackView( void )
{
return( s_controllerRackView );
}
@@ -171,7 +171,7 @@ private:
static bbTrackContainer * s_bbTrackContainer;
static projectJournal * s_projectJournal;
static dummyTrackContainer * s_dummyTC;
static controllerRackView * s_controllerRackView;
static ControllerRackView * s_controllerRackView;
// GUI
static mainWindow * s_mainWindow;

View File

@@ -2,8 +2,8 @@
* instrument.h - declaration of class instrument, which provides a
* standard interface for all instrument plugins
*
* Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
@@ -23,7 +23,6 @@
*
*/
#ifndef _INSTRUMENT_H
#define _INSTRUMENT_H
@@ -80,20 +79,20 @@ public:
// if no envelope is active - such instruments can re-implement this
// method for returning how many frames they at least like to have for
// release
virtual f_cnt_t desiredReleaseFrames( void ) const
virtual f_cnt_t desiredReleaseFrames() const
{
return 0;
}
// return false if instrument is not bendable
inline virtual bool isBendable( void ) const
inline virtual bool isBendable() const
{
return true;
}
// return true if instruments reacts to MIDI events passed to
// handleMidiEvent() rather than playNote() & Co
inline virtual bool isMidiBased( void ) const
inline virtual bool isMidiBased() const
{
return false;
}
@@ -106,7 +105,7 @@ public:
return false;
}
virtual QString fullDisplayName( void ) const;
virtual QString fullDisplayName() const;
// --------------------------------------------------------------------
// provided functions:
@@ -121,7 +120,7 @@ public:
protected:
inline instrumentTrack * getInstrumentTrack( void ) const
inline instrumentTrack * getInstrumentTrack() const
{
return m_instrumentTrack;
}
@@ -137,5 +136,4 @@ private:
} ;
#endif

View File

@@ -34,6 +34,7 @@
#include "instrument_sound_shaping.h"
#include "midi_event_processor.h"
#include "midi_port.h"
#include "note_play_handle.h"
#include "piano.h"
#include "track.h"
@@ -53,12 +54,11 @@ class midiPortMenu;
class multimediaProject;
class notePlayHandle;
class pluginView;
class presetPreviewPlayHandle;
class tabWidget;
class trackLabelButton;
class EXPORT instrumentTrack : public track, public midiEventProcessor
class EXPORT instrumentTrack : public track, public MidiEventProcessor
{
Q_OBJECT
mapPropertyFromModel(int,getVolume,setVolume,m_volumeModel);
@@ -76,7 +76,8 @@ public:
const midiTime & _time );
virtual void processOutEvent( const midiEvent & _me,
const midiTime & _time );
// silence all running notes played by this track
void silenceAllNotes();
f_cnt_t beatLen( notePlayHandle * _n ) const;
@@ -195,8 +196,6 @@ protected:
{
return "instrumenttrack";
}
// invalidates all note-play-handles linked to this instrument
void invalidateAllMyNPH( void );
protected slots:
@@ -213,8 +212,7 @@ private:
intModel m_baseNoteModel;
QList<notePlayHandle *> m_processHandles;
NotePlayHandleList m_processHandles;
floatModel m_volumeModel;
floatModel m_panningModel;
@@ -233,7 +231,6 @@ private:
friend class instrumentTrackView;
friend class instrumentTrackWindow;
friend class notePlayHandle;
friend class presetPreviewPlayHandle;
friend class flpImport;
} ;

View File

@@ -2,7 +2,7 @@
* midi.h - constants, structs etc. concerning MIDI
*
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
@@ -22,7 +22,6 @@
*
*/
#ifndef _MIDI_H
#define _MIDI_H

View File

@@ -1,8 +1,8 @@
/*
* midi_event_processor.h - base-class for midi-processing classes
*
* Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
@@ -22,7 +22,6 @@
*
*/
#ifndef _MIDI_EVENT_PROCESSOR_H
#define _MIDI_EVENT_PROCESSOR_H
@@ -31,14 +30,14 @@ class midiTime;
// all classes being able to process MIDI-events should inherit from this
class midiEventProcessor
class MidiEventProcessor
{
public:
inline midiEventProcessor( void )
inline MidiEventProcessor()
{
}
virtual inline ~midiEventProcessor()
virtual inline ~MidiEventProcessor()
{
}

View File

@@ -36,7 +36,7 @@
class midiClient;
class midiEventProcessor;
class MidiEventProcessor;
class midiPortMenu;
class midiTime;
@@ -74,30 +74,34 @@ public:
midiPort( const QString & _name,
midiClient * _mc,
midiEventProcessor * _mep,
MidiEventProcessor * _mep,
model * _parent = NULL,
Modes _mode = Disabled );
virtual ~midiPort();
void setName( const QString & _name );
inline Modes mode( void ) const
inline Modes mode() const
{
return( m_mode );
return m_mode;
}
void setMode( Modes _mode );
inline bool inputEnabled( void ) const
inline bool inputEnabled() const
{
return( mode() == Input || mode() == Duplex );
return mode() == Input || mode() == Duplex;
}
inline bool outputEnabled( void ) const
inline bool outputEnabled() const
{
return( mode() == Output || mode() == Duplex );
return mode() == Output || mode() == Duplex;
}
inline int realOutputChannel() const
{
return outputChannel() - 1;
}
void processInEvent( const midiEvent & _me, const midiTime & _time );
void processOutEvent( const midiEvent & _me, const midiTime & _time );
@@ -106,9 +110,9 @@ public:
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
virtual void loadSettings( const QDomElement & _this );
virtual QString nodeName( void ) const
virtual QString nodeName() const
{
return( "midiport" );
return "midiport";
}
void subscribeReadablePort( const QString & _port,
@@ -116,14 +120,14 @@ public:
void subscribeWritablePort( const QString & _port,
bool _subscribe = TRUE );
const map & readablePorts( void ) const
const map & readablePorts() const
{
return( m_readablePorts );
return m_readablePorts;
}
const map & writablePorts( void ) const
const map & writablePorts() const
{
return( m_writablePorts );
return m_writablePorts;
}
midiPortMenu * m_readablePortsMenu;
@@ -131,18 +135,18 @@ public:
public slots:
void updateMidiPortMode( void );
void updateMidiPortMode();
private slots:
void updateReadablePorts( void );
void updateWritablePorts( void );
void updateOutputProgram( void );
void updateReadablePorts();
void updateWritablePorts();
void updateOutputProgram();
private:
midiClient * m_midiClient;
midiEventProcessor * m_midiEventProcessor;
MidiEventProcessor * m_midiEventProcessor;
Modes m_mode;
@@ -160,14 +164,14 @@ private:
map m_writablePorts;
friend class controllerConnectionDialog;
friend class ControllerConnectionDialog;
friend class instrumentMidiIOView;
signals:
void readablePortsChanged( void );
void writablePortsChanged( void );
void modeChanged( void );
void readablePortsChanged();
void writablePortsChanged();
void modeChanged();
} ;

View File

@@ -135,7 +135,7 @@ public:
static int stepsPerTact( void )
{
return ticksPerTact() / DefaultBeatsPerTact;
return qMax( 1, ticksPerTact() / DefaultBeatsPerTact );
}
static void setTicksPerTact( tick_t _tpt )

View File

@@ -234,7 +234,7 @@ public:
void removePlayHandle( playHandle * _ph );
inline playHandleVector & playHandles( void )
inline PlayHandleList & playHandles( void )
{
return m_playHandles;
}
@@ -443,8 +443,8 @@ private:
QWaitCondition m_queueReadyWaitCond;
playHandleVector m_playHandles;
constPlayHandleVector m_playHandlesToRemove;
PlayHandleList m_playHandles;
ConstPlayHandleList m_playHandlesToRemove;
struct qualitySettings m_qualitySettings;
float m_masterGain;

View File

@@ -1,7 +1,7 @@
/*
* mmp.h - class for reading and writing multimedia-project-files
*
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -52,9 +52,8 @@ public:
} ;
multimediaProject( const QString & _in_file_name,
bool _is_filename = TRUE,
bool _upgrade = TRUE );
multimediaProject( const QString & _fileName );
multimediaProject( const QByteArray & _data );
multimediaProject( ProjectTypes _project_type );
virtual ~multimediaProject();
@@ -76,8 +75,6 @@ public:
return( m_type );
}
static ProjectTypes typeOfFile( const QString & _fn );
private:
static ProjectTypes type( const QString & _type_name );
@@ -87,6 +84,8 @@ private:
void upgrade( void );
void loadData( const QByteArray & _data, const QString & _sourceFile );
struct EXPORT typeDescStruct
{

View File

@@ -243,7 +243,7 @@ private:
} ;
typedef QVector<note *> noteVector;
typedef QVector<note *> NoteVector;
#endif

View File

@@ -2,7 +2,7 @@
* note_play_handle.h - declaration of class notePlayHandle which is needed
* by LMMS-Play-Engine
*
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -30,16 +30,16 @@
#include "lmmsconfig.h"
#include "mixer.h"
#include "note.h"
#include "instrument.h"
#include "instrument_track.h"
#include "engine.h"
#include "track.h"
class instrumentTrack;
class notePlayHandle;
template<ch_cnt_t=DEFAULT_CHANNELS> class basicFilters;
typedef QVector<notePlayHandle *> notePlayHandleVector;
typedef QVector<const notePlayHandle *> constNotePlayHandleVector;
typedef QList<notePlayHandle *> NotePlayHandleList;
typedef QList<const notePlayHandle *> ConstNotePlayHandleList;
class EXPORT notePlayHandle : public playHandle, public note
@@ -79,55 +79,6 @@ public:
return( m_released && framesLeft() <= 0 );
}
bool willFinishThisPeriod( void ) const
{
f_cnt_t rftd = m_releaseFramesToDo;
f_cnt_t fbr = m_framesBeforeRelease;
f_cnt_t rfd = m_releaseFramesDone;
if( m_released == TRUE )
{
f_cnt_t todo = engine::getMixer()->framesPerPeriod();
if( isArpeggioBaseNote() )
{
rftd = rfd + 2 *
engine::getMixer()->framesPerPeriod();
}
if( fbr )
{
if( fbr <=
engine::getMixer()->framesPerPeriod() )
{
todo -= fbr;
fbr = 0;
}
else
{
todo = 0;
fbr -=
engine::getMixer()->framesPerPeriod();
}
}
if( todo && rfd < rftd )
{
if( rftd - rfd >= todo )
{
rfd += todo;
}
else
{
rfd = rftd;
}
}
}
if( isArpeggioBaseNote() && m_subNotes.size() == 0 )
{
rfd = rftd;
}
return( ( m_released && fbr == 0 && rfd >= rftd ) );
}
f_cnt_t framesLeft( void ) const;
inline fpp_t framesLeftForCurrentPeriod( void ) const
@@ -202,11 +153,7 @@ public:
}
// returns whether note is base-note for arpeggio
inline bool isArpeggioBaseNote( void ) const
{
return( isBaseNote() && ( m_partOfArpeggio ||
m_instrumentTrack->arpeggiatorEnabled() ) );
}
bool isArpeggioBaseNote( void ) const;
inline bool isMuted( void ) const
{
@@ -219,15 +166,15 @@ public:
// belonging to this instrument-track - used by arpeggiator
int index( void ) const;
// note-play-handles belonging to given channel, if _all_ph = TRUE,
// note-play-handles belonging to given channel, if _all_ph = true,
// also released note-play-handles are returned
static constNotePlayHandleVector nphsOfInstrumentTrack(
const instrumentTrack * _ct, bool _all_ph = FALSE );
static ConstNotePlayHandleList nphsOfInstrumentTrack(
const instrumentTrack * _ct, bool _all_ph = false );
// return whether given note-play-handle is equal to *this
bool operator==( const notePlayHandle & _nph ) const;
bool bbTrackMuted( void )
inline bool bbTrackMuted( void )
{
return( m_bbTrack && m_bbTrack->isMuted() );
}
@@ -288,7 +235,7 @@ private:
// played after release
f_cnt_t m_releaseFramesDone; // number of frames done after
// release of note
notePlayHandleVector m_subNotes;// used for chords and arpeggios
NotePlayHandleList m_subNotes; // used for chords and arpeggios
volatile bool m_released; // indicates whether note is released
bool m_baseNote; // indicates whether note is a
// base-note (i.e. no sub-note)

View File

@@ -3,7 +3,7 @@
* panning of a note
*
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
@@ -41,9 +41,10 @@ inline stereoVolumeVector panningToVolumeVector( panning_t _p,
return v;
}
inline Sint16 panningToMidi( panning_t _p )
inline int panningToMidi( panning_t _p )
{
return MidiMinPanning + (panning_t) (
return MidiMinPanning + (int) (
( (float)( _p - PanningLeft ) ) /
( (float)( PanningRight - PanningLeft ) ) *
( (float)( MidiMaxPanning - MidiMinPanning ) ) );

View File

@@ -78,7 +78,7 @@ public:
void rearrangeAllNotes( void );
void clearNotes( void );
inline const noteVector & notes( void )
inline const NoteVector & notes( void ) const
{
return m_notes;
}
@@ -154,7 +154,7 @@ private:
PatternTypes m_patternType;
// data-stuff
noteVector m_notes;
NoteVector m_notes;
int m_steps;
// pattern freezing

View File

@@ -109,7 +109,7 @@ protected:
int _width, note * _n );
void removeSelection( void );
void selectAll( void );
void getSelectedNotes( noteVector & _selected_notes );
void getSelectedNotes( NoteVector & _selected_notes );
protected slots:
@@ -150,7 +150,7 @@ private:
ModeDraw,
ModeErase,
ModeSelect,
ModeOpen
ModeEditDetuning,
};
enum actions
@@ -306,13 +306,12 @@ private:
timeLine * m_timeLine;
bool m_scrollBack;
void copy_to_clipboard( const noteVector & _notes ) const;
void copy_to_clipboard( const NoteVector & _notes ) const;
void drawDetuningInfo( QPainter & _p, note * _n, int _x, int _y );
bool mouseOverNote( void );
note * noteUnderMouse( void );
noteVector::const_iterator noteIteratorUnderMouse( void );
// turn a selection rectangle into selected notes
void computeSelectedNotes( bool shift );
void clearSelectedNotes( void );

View File

@@ -1,8 +1,8 @@
/*
* play_handle.h - base-class playHandle - core of rendering engine
*
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
@@ -22,7 +22,6 @@
*
*/
#ifndef _PLAY_HANDLE_H
#define _PLAY_HANDLE_H
@@ -98,8 +97,8 @@ private:
} ;
typedef QVector<playHandle *> playHandleVector;
typedef QVector<const playHandle *> constPlayHandleVector;
typedef QList<playHandle *> PlayHandleList;
typedef QList<const playHandle *> ConstPlayHandleList;
#endif

View File

@@ -48,7 +48,7 @@ public:
static void init( void );
static void cleanup( void );
static constNotePlayHandleVector nphsOfInstrumentTrack(
static ConstNotePlayHandleList nphsOfInstrumentTrack(
const instrumentTrack * _ct );

View File

@@ -36,6 +36,16 @@
#include <cassert>
#ifdef LMMS_BUILD_WIN32
#define USE_QT_SEMAPHORES
#define USE_QT_SHMEM
#endif
#ifdef LMMS_BUILD_APPLE
#define USE_QT_SEMAPHORES
#endif
#ifdef USE_QT_SEMAPHORES
#ifdef LMMS_HAVE_PROCESS_H
#include <process.h>
@@ -44,22 +54,38 @@
#include <Qt/qglobal.h>
#if QT_VERSION >= 0x040400
#include <QtCore/QSharedMemory>
#include <QtCore/QSystemSemaphore>
#else
#error win32-build requires at least Qt 4.4.0
#error building LMMS on this platform requires at least Qt 4.4.0
#endif
typedef int32_t key_t;
#else
#define USE_NATIVE_SHMEM
#else /* USE_QT_SEMAPHORES */
#ifdef LMMS_HAVE_SYS_IPC_H
#include <sys/ipc.h>
#endif
#ifdef LMMS_HAVE_SEMAPHORE_H
#include <semaphore.h>
#endif
#endif
#ifdef USE_QT_SHMEM
#include <Qt/qglobal.h>
#if QT_VERSION >= 0x040400
#include <QtCore/QSharedMemory>
#else
#error building LMMS on this platform requires at least Qt 4.4.0
#endif
typedef int32_t key_t;
#else /* USE_QT_SHMEM */
#ifdef LMMS_HAVE_SYS_SHM_H
#include <sys/shm.h>
#endif
@@ -71,9 +97,6 @@ typedef int32_t key_t;
#endif
#ifdef LMMS_HAVE_SEMAPHORE_H
#include <semaphore.h>
#endif
#ifdef LMMS_HAVE_LOCALE_H
#include <locale.h>
@@ -102,7 +125,7 @@ class shmFifo
// and 64 bit platforms
union sem32_t
{
#ifdef LMMS_HAVE_SEMAPHORE_H
#ifndef USE_QT_SEMAPHORES
sem_t sem;
#endif
int semKey;
@@ -124,13 +147,13 @@ public:
m_invalid( false ),
m_master( true ),
m_shmKey( 0 ),
#ifdef USE_NATIVE_SHMEM
m_shmID( -1 ),
#else
#ifdef USE_QT_SHMEM
m_shmObj(),
#else
m_shmID( -1 ),
#endif
m_data( NULL ),
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
m_dataSem( QString::null ),
m_messageSem( QString::null ),
#else
@@ -139,13 +162,7 @@ public:
#endif
m_lockDepth( 0 )
{
#ifdef USE_NATIVE_SHMEM
while( ( m_shmID = shmget( ++m_shmKey, sizeof( shmData ),
IPC_CREAT | IPC_EXCL | 0600 ) ) == -1 )
{
}
m_data = (shmData *) shmat( m_shmID, 0, 0 );
#else
#ifdef USE_QT_SHMEM
do
{
m_shmObj.setKey( QString( "%1" ).arg( ++m_shmKey ) );
@@ -153,10 +170,16 @@ public:
} while( m_shmObj.error() != QSharedMemory::NoError );
m_data = (shmData *) m_shmObj.data();
#else
while( ( m_shmID = shmget( ++m_shmKey, sizeof( shmData ),
IPC_CREAT | IPC_EXCL | 0600 ) ) == -1 )
{
}
m_data = (shmData *) shmat( m_shmID, 0, 0 );
#endif
assert( m_data != NULL );
m_data->startPtr = m_data->endPtr = 0;
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
static int k = 0;
m_data->dataSem.semKey = ( getpid()<<10 ) + ++k;
m_data->messageSem.semKey = ( getpid()<<10 ) + ++k;
@@ -187,13 +210,13 @@ public:
m_invalid( false ),
m_master( false ),
m_shmKey( 0 ),
#ifdef USE_NATIVE_SHMEM
m_shmID( shmget( _shm_key, 0, 0 ) ),
#else
#ifdef USE_QT_SHMEM
m_shmObj( QString::number( _shm_key ) ),
#else
m_shmID( shmget( _shm_key, 0, 0 ) ),
#endif
m_data( NULL ),
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
m_dataSem( QString::null ),
m_messageSem( QString::null ),
#else
@@ -202,19 +225,19 @@ public:
#endif
m_lockDepth( 0 )
{
#ifdef USE_NATIVE_SHMEM
if( m_shmID != -1 )
{
m_data = (shmData *) shmat( m_shmID, 0, 0 );
}
#else
#ifdef USE_QT_SHMEM
if( m_shmObj.attach() )
{
m_data = (shmData *) m_shmObj.data();
}
#else
if( m_shmID != -1 )
{
m_data = (shmData *) shmat( m_shmID, 0, 0 );
}
#endif
assert( m_data != NULL );
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
m_dataSem.setKey( QString::number( m_data->dataSem.semKey ) );
m_messageSem.setKey( QString::number(
m_data->messageSem.semKey ) );
@@ -226,16 +249,16 @@ public:
~shmFifo()
{
#ifdef USE_NATIVE_SHMEM
#ifndef USE_QT_SHMEM
shmdt( m_data );
#endif
// master?
if( m_master )
{
#ifdef USE_NATIVE_SHMEM
#ifndef USE_QT_SHMEM
shmctl( m_shmID, IPC_RMID, NULL );
#endif
#ifndef LMMS_BUILD_WIN32
#ifndef USE_QT_SEMAPHORES
sem_destroy( m_dataSem );
sem_destroy( m_messageSem );
#endif
@@ -263,7 +286,7 @@ public:
{
if( !isInvalid() && ++m_lockDepth == 1 )
{
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
m_dataSem.acquire();
#else
sem_wait( m_dataSem );
@@ -278,7 +301,7 @@ public:
{
if( --m_lockDepth == 0 )
{
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
m_dataSem.release();
#else
sem_post( m_dataSem );
@@ -292,7 +315,7 @@ public:
{
if( !isInvalid() )
{
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
m_messageSem.acquire();
#else
sem_wait( m_messageSem );
@@ -303,7 +326,7 @@ public:
// increase message-semaphore
inline void messageSent( void )
{
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
m_messageSem.release();
#else
sem_post( m_messageSem );
@@ -353,7 +376,7 @@ public:
{
return false;
}
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
lock();
const bool empty = ( m_data->startPtr == m_data->endPtr );
unlock();
@@ -447,14 +470,14 @@ private:
volatile bool m_invalid;
bool m_master;
key_t m_shmKey;
#ifdef USE_NATIVE_SHMEM
int m_shmID;
#else
#ifdef USE_QT_SHMEM
QSharedMemory m_shmObj;
#else
int m_shmID;
#endif
size_t m_shmSize;
shmData * m_data;
#ifdef LMMS_BUILD_WIN32
#ifdef USE_QT_SEMAPHORES
QSystemSemaphore m_dataSem;
QSystemSemaphore m_messageSem;
#else
@@ -755,10 +778,10 @@ private:
QMutex m_commMutex;
bool m_splitChannels;
#ifdef USE_NATIVE_SHMEM
int m_shmID;
#else
#ifdef USE_QT_SHMEM
QSharedMemory m_shmObj;
#else
int m_shmID;
#endif
size_t m_shmSize;
float * m_shm;
@@ -845,7 +868,7 @@ private:
void setShmKey( key_t _key, int _size );
void doProcessing( void );
#ifndef USE_NATIVE_SHMEM
#ifdef USE_QT_SHMEM
QSharedMemory m_shmObj;
#endif
float * m_shm;
@@ -972,7 +995,7 @@ remotePluginBase::message remotePluginBase::waitForMessage(
remotePluginClient::remotePluginClient( key_t _shm_in, key_t _shm_out ) :
remotePluginBase( new shmFifo( _shm_in ),
new shmFifo( _shm_out ) ),
#ifndef USE_NATIVE_SHMEM
#ifdef USE_QT_SHMEM
m_shmObj(),
#endif
m_shm( NULL ),
@@ -992,7 +1015,7 @@ remotePluginClient::~remotePluginClient()
{
sendMessage( IdQuit );
#ifdef USE_NATIVE_SHMEM
#ifndef USE_QT_SHMEM
shmdt( m_shm );
#endif
}
@@ -1063,7 +1086,17 @@ bool remotePluginClient::processMessage( const message & _m )
void remotePluginClient::setShmKey( key_t _key, int _size )
{
#ifdef USE_NATIVE_SHMEM
#ifdef USE_QT_SHMEM
m_shmObj.setKey( QString::number( _key ) );
if( m_shmObj.attach() )
{
m_shm = (float *) m_shmObj.data();
}
else
{
fprintf( stderr, "failed getting shared memory\n" );
}
#else
if( m_shm != NULL )
{
shmdt( m_shm );
@@ -1085,16 +1118,6 @@ void remotePluginClient::setShmKey( key_t _key, int _size )
{
m_shm = (float *) shmat( shm_id, 0, 0 );
}
#else
m_shmObj.setKey( QString::number( _key ) );
if( m_shmObj.attach() )
{
m_shm = (float *) m_shmObj.data();
}
else
{
fprintf( stderr, "failed getting shared memory\n" );
}
#endif
}
@@ -1115,4 +1138,6 @@ void remotePluginClient::doProcessing( void )
#endif
#define QSTR_TO_STDSTR(s) std::string( s.toUtf8().constData() )
#endif

View File

@@ -2,7 +2,7 @@
* sample_buffer.h - container-class sampleBuffer
*
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
@@ -49,7 +49,7 @@ public:
class EXPORT handleState
{
public:
handleState( bool _varying_pitch = FALSE );
handleState( bool _varying_pitch = false );
virtual ~handleState();
@@ -66,16 +66,16 @@ public:
// constructor which either loads sample _audio_file or decodes
// base64-data out of string
sampleBuffer( const QString & _audio_file = QString(),
bool _is_base64_data = FALSE );
bool _is_base64_data = false );
sampleBuffer( const sampleFrame * _data, const f_cnt_t _frames );
sampleBuffer( const f_cnt_t _frames );
virtual ~sampleBuffer();
bool play( sampleFrame * _ab, handleState * _state,
const fpp_t _frames,
const float _freq,
const bool _looped = FALSE );
const bool _looped = false );
void visualize( QPainter & _p, const QRect & _dr, const QRect & _clip );
inline void visualize( QPainter & _p, const QRect & _dr )
@@ -83,17 +83,17 @@ public:
visualize( _p, _dr, _dr );
}
inline const QString & audioFile( void ) const
inline const QString & audioFile() const
{
return m_audioFile;
}
inline f_cnt_t startFrame( void ) const
inline f_cnt_t startFrame() const
{
return m_startFrame;
}
inline f_cnt_t endFrame( void ) const
inline f_cnt_t endFrame() const
{
return m_endFrame;
}
@@ -112,22 +112,22 @@ public:
m_varLock.unlock();
}
inline f_cnt_t frames( void ) const
inline f_cnt_t frames() const
{
return m_frames;
}
inline float amplification( void ) const
inline float amplification() const
{
return m_amplification;
}
inline bool reversed( void ) const
inline bool reversed() const
{
return m_reversed;
}
inline float frequency( void ) const
inline float frequency() const
{
return m_frequency;
}
@@ -146,12 +146,12 @@ public:
m_varLock.unlock();
}
inline const sampleFrame * data( void ) const
inline const sampleFrame * data() const
{
return m_data;
}
QString openAudioFile( void ) const;
QString openAudioFile() const;
QString & toBase64( QString & _dst ) const;
@@ -170,7 +170,7 @@ public:
}
void normalizeSampleRate( const sample_rate_t _src_sr,
bool _keep_settings = FALSE );
bool _keep_settings = false );
inline sample_t userWaveSample( const float _sample ) const
{
@@ -207,7 +207,7 @@ public slots:
private:
void update( bool _keep_settings = FALSE );
void update( bool _keep_settings = false );
f_cnt_t decodeSampleSF( const char * _f, int_sample_t * & _buf,
@@ -222,7 +222,6 @@ private:
ch_cnt_t & _channels,
sample_rate_t & _sample_rate );
QString m_audioFile;
sampleFrame * m_origData;
f_cnt_t m_origFrames;
@@ -245,7 +244,7 @@ private:
signals:
void sampleUpdated( void );
void sampleUpdated();
} ;

View File

@@ -30,7 +30,7 @@
#include "track_container.h"
#include "automatable_model.h"
#include "controller.h"
#include "Controller.h"
#include "meter_model.h"
@@ -177,11 +177,11 @@ public:
return false;
}
void addController( controller * _c );
void removeController( controller * _c );
void addController( Controller * _c );
void removeController( Controller * _c );
const controllerVector & controllers( void ) const
const ControllerVector & controllers( void ) const
{
return m_controllers;
}
@@ -270,7 +270,7 @@ private:
intModel m_masterVolumeModel;
intModel m_masterPitchModel;
controllerVector m_controllers;
ControllerVector m_controllers;
QString m_fileName;
@@ -308,7 +308,8 @@ private:
friend class engine;
friend class songEditor;
friend class controllerRackView;
friend class mainWindow;
friend class ControllerRackView;
signals:
void lengthChanged( int _tacts );

View File

@@ -41,7 +41,6 @@
class QMenu;
class QPushButton;
class bbTrack;
class pixmapButton;
class textFloat;
class track;

View File

@@ -17,7 +17,7 @@ BEGIN
VALUE "CompanyName", "LMMS Developers\0"
VALUE "FileDescription", "Linux MultiMedia Studio\0"
VALUE "FileVersion", "@VERSION@\0"
VALUE "LegalCopyright", "Copyright (c) 2004-2008 LMMS Developers\0"
VALUE "LegalCopyright", "Copyright (c) 2004-2009 LMMS Developers\0"
VALUE "OriginalFilename", "lmms.exe\0"
VALUE "ProductName", "LMMS\0"
VALUE "ProductVersion", "@VERSION@\0"

View File

@@ -1,7 +1,7 @@
/*
* audio_file_processor.cpp - instrument for using audio-files
*
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -41,8 +41,6 @@
#include "string_pair_drag.h"
#include "mmp.h"
#undef SINGLE_SOURCE_COMPILE
#include "embed.cpp"
@@ -212,13 +210,13 @@ pluginView * audioFileProcessor::instantiateView( QWidget * _parent )
void audioFileProcessor::setAudioFile( const QString & _audio_file,
bool _rename )
bool _rename )
{
// is current channel-name equal to previous-filename??
if( _rename &&
if( _rename &&
( getInstrumentTrack()->name() ==
QFileInfo( m_sampleBuffer.audioFile() ).fileName() ||
m_sampleBuffer.audioFile() == "" ) )
m_sampleBuffer.audioFile().isEmpty() ) )
{
// then set it to new one
getInstrumentTrack()->setName( QFileInfo( _audio_file
@@ -424,7 +422,7 @@ void audioFileProcessorView::dropEvent( QDropEvent * _de )
}
else if( type == QString( "tco_%1" ).arg( track::SampleTrack ) )
{
multimediaProject mmp( value, FALSE );
multimediaProject mmp( value.toUtf8() );
castModel<audioFileProcessor>()->setAudioFile( mmp.content().
firstChild().toElement().attribute( "src" ) );
_de->accept();

View File

@@ -1,7 +1,7 @@
/*
* bass_booster.cpp - bass-booster-effect-plugin
*
* Copyright (c) 2006-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -25,8 +25,6 @@
#include "bass_booster.h"
#undef SINGLE_SOURCE_COMPILE
#include "embed.cpp"

View File

@@ -39,7 +39,6 @@
#include "tooltip.h"
#include "song.h"
#undef SINGLE_SOURCE_COMPILE
#include "embed.cpp"
extern "C"

View File

@@ -1,10 +1,10 @@
/*
* flp_import.cpp - support for importing FLP-files
*
* Copyright (c) 2006-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
@@ -22,7 +22,6 @@
*
*/
#include <QtXml/QDomDocument>
#include <QtGui/QApplication>
#include <QtGui/QProgressDialog>
@@ -105,9 +104,9 @@ static void dump_mem( const void * buffer, uint n_bytes )
uchar * cp = (uchar *) buffer;
for( uint k = 0; k < n_bytes; ++k )
{
printf( "%02x ", (unsigned int)cp[k] );//( cp[k] > 31 || cp[k] < 7 ) ? cp[k] : '.' );
qDebug( "%02x ", (unsigned int)cp[k] );//( cp[k] > 31 || cp[k] < 7 ) ? cp[k] : '.' );
}
printf( "\n" );
qDebug( "\n" );
}
@@ -586,6 +585,8 @@ flpImport::~flpImport()
}
bool flpImport::tryImport( trackContainer * _tc )
{
const int mappedFilter[] =
@@ -676,21 +677,21 @@ bool flpImport::tryImport( trackContainer * _tc )
if( readID() != makeID( 'F', 'L', 'h', 'd' ) )
{
printf( "flpImport::tryImport(): not a valid FL project\n" );
qWarning( "flpImport::tryImport(): not a valid FL project\n" );
return false;
}
const int header_len = read32LE();
if( header_len != 6 )
{
printf( "flpImport::tryImport(): invalid file format\n" );
qWarning( "flpImport::tryImport(): invalid file format\n" );
return false;
}
const int type = read16LE();
if( type != 0 )
{
printf( "flpImport::tryImport(): type %d format is not "
qWarning( "flpImport::tryImport(): type %d format is not "
"supported\n", type );
return false;
}
@@ -698,7 +699,7 @@ bool flpImport::tryImport( trackContainer * _tc )
p.numChannels = read16LE();
if( p.numChannels < 1 || p.numChannels > 1000 )
{
printf( "flpImport::tryImport(): invalid number of channels "
qWarning( "flpImport::tryImport(): invalid number of channels "
"(%d)\n", p.numChannels );
return false;
}
@@ -706,7 +707,7 @@ bool flpImport::tryImport( trackContainer * _tc )
const int ppq = read16LE();
if( ppq < 0 )
{
printf( "flpImport::tryImport(): invalid ppq\n" );
qWarning( "flpImport::tryImport(): invalid ppq\n" );
return false;
}
@@ -725,14 +726,14 @@ bool flpImport::tryImport( trackContainer * _tc )
const int len = read32LE();
if( file().atEnd() )
{
printf( "flpImport::tryImport(): unexpected "
"end of file\n" );
qWarning( "flpImport::tryImport(): unexpected "
"end of file\n" );
return false;
}
if( len < 0 || len >= 0x10000000 )
{
printf( "flpImport::tryImport(): invalid "
"chunk length %d\n", len );
qWarning( "flpImport::tryImport(): invalid "
"chunk length %d\n", len );
return false;
}
if( id == makeID( 'F', 'L', 'd', 't' ) )
@@ -753,7 +754,7 @@ bool flpImport::tryImport( trackContainer * _tc )
p.channels += FL_Channel();
}
printf( "channels: %d\n", p.numChannels );
qDebug( "channels: %d\n", p.numChannels );
char * text = NULL;
@@ -798,8 +799,8 @@ bool flpImport::tryImport( trackContainer * _tc )
text = new char[text_len+1];
if( readBlock( text, text_len ) <= 0 )
{
printf( "could not read string (len: %d)\n",
text_len );
qWarning( "could not read string (len: %d)\n",
text_len );
}
text[text_len] = 0;
@@ -815,32 +816,32 @@ bool flpImport::tryImport( trackContainer * _tc )
{
// BYTE EVENTS
case FLP_Byte:
printf( "undefined byte %d\n", data );
qDebug( "undefined byte %d\n", data );
break;
case FLP_NoteOn:
printf( "note on: %d\n", data );
qDebug( "note on: %d\n", data );
// data = pos how to handle?
break;
case FLP_Vol:
printf( "vol %d\n", data );
qDebug( "vol %d\n", data );
break;
case FLP_Pan:
printf( "pan %d\n", data );
qDebug( "pan %d\n", data );
break;
case FLP_LoopActive:
printf( "active loop: %d\n", data );
qDebug( "active loop: %d\n", data );
break;
case FLP_ShowInfo:
printf( "show info: %d\n", data );
qDebug( "show info: %d\n", data );
break;
case FLP_Shuffle:
printf( "shuffle: %d\n", data );
qDebug( "shuffle: %d\n", data );
break;
case FLP_MainVol:
@@ -848,11 +849,11 @@ bool flpImport::tryImport( trackContainer * _tc )
break;
case FLP_PatLength:
printf( "pattern length: %d\n", data );
qDebug( "pattern length: %d\n", data );
break;
case FLP_BlockLength:
printf( "block length: %d\n", data );
qDebug( "block length: %d\n", data );
break;
case FLP_UseLoopPoints:
@@ -860,11 +861,11 @@ bool flpImport::tryImport( trackContainer * _tc )
break;
case FLP_LoopType:
printf( "loop type: %d\n", data );
qDebug( "loop type: %d\n", data );
break;
case FLP_ChanType:
printf( "channel type: %d\n", data );
qDebug( "channel type: %d\n", data );
if( cc )
{
switch( data )
@@ -895,7 +896,7 @@ if( p.currentEffectChannel <= NumFxChannels )
// WORD EVENTS
case FLP_NewChan:
cur_channel = data;
printf( "new channel: %d\n", data );
qDebug( "new channel: %d\n", data );
break;
case FLP_NewPat:
@@ -915,7 +916,7 @@ if( p.currentEffectChannel <= NumFxChannels )
break;
case FLP_FX:
printf( "FX: %d\n", data );
qDebug( "FX: %d\n", data );
break;
case FLP_Fade_Stereo:
@@ -927,11 +928,11 @@ if( p.currentEffectChannel <= NumFxChannels )
{
cc->sampleReverseStereo = true;
}
printf( "fade stereo: %d\n", data );
qDebug( "fade stereo: %d\n", data );
break;
case FLP_CutOff:
printf( "cutoff (sample): %d\n", data );
qDebug( "cutoff (sample): %d\n", data );
break;
case FLP_PreAmp:
@@ -939,11 +940,11 @@ if( p.currentEffectChannel <= NumFxChannels )
break;
case FLP_Decay:
printf( "decay (sample): %d\n", data );
qDebug( "decay (sample): %d\n", data );
break;
case FLP_Attack:
printf( "attack (sample): %d\n", data );
qDebug( "attack (sample): %d\n", data );
break;
case FLP_MainPitch:
@@ -951,23 +952,23 @@ if( p.currentEffectChannel <= NumFxChannels )
break;
case FLP_Resonance:
printf( "reso (sample): %d\n", data );
qDebug( "reso (sample): %d\n", data );
break;
case FLP_LoopBar:
printf( "loop bar: %d\n", data );
qDebug( "loop bar: %d\n", data );
break;
case FLP_StDel:
printf( "stdel (delay?): %d\n", data );
qDebug( "stdel (delay?): %d\n", data );
break;
case FLP_FX3:
printf( "FX 3: %d\n", data );
qDebug( "FX 3: %d\n", data );
break;
case FLP_ShiftDelay:
printf( "shift delay: %d\n", data );
qDebug( "shift delay: %d\n", data );
break;
case FLP_Dot:
@@ -999,11 +1000,11 @@ if( p.currentEffectChannel <= NumFxChannels )
}
case FLP_FXSine:
printf( "fx sine: %d\n", data );
qDebug( "fx sine: %d\n", data );
break;
case FLP_CutCutBy:
printf( "cut cut by: %d\n", data );
qDebug( "cut cut by: %d\n", data );
break;
case FLP_MiddleNote:
@@ -1011,15 +1012,15 @@ if( p.currentEffectChannel <= NumFxChannels )
break;
case FLP_DelayReso:
printf( "delay resonance: %d\n", data );
qDebug( "delay resonance: %d\n", data );
break;
case FLP_Reverb:
printf( "reverb (sample): %d\n", data );
qDebug( "reverb (sample): %d\n", data );
break;
case FLP_IntStretch:
printf( "int stretch (sample): %d\n", data );
qDebug( "int stretch (sample): %d\n", data );
break;
// TEXT EVENTS
@@ -1082,7 +1083,7 @@ if( p.currentEffectChannel <= NumFxChannels )
case FLP_Text_Version:
{
printf( "FLP version: %s\n", text );
qDebug( "FLP version: %s\n", text );
p.versionString = text;
QStringList l = p.versionString.split( '.' );
p.version = ( l[0].toInt() << 8 ) +
@@ -1103,36 +1104,32 @@ if( p.currentEffectChannel <= NumFxChannels )
mappedPluginTypes[QString( text ).toLower()] );
if( t > FL_Plugin::EffectPlugin )
{
printf( "recognized new "
"effect %s\n", text );
p.effects.push_back(
FL_Effect( t ) );
qDebug( "recognized new effect %s\n", text );
p.effects.push_back( FL_Effect( t ) );
}
else if( cc )
{
printf( "recognized new "
"plugin %s\n", text );
qDebug( "recognized new plugin %s\n", text );
cc->pluginType = t;
}
last_plugin_type = t;
}
else
{
printf( "unsupported plugin: %s!\n",
text );
qDebug( "unsupported plugin: %s!\n", text );
}
break;
case FLP_Text_EffectChanName:
++p.currentEffectChannel;
if( p.currentEffectChannel <= NumFxChannels )
{
p.effectChannels[p.currentEffectChannel].name = text;
}
++p.currentEffectChannel;
if( p.currentEffectChannel <= NumFxChannels )
{
p.effectChannels[p.currentEffectChannel].name = text;
}
break;
case FLP_Text_Delay:
printf( "delay data: " );
qDebug( "delay data: " );
// pi[1] seems to be volume or similiar and
// needs to be divided
// by p.versionSpecificFactor
@@ -1140,30 +1137,27 @@ if( p.currentEffectChannel <= NumFxChannels )
break;
case FLP_Text_TS404Params:
if( cc && cc->pluginType ==
FL_Plugin::UnknownPlugin &&
if( cc && cc->pluginType == FL_Plugin::UnknownPlugin &&
cc->pluginSettings == NULL )
{
cc->pluginSettings = new char[text_len];
memcpy( cc->pluginSettings, text,
text_len );
memcpy( cc->pluginSettings, text, text_len );
cc->pluginSettingsLength = text_len;
cc->pluginType =
FL_Plugin::TS404;
cc->pluginType = FL_Plugin::TS404;
}
break;
case FLP_Text_NewPlugin:
if( last_plugin_type > FL_Plugin::EffectPlugin )
{
FL_Effect * e = &p.effects.last();
e->fxChannel = puc[0];
e->fxPos = puc[4];
printf( "new effect: " );
FL_Effect * e = &p.effects.last();
e->fxChannel = puc[0];
e->fxPos = puc[4];
qDebug( "new effect: " );
}
else
{
printf( "new plugin: " );
qDebug( "new plugin: " );
}
dump_mem( text, text_len );
break;
@@ -1176,7 +1170,7 @@ if( p.currentEffectChannel <= NumFxChannels )
text_len );
cc->pluginSettingsLength = text_len;
}
printf( "plugin params: " );
qDebug( "plugin params: " );
dump_mem( text, text_len );
break;
@@ -1194,7 +1188,7 @@ if( p.currentEffectChannel <= NumFxChannels )
cc->arpGate = ( pi[14] * 100.0f ) / 48.0f;
cc->arpEnabled = pi[10] > 0;
printf( "channel params: " );
qDebug( "channel params: " );
dump_mem( text, text_len );
break;
@@ -1235,7 +1229,7 @@ if( p.currentEffectChannel <= NumFxChannels )
// e.lfoAmount = pi[11] / 128.0f;
cc->envelopes.push_back( e );
printf( "envelope and lfo params:\n" );
qDebug( "envelope and lfo params:\n" );
dump_mem( text, text_len );
break;
@@ -1256,7 +1250,7 @@ if( p.currentEffectChannel <= NumFxChannels )
cc->filterCut *= 0.5f;
}
}
printf( "basic chan params: " );
qDebug( "basic chan params: " );
dump_mem( text, text_len );
break;
@@ -1269,7 +1263,7 @@ if( p.currentEffectChannel <= NumFxChannels )
{
cc->filterCut *= 0.5;
}
printf( "old filter params: " );
qDebug( "old filter params: " );
dump_mem( text, text_len );
break;
@@ -1277,7 +1271,7 @@ if( p.currentEffectChannel <= NumFxChannels )
{
const int bpae = 12;
const int imax = text_len / bpae;
printf( "automation data (%d items)\n", imax );
qDebug( "automation data (%d items)\n", imax );
for( int i = 0; i < imax; ++i )
{
FL_Automation a;
@@ -1289,8 +1283,9 @@ if( p.currentEffectChannel <= NumFxChannels )
if( a.channel >= 0 &&
a.channel < p.numChannels )
{
printf("add channel %d at %d val %d control:%d\n", a.channel, a.pos, a.value, a.control );
p.channels[a.channel].automationData += a;
qDebug( "add channel %d at %d val %d control:%d\n",
a.channel, a.pos, a.value, a.control );
p.channels[a.channel].automationData += a;
}
// dump_mem( text+i*bpae, bpae );
}
@@ -1315,15 +1310,22 @@ printf("add channel %d at %d val %d control:%d\n", a.channel, a.pos, a.value,
len /= (4*ppq) / DefaultTicksPerTact;
note n( len, pos, key, vol * 100 / 128,
pan*200 / 128 - 100 );
if( ch < p.numChannels )
{
p.channels[ch].notes.push_back( qMakePair( p.currentPattern, n ) );
printf( "note: " );
}
else
{
qDebug( "invalid " );
}
qDebug( "note: " );
dump_mem( text+i*bpn, bpn );
}
break;
}
case FLP_Text_ChanGroupName:
printf( "channel group name: %s\n", text );
qDebug( "channel group name: %s\n", text );
break;
// case 216: pi[2] /= p.versionSpecificFactor
@@ -1354,7 +1356,7 @@ p.effectChannels[ch].volume = ( val / p.versionSpecificFactor ) * 100 / 128;
}
else
{
printf("FX-ch: %d param: %x value:%x\n", ch, param, val );
qDebug( "FX-ch: %d param: %x value:%x\n", ch, param, val );
}
}
break;
@@ -1379,7 +1381,7 @@ if( pat > 2146 && pat <= 2278 ) // whatever these magic numbers are for...
}
else
{
printf( "playlist item: " );
qDebug( "unknown playlist item: " );
dump_mem( text+i*bpi, bpi );
}
}
@@ -1389,25 +1391,18 @@ else
default:
if( ev >= FLP_Text )
{
printf( "!! unhandled text (ev: %d, "
"len: %d): ",
ev, text_len );
qDebug( "!! unhandled text (ev: %d, len: %d): ",
ev, text_len );
dump_mem( text, text_len );
}
else
{
printf( "!! handling of FLP-event %d "
"not implemented yet "
"(data=%d).\n", ev, data );
qDebug( "!! handling of FLP-event %d not implemented yet "
"(data=%d).\n", ev, data );
}
break;
}
/* if( ev >= FLP_Text )
{
printf( "dump (ev: %d, len: %d): ", ev, text_len );
dump_mem( text, text_len );
}*/
}
}
// now create a project from FL_Project data structure
@@ -1424,7 +1419,7 @@ else
progressDialog.setMaximum( p.maxPatterns + p.channels.size() +
p.effects.size() );
p.effects.size() );
int cur_progress = 0;
// create BB tracks
@@ -1480,8 +1475,7 @@ else
case FL_Plugin::UnknownPlugin:
default:
it->instrumentPlugin =
t->loadInstrument(
"audiofileprocessor" );
t->loadInstrument( "audiofileprocessor" );
break;
}
processPluginParams( &( *it ) );
@@ -1504,8 +1498,7 @@ else
iss->m_filterResModel.minValue() );
iss->m_filterEnabledModel.setValue( it->filterEnabled );
for( QList<FL_Channel_Envelope>::iterator jt =
it->envelopes.begin();
for( QList<FL_Channel_Envelope>::iterator jt = it->envelopes.begin();
jt != it->envelopes.end(); ++jt )
{
if( jt->target != instrumentSoundShaping::NumTargets )
@@ -1554,8 +1547,7 @@ else
}
// process all notes
for( FL_Channel::noteVector::const_iterator jt =
it->notes.begin();
for( FL_Channel::noteVector::const_iterator jt = it->notes.begin();
jt != it->notes.end(); ++jt )
{
const int pat = jt->first;
@@ -1564,8 +1556,7 @@ else
{
continue;
}
pattern * p = dynamic_cast<pattern *>(
t->getTCO( pat ) );
pattern * p = dynamic_cast<pattern *>( t->getTCO( pat ) );
if( p != NULL )
{
p->addNote( jt->second, false );
@@ -1584,8 +1575,7 @@ else
{
case FL_Automation::ControlVolume:
m = t->volumeModel();
value *= ( 100.0f / 128.0f ) /
p.versionSpecificFactor;
value *= ( 100.0f / 128.0f ) / p.versionSpecificFactor;
break;
case FL_Automation::ControlPanning:
m = t->panningModel();
@@ -1613,9 +1603,9 @@ else
value = mappedFilter[jt->value];
break;
default:
printf( "handling automation data of "
"control %d not implemented "
"yet\n", jt->control );
qDebug( "handling automation data of "
"control %d not implemented "
"yet\n", jt->control );
break;
}
if( m )
@@ -1653,14 +1643,12 @@ p->putValue( jt->pos, value, false );
else
{
effKeys << effectKey( &( *it ), it->name );
}
}
for( int fx_ch = 0; fx_ch <= NumFxChannels ; ++fx_ch )
{
fxChannel * ch = engine::getFxMixer()->getEffectChannel(
fx_ch );
fxChannel * ch = engine::getFxMixer()->getEffectChannel( fx_ch );
if( !ch )
{
continue;
@@ -1723,9 +1711,8 @@ p->putValue( jt->pos, value, false );
continue;
}
effectChain * ec = &engine::getFxMixer()->
getEffectChannel( it->fxChannel )->
m_fxChain;
printf("adding %s to %d\n", effName.toAscii().constData(),
getEffectChannel( it->fxChannel )->m_fxChain;
qDebug( "adding %s to %d\n", effName.toUtf8().constData(),
it->fxChannel );
for( effectKeyList::iterator jt = effKeys.begin();
jt != effKeys.end(); ++jt )
@@ -1735,7 +1722,7 @@ p->putValue( jt->pos, value, false );
( jt->desc->sub_plugin_features != NULL &&
jt->name.contains( effName ) ) )
{
printf("instantiate %s\n", jt->desc->name );
qDebug( "instantiate %s\n", jt->desc->name );
effect * e = effect::instantiate(
jt->desc->name, ec, &( *jt ) );
ec->appendEffect( e );
@@ -1788,7 +1775,7 @@ p->putValue( jt->pos, value, false );
void flpImport::processPluginParams( FL_Channel * _ch )
{
printf( "plugin params for plugin %d (%d bytes): ", _ch->pluginType,
qDebug( "plugin params for plugin %d (%d bytes): ", _ch->pluginType,
_ch->pluginSettingsLength );
dump_mem( _ch->pluginSettings, _ch->pluginSettingsLength );
switch( _ch->pluginType )
@@ -1877,7 +1864,7 @@ void flpImport::processPluginParams( FL_Channel * _ch )
case FL_Plugin::UnknownPlugin:
default:
printf( "handling of plugin params not implemented "
qDebug( "handling of plugin params not implemented "
"for current plugin\n" );
break;
}

View File

@@ -108,10 +108,10 @@ attr_express_begin (int attr, const char* param) {
switch(attr)
{
case ATTR_BOLD:
outstring+=QString().sprintf(op->bold_begin);
outstring+=QString().sprintf("%s", op->bold_begin);
break;
case ATTR_ITALIC:
outstring+=QString().sprintf(op->italic_begin);
outstring+=QString().sprintf("%s", op->italic_begin);
break;
/* Various underlines, they all resolve to HTML's <u> */
@@ -123,11 +123,11 @@ attr_express_begin (int attr, const char* param) {
case ATTR_2DOT_DASH_UL:
case ATTR_WORD_UL:
case ATTR_UNDERLINE:
outstring+=QString().sprintf(op->underline_begin);
outstring+=QString().sprintf("%s", op->underline_begin);
break;
case ATTR_DOUBLE_UL:
outstring+=QString().sprintf(op->dbl_underline_begin);
outstring+=QString().sprintf("%s", op->dbl_underline_begin);
break;
case ATTR_FONTSIZE:
@@ -148,18 +148,18 @@ attr_express_begin (int attr, const char* param) {
break;
case ATTR_SUPER:
outstring+=QString().sprintf(op->superscript_begin);
outstring+=QString().sprintf("%s", op->superscript_begin);
break;
case ATTR_SUB:
outstring+=QString().sprintf(op->subscript_begin);
outstring+=QString().sprintf("%s", op->subscript_begin);
break;
case ATTR_STRIKE:
outstring+=QString().sprintf(op->strikethru_begin);
outstring+=QString().sprintf("%s", op->strikethru_begin);
break;
case ATTR_DBL_STRIKE:
outstring+=QString().sprintf(op->dbl_strikethru_begin);
outstring+=QString().sprintf("%s", op->dbl_strikethru_begin);
break;
case ATTR_EXPAND:
@@ -167,16 +167,16 @@ attr_express_begin (int attr, const char* param) {
break;
case ATTR_OUTLINE:
outstring+=QString().sprintf(op->outline_begin);
outstring+=QString().sprintf("%s", op->outline_begin);
break;
case ATTR_SHADOW:
outstring+=QString().sprintf(op->shadow_begin);
outstring+=QString().sprintf("%s", op->shadow_begin);
break;
case ATTR_EMBOSS:
outstring+=QString().sprintf(op->emboss_begin);
outstring+=QString().sprintf("%s", op->emboss_begin);
break;
case ATTR_ENGRAVE:
outstring+=QString().sprintf(op->engrave_begin);
outstring+=QString().sprintf("%s", op->engrave_begin);
break;
case ATTR_CAPS:
@@ -189,7 +189,7 @@ attr_express_begin (int attr, const char* param) {
simulate_smallcaps = TRUE;
else {
if (op->small_caps_begin)
outstring+=QString().sprintf(op->small_caps_begin);
outstring+=QString().sprintf("%s", op->small_caps_begin);
}
break;
}
@@ -209,10 +209,10 @@ attr_express_end (int attr, char *param)
switch(attr)
{
case ATTR_BOLD:
outstring+=QString().sprintf(op->bold_end);
outstring+=QString().sprintf("%s", op->bold_end);
break;
case ATTR_ITALIC:
outstring+=QString().sprintf(op->italic_end);
outstring+=QString().sprintf("%s", op->italic_end);
break;
/* Various underlines, they all resolve to HTML's </u> */
@@ -224,11 +224,11 @@ attr_express_end (int attr, char *param)
case ATTR_2DOT_DASH_UL:
case ATTR_WORD_UL:
case ATTR_UNDERLINE:
outstring+=QString().sprintf(op->underline_end);
outstring+=QString().sprintf("%s", op->underline_end);
break;
case ATTR_DOUBLE_UL:
outstring+=QString().sprintf(op->dbl_underline_end);
outstring+=QString().sprintf("%s", op->dbl_underline_end);
break;
case ATTR_FONTSIZE:
@@ -236,47 +236,47 @@ attr_express_end (int attr, char *param)
break;
case ATTR_FONTFACE:
outstring+=QString().sprintf(op->font_end);
outstring+=QString().sprintf("%s", op->font_end);
break;
case ATTR_FOREGROUND:
outstring+=QString().sprintf(op->foreground_end);
outstring+=QString().sprintf("%s", op->foreground_end);
break;
case ATTR_BACKGROUND:
if (!simple_mode)
outstring+=QString().sprintf(op->background_end);
outstring+=QString().sprintf("%s", op->background_end);
break;
case ATTR_SUPER:
outstring+=QString().sprintf(op->superscript_end);
outstring+=QString().sprintf("%s", op->superscript_end);
break;
case ATTR_SUB:
outstring+=QString().sprintf(op->subscript_end);
outstring+=QString().sprintf("%s", op->subscript_end);
break;
case ATTR_STRIKE:
outstring+=QString().sprintf(op->strikethru_end);
outstring+=QString().sprintf("%s", op->strikethru_end);
break;
case ATTR_DBL_STRIKE:
outstring+=QString().sprintf(op->dbl_strikethru_end);
outstring+=QString().sprintf("%s", op->dbl_strikethru_end);
break;
case ATTR_OUTLINE:
outstring+=QString().sprintf(op->outline_end);
outstring+=QString().sprintf("%s", op->outline_end);
break;
case ATTR_SHADOW:
outstring+=QString().sprintf(op->shadow_end);
outstring+=QString().sprintf("%s", op->shadow_end);
break;
case ATTR_EMBOSS:
outstring+=QString().sprintf(op->emboss_end);
outstring+=QString().sprintf("%s", op->emboss_end);
break;
case ATTR_ENGRAVE:
outstring+=QString().sprintf(op->engrave_end);
outstring+=QString().sprintf("%s", op->engrave_end);
break;
case ATTR_EXPAND:
outstring+=QString().sprintf(op->expand_end);
outstring+=QString().sprintf("%s", op->expand_end);
break;
case ATTR_CAPS:
@@ -289,7 +289,7 @@ attr_express_end (int attr, char *param)
simulate_smallcaps = FALSE;
else {
if (op->small_caps_end)
outstring+=QString().sprintf(op->small_caps_end);
outstring+=QString().sprintf("%s", op->small_caps_end);
}
break;
}

View File

@@ -650,8 +650,8 @@ starting_body ()
{
if (!have_printed_body) {
if (!inline_mode) {
outstring+=QString().sprintf(op->header_end);
outstring+=QString().sprintf(op->body_begin);
outstring+=QString().sprintf("%s", op->header_end);
outstring+=QString().sprintf("%s", op->body_begin);
}
within_header = FALSE;
have_printed_body = TRUE;
@@ -926,7 +926,7 @@ process_info_group (Word *w)
if (!inline_mode) {
if (!strcmp("\\title", s)) {
outstring+=QString().sprintf(op->document_title_begin);
outstring+=QString().sprintf("%s", op->document_title_begin);
w2=child->next;
while (w2) {
char *s2 = word_string(w2);
@@ -951,18 +951,18 @@ process_info_group (Word *w)
s3 = op_translate_char (op, charset_type, charset_codepage, ch, numchar_table);
if (!s3 || !*s3)
{
outstring+=QString().sprintf(op->comment_begin);
outstring+=QString().sprintf("%s", op->comment_begin);
outstring+=QString().sprintf("char 0x%02x",ch);
outstring+=QString().sprintf(op->comment_end);
outstring+=QString().sprintf("%s", op->comment_end);
}
else
{
if (op->word_begin)
outstring+=QString().sprintf(op->word_begin);
outstring+=QString().sprintf("%s", op->word_begin);
outstring+=QString().sprintf("%s", s3);
if (op->word_end)
outstring+=QString().sprintf(op->word_end);
outstring+=QString().sprintf("%s", op->word_end);
}
}
}
@@ -970,10 +970,10 @@ process_info_group (Word *w)
#endif
w2 = w2->next;
}
outstring+=QString().sprintf(op->document_title_end);
outstring+=QString().sprintf("%s", op->document_title_end);
}
else if (!strcmp("\\keywords", s)) {
outstring+=QString().sprintf(op->document_keywords_begin);
outstring+=QString().sprintf("%s", op->document_keywords_begin);
w2=child->next;
while (w2) {
char *s2 = word_string(w2);
@@ -981,10 +981,10 @@ process_info_group (Word *w)
outstring+=QString().sprintf("%s,", s2);
w2 = w2->next;
}
outstring+=QString().sprintf(op->document_keywords_end);
outstring+=QString().sprintf("%s", op->document_keywords_end);
}
else if (!strcmp("\\author", s)) {
outstring+=QString().sprintf(op->document_author_begin);
outstring+=QString().sprintf("%s", op->document_author_begin);
w2=child->next;
while (w2) {
char *s2 = word_string(w2);
@@ -992,7 +992,7 @@ process_info_group (Word *w)
outstring+=QString().sprintf("%s", s2);
w2 = w2->next;
}
outstring+=QString().sprintf(op->document_author_end);
outstring+=QString().sprintf("%s", op->document_author_end);
}
else if (!strcmp("\\comment", s)) {
outstring+=QString().sprintf("%s",op->comment_begin);
@@ -1312,9 +1312,9 @@ cmd_field (Word *w, int align, char has_param, int num) {
w4=w4->next;
if (w4) {
s4=word_string(w4);
outstring+=QString().sprintf(op->hyperlink_begin);
outstring+=QString().sprintf("%s", op->hyperlink_begin);
outstring+=QString().sprintf("%s", s4);
outstring+=QString().sprintf(op->hyperlink_end);
outstring+=QString().sprintf("%s", op->hyperlink_end);
return TRUE;
}
@@ -1423,7 +1423,7 @@ cmd_tab (Word *w, int align, char has_param, int param)
int need= 8-(total_chars_this_line%8);
total_chars_this_line += need;
while(need>0) {
outstring+=QString().sprintf(op->forced_space);
outstring+=QString().sprintf("%s", op->forced_space);
need--;
}
outstring+=QString().sprintf("\n");
@@ -1643,7 +1643,7 @@ cmd_scaps (Word *w, int align, char has_param, int param) {
static int
cmd_bullet (Word *w, int align, char has_param, int param) {
if (op->chars.bullet) {
outstring+=QString().sprintf(op->chars.bullet);
outstring+=QString().sprintf("%s", op->chars.bullet);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1658,7 +1658,7 @@ cmd_bullet (Word *w, int align, char has_param, int param) {
static int
cmd_ldblquote (Word *w, int align, char has_param, int param) {
if (op->chars.left_dbl_quote) {
outstring+=QString().sprintf(op->chars.left_dbl_quote);
outstring+=QString().sprintf("%s", op->chars.left_dbl_quote);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1675,7 +1675,7 @@ cmd_ldblquote (Word *w, int align, char has_param, int param) {
static int
cmd_rdblquote (Word *w, int align, char has_param, int param) {
if (op->chars.right_dbl_quote) {
outstring+=QString().sprintf(op->chars.right_dbl_quote);
outstring+=QString().sprintf("%s", op->chars.right_dbl_quote);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1691,7 +1691,7 @@ cmd_rdblquote (Word *w, int align, char has_param, int param) {
static int
cmd_lquote (Word *w, int align, char has_param, int param) {
if (op->chars.left_quote) {
outstring+=QString().sprintf(op->chars.left_quote);
outstring+=QString().sprintf("%s", op->chars.left_quote);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1708,7 +1708,7 @@ cmd_lquote (Word *w, int align, char has_param, int param) {
static int
cmd_nonbreaking_space (Word *w, int align, char has_param, int param) {
if (op->chars.nonbreaking_space) {
outstring+=QString().sprintf(op->chars.nonbreaking_space);
outstring+=QString().sprintf("%s", op->chars.nonbreaking_space);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1725,7 +1725,7 @@ cmd_nonbreaking_space (Word *w, int align, char has_param, int param) {
static int
cmd_nonbreaking_hyphen (Word *w, int align, char has_param, int param) {
if (op->chars.nonbreaking_hyphen) {
outstring+=QString().sprintf(op->chars.nonbreaking_hyphen);
outstring+=QString().sprintf("%s", op->chars.nonbreaking_hyphen);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1742,7 +1742,7 @@ cmd_nonbreaking_hyphen (Word *w, int align, char has_param, int param) {
static int
cmd_optional_hyphen (Word *w, int align, char has_param, int param) {
if (op->chars.optional_hyphen) {
outstring+=QString().sprintf(op->chars.optional_hyphen);
outstring+=QString().sprintf("%s", op->chars.optional_hyphen);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1758,7 +1758,7 @@ cmd_optional_hyphen (Word *w, int align, char has_param, int param) {
static int
cmd_emdash (Word *w, int align, char has_param, int param) {
if (op->chars.emdash) {
outstring+=QString().sprintf(op->chars.emdash);
outstring+=QString().sprintf("%s", op->chars.emdash);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1775,7 +1775,7 @@ cmd_emdash (Word *w, int align, char has_param, int param) {
static int
cmd_endash (Word *w, int align, char has_param, int param) {
if (op->chars.endash) {
outstring+=QString().sprintf(op->chars.endash);
outstring+=QString().sprintf("%s", op->chars.endash);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1792,7 +1792,7 @@ cmd_endash (Word *w, int align, char has_param, int param) {
static int
cmd_rquote (Word *w, int align, char has_param, int param) {
if (op->chars.right_quote) {
outstring+=QString().sprintf(op->chars.right_quote);
outstring+=QString().sprintf("%s", op->chars.right_quote);
++total_chars_this_line; /* \tab */
}
return FALSE;
@@ -1808,7 +1808,7 @@ cmd_rquote (Word *w, int align, char has_param, int param) {
static int
cmd_par (Word *w, int align, char has_param, int param) {
if (op->line_break) {
outstring+=QString().sprintf(op->line_break);
outstring+=QString().sprintf("%s", op->line_break);
total_chars_this_line = 0; /* \tab */
}
return FALSE;
@@ -1825,7 +1825,7 @@ cmd_par (Word *w, int align, char has_param, int param) {
static int
cmd_line (Word *w, int align, char has_param, int param) {
if (op->line_break) {
outstring+=QString().sprintf(op->line_break);
outstring+=QString().sprintf("%s", op->line_break);
total_chars_this_line = 0; /* \tab */
}
return FALSE;
@@ -1841,7 +1841,7 @@ cmd_line (Word *w, int align, char has_param, int param) {
static int cmd_page (Word *w, int align, char has_param, int param) {
if (op->page_break) {
outstring+=QString().sprintf(op->page_break);
outstring+=QString().sprintf("%s", op->page_break);
total_chars_this_line = 0; /* \tab */
}
return FALSE;
@@ -2337,7 +2337,7 @@ static int cmd_s (Word *w, int align, char has_param, int param) {
static int cmd_sect (Word *w, int align, char has_param, int param) {
/* XX kludge */
if (op->paragraph_begin) {
outstring+=QString().sprintf(op->paragraph_begin);
outstring+=QString().sprintf("%s", op->paragraph_begin);
}
return FALSE;
}
@@ -2351,9 +2351,9 @@ static int cmd_sect (Word *w, int align, char has_param, int param) {
static int cmd_shp (Word *w, int align, char has_param, int param) {
if (op->comment_begin) {
outstring+=QString().sprintf(op->comment_begin);
outstring+=QString().sprintf("%s", op->comment_begin);
outstring+=QString().sprintf("Drawn Shape (ignored--not implemented yet)");
outstring+=QString().sprintf(op->comment_end); /* daved 0.20.2 */
outstring+=QString().sprintf("%s", op->comment_end); /* daved 0.20.2 */
}
return FALSE;
@@ -2404,17 +2404,17 @@ static int cmd_ansicpg (Word *w, int align, char has_param, int param) {
charset_codepage = &codepages[i];
if (charset_codepage->cp == param) {
if (op->comment_begin) {
outstring+=QString().sprintf(op->comment_begin);
outstring+=QString().sprintf("%s", op->comment_begin);
outstring+=QString().sprintf("document uses ANSI codepage %d character set", param);
outstring+=QString().sprintf(op->comment_end);
outstring+=QString().sprintf("%s", op->comment_end);
}
break;
}
}
if ((charset_codepage == NULL || charset_codepage->cp == 0) && op->comment_begin) {
outstring+=QString().sprintf(op->comment_begin);
outstring+=QString().sprintf("%s", op->comment_begin);
outstring+=QString().sprintf("document uses default ANSI codepage character set");
outstring+=QString().sprintf(op->comment_end);
outstring+=QString().sprintf("%s", op->comment_end);
}
return FALSE;
}
@@ -3106,7 +3106,7 @@ enum { SMALL=0, BIG=1 };
if (simulate_smallcaps) {
if (*s >= 'a' && *s <= 'z') {
state=SMALL;
outstring+=QString().sprintf(op->smaller_begin);
outstring+=QString().sprintf("%s", op->smaller_begin);
}
else
state=BIG;
@@ -3136,13 +3136,13 @@ enum { SMALL=0, BIG=1 };
ch = *s;
if (ch >= 'a' && ch <= 'z') {
if (state==BIG)
outstring+=QString().sprintf(op->smaller_begin);
outstring+=QString().sprintf("%s", op->smaller_begin);
state=SMALL;
}
else
{
if (state==SMALL)
outstring+=QString().sprintf(op->smaller_end);
outstring+=QString().sprintf("%s", op->smaller_end);
state=BIG;
}
}
@@ -3168,7 +3168,7 @@ begin_table()
have_printed_cell_end = FALSE;
attrstack_push();
starting_body();
outstring+=QString().sprintf(op->table_begin);
outstring+=QString().sprintf("%s", op->table_begin);
}
@@ -3186,12 +3186,12 @@ end_table ()
if (within_table) {
if (!have_printed_cell_end) {
attr_pop_dump();
outstring+=QString().sprintf(op->table_cell_end);
outstring+=QString().sprintf("%s", op->table_cell_end);
}
if (!have_printed_row_end) {
outstring+=QString().sprintf(op->table_row_end);
outstring+=QString().sprintf("%s", op->table_row_end);
}
outstring+=QString().sprintf(op->table_end);
outstring+=QString().sprintf("%s", op->table_end);
within_table=FALSE;
have_printed_row_begin = FALSE;
have_printed_cell_begin = FALSE;
@@ -3213,13 +3213,13 @@ void
starting_text() {
if (within_table) {
if (!have_printed_row_begin) {
outstring+=QString().sprintf(op->table_row_begin);
outstring+=QString().sprintf("%s", op->table_row_begin);
have_printed_row_begin=TRUE;
have_printed_row_end=FALSE;
have_printed_cell_begin=FALSE;
}
if (!have_printed_cell_begin) {
outstring+=QString().sprintf(op->table_cell_begin);
outstring+=QString().sprintf("%s", op->table_cell_begin);
attrstack_express_all();
have_printed_cell_begin=TRUE;
have_printed_cell_end=FALSE;
@@ -3246,15 +3246,15 @@ starting_paragraph_align (int align)
switch (align)
{
case ALIGN_CENTER:
outstring+=QString().sprintf(op->center_begin);
outstring+=QString().sprintf("%s", op->center_begin);
break;
case ALIGN_LEFT:
break;
case ALIGN_RIGHT:
outstring+=QString().sprintf(op->align_right_begin);
outstring+=QString().sprintf("%s", op->align_right_begin);
break;
case ALIGN_JUSTIFY:
outstring+=QString().sprintf(op->align_right_begin); /* This is WRONG! */
outstring+=QString().sprintf("%s", op->align_right_begin); /* This is WRONG! */
break;
}
}
@@ -3273,16 +3273,16 @@ ending_paragraph_align (int align)
{
switch (align) {
case ALIGN_CENTER:
outstring+=QString().sprintf(op->center_end);
outstring+=QString().sprintf("%s", op->center_end);
break;
case ALIGN_LEFT:
/* outstring+=QString().sprintf(op->align_left_end); */
/* outstring+=QString().sprintf("%s", op->align_left_end); */
break;
case ALIGN_RIGHT:
outstring+=QString().sprintf(op->align_right_end);
outstring+=QString().sprintf("%s", op->align_right_end);
break;
case ALIGN_JUSTIFY:
outstring+=QString().sprintf(op->justify_end);
outstring+=QString().sprintf("%s", op->justify_end);
break;
}
}
@@ -3391,12 +3391,12 @@ word_print_core (Word *w)
total_chars_this_line += strlen(s);
if (op->word_begin)
outstring+=QString().sprintf(op->word_begin);
outstring+=QString().sprintf("%s", op->word_begin);
print_with_special_exprs (s);
if (op->word_end)
outstring+=QString().sprintf(op->word_end);
outstring+=QString().sprintf("%s", op->word_end);
}
@@ -3456,17 +3456,17 @@ word_print_core (Word *w)
is_cell_group=TRUE;
if (!have_printed_cell_begin) {
/* Need this with empty cells */
outstring+=QString().sprintf(op->table_cell_begin);
outstring+=QString().sprintf("%s", op->table_cell_begin);
attrstack_express_all();
}
attr_pop_dump();
outstring+=QString().sprintf(op->table_cell_end);
outstring+=QString().sprintf("%s", op->table_cell_end);
have_printed_cell_begin = FALSE;
have_printed_cell_end=TRUE;
}
else if (!strcmp (s, "row")) {
if (within_table) {
outstring+=QString().sprintf(op->table_row_end);
outstring+=QString().sprintf("%s", op->table_row_end);
have_printed_row_begin = FALSE;
have_printed_row_end=TRUE;
} else {
@@ -3496,10 +3496,10 @@ word_print_core (Word *w)
outstring+=QString().sprintf("%s",op->comment_end);
} else {
if (op->word_begin)
outstring+=QString().sprintf(op->word_begin);
outstring+=QString().sprintf("%s", op->word_begin);
outstring+=QString().sprintf("%s", s2);
if (op->word_end)
outstring+=QString().sprintf(op->word_end);
outstring+=QString().sprintf("%s", op->word_end);
}
}
else
@@ -3529,9 +3529,9 @@ word_print_core (Word *w)
if (!hip) {
if (debug_mode) {
outstring+=QString().sprintf(op->comment_begin);
outstring+=QString().sprintf("%s", op->comment_begin);
outstring+=QString().sprintf("Unfamiliar RTF command: %s (HashIndex not found)", s);
outstring+=QString().sprintf(op->comment_end); /* daved 0.20.2 */
outstring+=QString().sprintf("%s", op->comment_end); /* daved 0.20.2 */
}
}
else {
@@ -3610,9 +3610,9 @@ word_print_core (Word *w)
if (within_picture) {
if(pictfile) {
fclose(pictfile);
outstring+=QString().sprintf(op->imagelink_begin);
outstring+=QString().sprintf("%s", op->imagelink_begin);
outstring+=QString().sprintf("%s", picture_path);
outstring+=QString().sprintf(op->imagelink_end);
outstring+=QString().sprintf("%s", op->imagelink_end);
}
within_picture=FALSE;
}
@@ -3665,8 +3665,8 @@ word_print (Word *w, QString & _s)
outstring = "";
if (!inline_mode) {
outstring+=QString().sprintf(op->document_begin);
outstring+=QString().sprintf(op->header_begin);
outstring+=QString().sprintf("%s", op->document_begin);
outstring+=QString().sprintf("%s", op->header_begin);
}
within_header=TRUE;
@@ -3677,8 +3677,8 @@ word_print (Word *w, QString & _s)
end_table();
if (!inline_mode) {
outstring+=QString().sprintf(op->body_end);
outstring+=QString().sprintf(op->document_end);
outstring+=QString().sprintf("%s", op->body_end);
outstring+=QString().sprintf("%s", op->document_end);
}
_s = outstring;
}

View File

@@ -238,49 +238,49 @@ op_begin_std_fontsize (OutputPersonality *op, int size)
switch (size) {
case 8:
if (op->fontsize8_begin) {
outstring+=QString().sprintf(op->fontsize8_begin);
outstring+=QString().sprintf("%s", op->fontsize8_begin);
found_std_expr = TRUE;
}
break;
case 10:
if (op->fontsize10_begin) {
outstring+=QString().sprintf(op->fontsize10_begin);
outstring+=QString().sprintf("%s", op->fontsize10_begin);
found_std_expr = TRUE;
}
break;
case 12:
if (op->fontsize12_begin) {
outstring+=QString().sprintf(op->fontsize12_begin);
outstring+=QString().sprintf("%s", op->fontsize12_begin);
found_std_expr = TRUE;
}
break;
case 14:
if (op->fontsize14_begin) {
outstring+=QString().sprintf(op->fontsize14_begin);
outstring+=QString().sprintf("%s", op->fontsize14_begin);
found_std_expr = TRUE;
}
break;
case 18:
if (op->fontsize18_begin) {
outstring+=QString().sprintf(op->fontsize18_begin);
outstring+=QString().sprintf("%s", op->fontsize18_begin);
found_std_expr = TRUE;
}
break;
case 24:
if (op->fontsize24_begin) {
outstring+=QString().sprintf(op->fontsize24_begin);
outstring+=QString().sprintf("%s", op->fontsize24_begin);
found_std_expr = TRUE;
}
break;
case 36:
if (op->fontsize36_begin) {
outstring+=QString().sprintf(op->fontsize36_begin);
outstring+=QString().sprintf("%s", op->fontsize36_begin);
found_std_expr = TRUE;
}
break;
case 48:
if (op->fontsize48_begin) {
outstring+=QString().sprintf(op->fontsize48_begin);
outstring+=QString().sprintf("%s", op->fontsize48_begin);
found_std_expr = TRUE;
}
break;
@@ -300,46 +300,46 @@ op_begin_std_fontsize (OutputPersonality *op, int size)
* size.
*/
if (size<9 && op->fontsize8_begin) {
outstring+=QString().sprintf(op->fontsize8_begin);
outstring+=QString().sprintf("%s", op->fontsize8_begin);
} else
if (size<11 && op->fontsize10_begin) {
outstring+=QString().sprintf(op->fontsize10_begin);
outstring+=QString().sprintf("%s", op->fontsize10_begin);
} else
if (size<13 && op->fontsize12_begin) {
outstring+=QString().sprintf(op->fontsize12_begin);
outstring+=QString().sprintf("%s", op->fontsize12_begin);
} else
if (size<16 && op->fontsize14_begin) {
outstring+=QString().sprintf(op->fontsize14_begin);
outstring+=QString().sprintf("%s", op->fontsize14_begin);
} else
if (size<21 && op->fontsize18_begin) {
outstring+=QString().sprintf(op->fontsize18_begin);
outstring+=QString().sprintf("%s", op->fontsize18_begin);
} else
if (size<30 && op->fontsize24_begin) {
outstring+=QString().sprintf(op->fontsize24_begin);
outstring+=QString().sprintf("%s", op->fontsize24_begin);
} else
if (size<42 && op->fontsize36_begin) {
outstring+=QString().sprintf(op->fontsize36_begin);
outstring+=QString().sprintf("%s", op->fontsize36_begin);
} else
if (size>40 && op->fontsize48_begin) {
outstring+=QString().sprintf(op->fontsize48_begin);
outstring+=QString().sprintf("%s", op->fontsize48_begin);
} else
/* If we can't even produce a good approximation,
* just try to get a font size near 12 point.
*/
if (op->fontsize12_begin)
outstring+=QString().sprintf(op->fontsize12_begin);
outstring+=QString().sprintf("%s", op->fontsize12_begin);
else
if (op->fontsize14_begin)
outstring+=QString().sprintf(op->fontsize14_begin);
outstring+=QString().sprintf("%s", op->fontsize14_begin);
else
if (op->fontsize10_begin)
outstring+=QString().sprintf(op->fontsize10_begin);
outstring+=QString().sprintf("%s", op->fontsize10_begin);
else
if (op->fontsize18_begin)
outstring+=QString().sprintf(op->fontsize18_begin);
outstring+=QString().sprintf("%s", op->fontsize18_begin);
else
if (op->fontsize8_begin)
outstring+=QString().sprintf(op->fontsize8_begin);
outstring+=QString().sprintf("%s", op->fontsize8_begin);
else
error_handler ("output personality lacks sufficient font size change capability");
}
@@ -367,49 +367,49 @@ op_end_std_fontsize (OutputPersonality *op, int size)
switch (size) {
case 8:
if (op->fontsize8_end) {
outstring+=QString().sprintf(op->fontsize8_end);
outstring+=QString().sprintf("%s", op->fontsize8_end);
found_std_expr = TRUE;
}
break;
case 10:
if (op->fontsize10_end) {
outstring+=QString().sprintf(op->fontsize10_end);
outstring+=QString().sprintf("%s", op->fontsize10_end);
found_std_expr = TRUE;
}
break;
case 12:
if (op->fontsize12_end) {
outstring+=QString().sprintf(op->fontsize12_end);
outstring+=QString().sprintf("%s", op->fontsize12_end);
found_std_expr = TRUE;
}
break;
case 14:
if (op->fontsize14_end) {
outstring+=QString().sprintf(op->fontsize14_end);
outstring+=QString().sprintf("%s", op->fontsize14_end);
found_std_expr = TRUE;
}
break;
case 18:
if (op->fontsize18_end) {
outstring+=QString().sprintf(op->fontsize18_end);
outstring+=QString().sprintf("%s", op->fontsize18_end);
found_std_expr = TRUE;
}
break;
case 24:
if (op->fontsize24_end) {
outstring+=QString().sprintf(op->fontsize24_end);
outstring+=QString().sprintf("%s", op->fontsize24_end);
found_std_expr = TRUE;
}
break;
case 36:
if (op->fontsize36_end) {
outstring+=QString().sprintf(op->fontsize36_end);
outstring+=QString().sprintf("%s", op->fontsize36_end);
found_std_expr = TRUE;
}
break;
case 48:
if (op->fontsize48_end) {
outstring+=QString().sprintf(op->fontsize48_end);
outstring+=QString().sprintf("%s", op->fontsize48_end);
found_std_expr = TRUE;
}
break;
@@ -429,46 +429,46 @@ op_end_std_fontsize (OutputPersonality *op, int size)
* size.
*/
if (size<9 && op->fontsize8_end) {
outstring+=QString().sprintf(op->fontsize8_end);
outstring+=QString().sprintf("%s", op->fontsize8_end);
} else
if (size<11 && op->fontsize10_end) {
outstring+=QString().sprintf(op->fontsize10_end);
outstring+=QString().sprintf("%s", op->fontsize10_end);
} else
if (size<13 && op->fontsize12_end) {
outstring+=QString().sprintf(op->fontsize12_end);
outstring+=QString().sprintf("%s", op->fontsize12_end);
} else
if (size<16 && op->fontsize14_end) {
outstring+=QString().sprintf(op->fontsize14_end);
outstring+=QString().sprintf("%s", op->fontsize14_end);
} else
if (size<21 && op->fontsize18_end) {
outstring+=QString().sprintf(op->fontsize18_end);
outstring+=QString().sprintf("%s", op->fontsize18_end);
} else
if (size<30 && op->fontsize24_end) {
outstring+=QString().sprintf(op->fontsize24_end);
outstring+=QString().sprintf("%s", op->fontsize24_end);
} else
if (size<42 && op->fontsize36_end) {
outstring+=QString().sprintf(op->fontsize36_end);
outstring+=QString().sprintf("%s", op->fontsize36_end);
} else
if (size>40 && op->fontsize48_end) {
outstring+=QString().sprintf(op->fontsize48_end);
outstring+=QString().sprintf("%s", op->fontsize48_end);
} else
/* If we can't even produce a good approximation,
* just try to get a font size near 12 point.
*/
if (op->fontsize12_end)
outstring+=QString().sprintf(op->fontsize12_end);
outstring+=QString().sprintf("%s", op->fontsize12_end);
else
if (op->fontsize14_end)
outstring+=QString().sprintf(op->fontsize14_end);
outstring+=QString().sprintf("%s", op->fontsize14_end);
else
if (op->fontsize10_end)
outstring+=QString().sprintf(op->fontsize10_end);
outstring+=QString().sprintf("%s", op->fontsize10_end);
else
if (op->fontsize18_end)
outstring+=QString().sprintf(op->fontsize18_end);
outstring+=QString().sprintf("%s", op->fontsize18_end);
else
if (op->fontsize8_end)
outstring+=QString().sprintf(op->fontsize8_end);
outstring+=QString().sprintf("%s", op->fontsize8_end);
else
error_handler ("output personality lacks sufficient font size change capability");
}

View File

@@ -1,7 +1,7 @@
/*
* kicker.cpp - bassdrum-synthesizer
*
* Copyright (c) 2006-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -33,7 +33,6 @@
#include "note_play_handle.h"
#include "sweep_oscillator.h"
#undef SINGLE_SOURCE_COMPILE
#include "embed.cpp"

View File

@@ -37,13 +37,10 @@
#include "tab_bar.h"
#include "tab_button.h"
#undef SINGLE_SOURCE_COMPILE
#include "embed.cpp"
extern "C"
{

View File

@@ -14,6 +14,10 @@ IF(WANT_CMT)
ADD_SUBDIRECTORY(cmt)
ENDIF(WANT_CMT)
IF(WANT_CALF)
ADD_SUBDIRECTORY(calf)
ENDIF(WANT_CALF)
INCLUDE(BuildPlugin)

View File

@@ -0,0 +1,9 @@
Krzysztof Foltman <wdev@foltman.com>
Hermann Meyer <brummer-@web.de>
Thor Harald Johansen <thj@thj.no>
Thorsten Wilms <t_w_@freenet.de>
Hans Baier <hansfbaier@googlemail.com>
Torben Hohn <torbenh@gmx.de>
Additional bugfixes/enhancement patches:
David Täht <d@teklibre.com>

View File

@@ -0,0 +1,15 @@
FILE(GLOB SOURCES *.cpp)
ADD_LIBRARY(calf MODULE ${SOURCES})
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/calf)
INSTALL(TARGETS calf LIBRARY DESTINATION ${PLUGIN_DIR}/ladspa)
ADD_DEFINITIONS(-DUSE_LADSPA=1)
SET_TARGET_PROPERTIES(calf PROPERTIES PREFIX "")
SET_TARGET_PROPERTIES(calf PROPERTIES COMPILE_FLAGS "-O2 -finline-limit=80 -funroll-loops")
IF(LMMS_BUILD_WIN32)
ADD_CUSTOM_COMMAND(TARGET calf POST_BUILD COMMAND ${STRIP} ${CMAKE_CURRENT_BINARY_DIR}/calf.dll)
ENDIF(LMMS_BUILD_WIN32)
IF(NOT LMMS_BUILD_APPLE)
SET_TARGET_PROPERTIES(calf PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined")
ENDIF(NOT LMMS_BUILD_APPLE)

View File

@@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@@ -0,0 +1,170 @@
Version 0.0.18.2
+ Organ: fix voice stealing of released notes, sort out GUI, add quadratic
mode for amplitude envelope (enabled by default) - sounds more natural
+ Monosynth: fix the bug that caused JACK to kick the client out due
to precalculating waves in a completely wrong place, fix portamento
for off-stack notes
+ Presets: 3 new presets for Organ, 4 for Monosynth, 2 for Reverb
Version 0.0.18.1
+ Filter: fixed subtle redraw bugs
+ Icons: fixed packaging-incompatible paths
Version 0.0.18
+ Filterclavier: new plugin (a MIDI controlled filter) by Hans Baier
+ DSSI: added a basic implementation of live graphs. The graphs have a
limited resolution (128 data points), and are rather inefficient
(as the graph data need to be transmitted via OSC to a different
process), but it's better than nothing
+ GUI: Torben Hohn's drawing optimizations (critical for Intel graphics
cards, but should also reduce CPU usage on other hardware)
+ Phaser: added frequency response graph
+ JACK host: discontinue the broken option -p; allow giving preset names
after a colon sign (reverb:DiscoVerb instead of -p DiscoVerb reverb)
+ Reverb: less modulation; tone controls; 2 more room types
+ MultiChorus: add double bandpass filter on input
+ GUI: added frequency grid
+ Organ: added progress reporting on load (works with JACK host and LV2)
+ JACK host: use sensible port names (possibly breaking new LASH sessions)
+ Organ: added polyphony limit
+ Small plugins: added support for polymorphic port extension to allow
the same plugins to be used for control and audio signals
+ DSSI: renamed all the plugins from "plugin LADSPA" to "plugin DSSI"
+ LADSPA: more reasonable default value hints, fixed locale issue in LRDF
+ JACK host: added icons by Thorsten Wilms (thanks!)
+ Organ, Monosynth: better memory usage
+ LV2: attempt at supporting configure-like parameters (key mapping curve
in Organ) by the new String Port extension
+ AutoHell: header files are not installed anymore (they are of little
use anyway)
+ AutoHell: configure script prints if --enable-experimental was specified
Version 0.0.17
+ Compressor: new plugin by Thor Harald Johansen
+ GUI: control improvements (new LED control, improved VU meter, XML
improvements, line graph with dots and grid lines - no legend yet), move
autolayout code from the plugin libraries to makerdf executable,
+ Most plugins: use custom GUI layouts instead of autogenerated ones
+ Most plugins: add dry amount (for aux bus type uses)
+ Flanger, Filter, MultiChorus: added live graphs displaying frequency
response and (in case of MultiChorus) LFO positions
+ LV2 GUI: added a way to display live graphs in Ardour and Zynjacku/LV2Rack
(only works when the plugin and the GUI are in the same process)
+ Framework: general improvements/cleanups to reduce the chance of the
kind of errors that were introduced in 0.0.16 and reduce dependencies
+ Monosynth: removed soft clipper on output
Version 0.0.16.3
+ Fixed compilation without LV2 core installed
Version 0.0.16.2
+ Fixed DSSI GUI for MultiChorus
+ Fixed LV2 GUI for MultiChorus
+ Make knob control mouse wheel handling work better in Ingen
Version 0.0.16
+ New MultiChorus plugin (stereo multitap chorus with maximum of 8 voices)
+ Experimental set of plugins for modular synthesizers like Ingen by
Dave Robillard (enabled using --enable-experimental option in configure
script)
+ Minor improvements to other plugins (like Rotary Speaker)
+ More work on API documentation
Version 0.0.15
+ Organ: new percussive section, using 2-operator FM synthesis for
monophonic or polyphonic percussive attack; added global transpose and
detune; rearrangement of controls between sections
+ Rotary Speaker: another attempt at making it useful (thanks FishB8)
+ JACK host: eliminate deadlock on exit
+ GUI: bipolar knobs now have a "dead zone" (magnet) in the middle point
+ GUI: dragging a knob with SHIFT held allows for fine adjustments
+ GUI: new controls - curve editor and keyboard
+ LV2: improved extension support (supports my "extended port properties"
extension now)
+ Added some API documentation
Version 0.0.14
+ OSC: totally new OSC wrapper, to allow for realtime-safe parsing (doesn't
matter as far as functionality goes, will probably be rewritten again
anyway)
+ Everything: memory management fixes (should improve stability and
compatibility)
+ Organ: improved memory usage
+ GUI: improved bipolar knobs, added endless knobs
+ Presets: separate 'built-in' and 'user' presets (so that built-in presets
can be upgraded without affecting user's own presets)
+ Monosynth: new presets
Version 0.0.13
+ Fixed several problems related to 64-bit environments and OpenSUSE (thanks
oc2pus!)
+ Added NOCONFIGURE environment variable support to autogen.sh
Version 0.0.12
+ RotarySpeaker: work in progress; enabled by default just in case it's
useful for anyone
+ Organ: reworked to add a complete subtractive synth section, a selection
of waveform (settable on a per-drawbar basis), individual settings of
phase, detune, panning, routing for each drawbar, as well as improved(?)
percussive section and vibrato/phaser section. It is usable (and sounds
good!), but some parameters, waveform set etc. may change in future. May
take up to 100 MB of RAM due to pre-calculated bandlimited waveforms.
+ Added half-complete implementation of LV2 (including GUI and events).
+ Lots of small "polishing" kind of fixes in many places (like proper
rounding of values in the GUIs, another set of hold/sostenuto fixes etc)
Version 0.0.11
+ Fixed x86-64 bugs
+ JackHost: implemented LASH support
+ RotarySpeaker: fixed panning bug, implemented acceleration/decceleration
for "off" state
Version 0.0.10
+ First attempt at DSSI GUI, does not support some features from JACK host,
but that's inevitable because of API limitations
+ Reverb: improvements (more parameters, fixed denormals)
+ Knob: added custom support for scroll wheel (instead of one inherited from
GtkRange)
Version 0.0.9
+ started creating an XML-based GUI
+ LineGraph: new GTK+ control for displaying waveforms and filter response
graphs in Monosynth (and maybe others in future)
+ Monosynth: notch filter changes (made notch bandwidth proportional to Q,
just for fun, might be a bad idea)
+ Monosynth: more waveforms (these might be final?)
+ Monosynth: capped Sustain level to 0.999 so that decay time actually means
something with Sustain = 100% (not a great way to do it, but acceptable in
this case)
+ Monosynth: GUI refreshes less often (which means less CPU use)
+ Monosynth: less clicks on sounds using LP filter with very low cutoff
(using ramp of 256 samples instead of 64 samples as before)
+ Knob: new GTK+ control based on GtkRange, with my primitive bitmap set
(generated with Python and Cairo)
+ Organ: added a GUI too, very provisional
+ Organ: fixed Hold pedal (doesn't release the notes which are still depressed)
+ RotarySpeaker: new effect (split off Organ)
+ all: denormal fixes (still some denormals present in reverb)
+ Reverb: better time setting (decay time somewhat corresponds to -60dB
attenuation time)
+ JackHost: -M switch allows for automatic connection to JACK MIDI event source
(use -M system:midi_capture_2 or -M 2 for autoconnection to
system:midi_capture_2; of course, the short numeric form only work for
system:midi_capture_ ports)
+ JackHost: -p switch selects a preset automatically
+ JackHost: better size setting algorithm
+ JackHost: duplicate client name (causing JACK to rename the client) doesn't
break autoconnecting functionality
+ autotools configuration update (detect Cairo and require newer GTK+)
+ more presets

View File

@@ -0,0 +1,254 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
Software Foundation, Inc.
Copyright (C) 2007-2008 Krzysztof Foltman
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Prerequisites
=============
To compile and install Calf, you need:
- POSIX-compliant operating system
- G++ version 4.0 or higher (tested with 4.1.3)
- GTK+2 headers and libraries (glib 2.10, gtk+ 2.12)
- Cairo headers and libraries
- Glade 2 headers and libraries
Optional but recommended:
- JACK header and libraries (tested with 0.109.0)
- LADSPA header
- DSSI header
- LV2 core
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. (Caching is
disabled by default to prevent problems with accidental use of stale
cache files.)
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You only need
`configure.ac' if you want to change it or regenerate `configure' using
a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not support the `VPATH'
variable, you have to compile the package for one architecture at a
time in the source code directory. After you have installed the
package for one architecture, use `make distclean' before reconfiguring
for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script). Here is a another example:
/bin/bash ./configure CONFIG_SHELL=/bin/bash
Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
configuration-related scripts to be executed by `/bin/bash'.
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

View File

@@ -0,0 +1,49 @@
Calf is a pack of audio plugins - effects and instruments, currently in
development. The goal is to create a set of plugins using decent algorithms
and parameter settings, available in a form which is compatible with as many
open source applications as possible.
How to use Calf plugins:
* LADSPA plugins
Calf is installed as calf.so library in your LADSPA directory (typically
/usr/lib/ladspa). It means that typical LADSPA host should be able to find
Calf's plugins.
* DSSI plugins
Calf .so module is also installed in your DSSI plugin directory, which means
your DSSI host (like jack-dssi-host or rosegarden) should find it and
include its plugins in the plugin list.
* JACK client application
You can also use Calf plugins as separate applications, connecting to other
applications using JACK Audio Connection Kit (version 0.103 or newer is
required). To run the client, type:
calfjackhost monosynth !
(! means "connect", last "!" means "connect to output")
Other examples:
calfjackhost monosynth ! vintagedelay ! flanger !
(runs monosynth into vintagedelay and vintagedelay into flanger, then to
output)
calfjackhost ! reverb !
(takes signal from system:capture_1 and _2, puts it through reverb, and then
sends to system:playback_1 and _2)
You can also change client name or input/output port names with command-line
options (type calfjackhost --help). Use qjackctl, patchage or jack_connect
to connect the Calf JACK client to your sound card or other applications, if
"!" is inadequate for any reason (if I didn't explain it properly, or if it
doesn't provide the connectivity options needed).
Keep in mind this project is in the early development phase. It is usable
for certain purposes, but drop me a note if you need something.

View File

@@ -0,0 +1,40 @@
1. More effects
- auto-wah (might be integrated into filter)
- envelope follower
- better reverb (more features, use nested allpasses, use 1-pole
1-zero allpass instead of fractional delays)
- dynamics processing (Thor already did the compressor)
- distortion?
- windy rotary speakery stuff
- filter: more types
2. Some instruments
- some virtual analogue thing (something larger than Monosynth)
- FM (by reusing my MMX code, or something)
3. DSP library
- profiling framework
- optimized code (the one I have now only pretends to be optimized :) )
- underflow handling
4. Wrappers
- LADSPA: proper rdf (get clearance from drobilla ;) )
- better jack host (controls etc)
- BSE
- buzztard
- Linux VST
- LV2
Message Context (for Organ)
EPP (the rest of them)
Mixing Controls
5. Organization stuff (autotools etc)
- correct compilation and installation of LADSPA plugins (current version is a hack!)
- switch to -O3
- get to work on 64-bit architectures
- i18n (gettext or whatever)

View File

@@ -0,0 +1,790 @@
/* Calf DSP Library
* Reusable audio effect classes.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __CALF_AUDIOFX_H
#define __CALF_AUDIOFX_H
#include <complex>
#include <iostream>
#include <calf/biquad.h>
#include <calf/onepole.h>
#include "primitives.h"
#include "delay.h"
#include "fixed_point.h"
#include "inertia.h"
namespace dsp {
#if 0
}; to keep editor happy
#endif
/**
* Audio effect base class. Not really useful until it gets more developed.
*/
class audio_effect
{
public:
virtual void setup(int sample_rate)=0;
virtual ~audio_effect() {}
};
class modulation_effect: public audio_effect
{
protected:
int sample_rate;
float rate, wet, dry, odsr;
gain_smoothing gs_wet, gs_dry;
public:
fixed_point<unsigned int, 20> phase, dphase;
float get_rate() {
return rate;
}
void set_rate(float rate) {
this->rate = rate;
dphase = rate/sample_rate*4096;
}
float get_wet() {
return wet;
}
void set_wet(float wet) {
this->wet = wet;
gs_wet.set_inertia(wet);
}
float get_dry() {
return dry;
}
void set_dry(float dry) {
this->dry = dry;
gs_dry.set_inertia(dry);
}
void reset_phase(float req_phase)
{
phase = req_phase * 4096.0;
}
void inc_phase(float req_phase)
{
phase += fixed_point<unsigned int, 20>(req_phase * 4096.0);
}
void setup(int sample_rate)
{
this->sample_rate = sample_rate;
this->odsr = 1.0 / sample_rate;
phase = 0;
set_rate(get_rate());
}
};
/**
* A monophonic phaser. If you want stereo, combine two :)
* Also, gave up on using template args for signal type.
*/
template<int MaxStages>
class simple_phaser: public modulation_effect
{
protected:
float base_frq, mod_depth, fb;
float state;
int cnt, stages;
dsp::onepole<float, float> stage1;
float x1[MaxStages], y1[MaxStages];
public:
simple_phaser()
{
set_base_frq(1000);
set_mod_depth(1000);
set_fb(0);
state = 0;
cnt = 0;
stages = 0;
set_stages(6);
}
float get_base_frq() {
return base_frq;
}
void set_base_frq(float _base_frq) {
base_frq = _base_frq;
}
int get_stages() {
return stages;
}
void set_stages(int _stages) {
if (_stages > stages)
{
for (int i = stages; i < _stages; i++)
{
x1[i] = x1[stages-1];
y1[i] = y1[stages-1];
}
}
stages = _stages;
}
float get_mod_depth() {
return mod_depth;
}
void set_mod_depth(float _mod_depth) {
mod_depth = _mod_depth;
}
float get_fb() {
return fb;
}
void set_fb(float fb) {
this->fb = fb;
}
virtual void setup(int sample_rate) {
modulation_effect::setup(sample_rate);
reset();
}
void reset()
{
cnt = 0;
state = 0;
phase.set(0);
for (int i = 0; i < MaxStages; i++)
x1[i] = y1[i] = 0;
control_step();
}
inline void control_step()
{
cnt = 0;
int v = phase.get() + 0x40000000;
int sign = v >> 31;
v ^= sign;
// triangle wave, range from 0 to INT_MAX
double vf = (double)((v >> 16) * (1.0 / 16384.0) - 1);
float freq = base_frq * pow(2.0, vf * mod_depth / 1200.0);
freq = dsp::clip<float>(freq, 10.0, 0.49 * sample_rate);
stage1.set_ap_w(freq * (M_PI / 2.0) * odsr);
phase += dphase * 32;
for (int i = 0; i < stages; i++)
{
dsp::sanitize(x1[i]);
dsp::sanitize(y1[i]);
}
dsp::sanitize(state);
}
void process(float *buf_out, float *buf_in, int nsamples) {
for (int i=0; i<nsamples; i++) {
cnt++;
if (cnt == 32)
control_step();
float in = *buf_in++;
float fd = in + state * fb;
for (int j = 0; j < stages; j++)
fd = stage1.process_ap(fd, x1[j], y1[j]);
state = fd;
float sdry = in * gs_dry.get();
float swet = fd * gs_wet.get();
*buf_out++ = sdry + swet;
}
}
float freq_gain(float freq, float sr)
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
cfloat p = cfloat(1.0);
cfloat stg = stage1.h_z(z);
for (int i = 0; i < stages; i++)
p = p * stg;
p = p / (cfloat(1.0) - cfloat(fb) * p);
return std::abs(cfloat(gs_dry.get_last()) + cfloat(gs_wet.get_last()) * p);
}
};
/**
* Base class for chorus and flanger. Wouldn't be needed if it wasn't
* for odd behaviour of GCC when deriving templates from template
* base classes (not seeing fields from base classes!).
*/
class chorus_base: public modulation_effect
{
protected:
int min_delay_samples, mod_depth_samples;
float min_delay, mod_depth;
sine_table<int, 4096, 65536> sine;
public:
float get_min_delay() {
return min_delay;
}
void set_min_delay(float min_delay) {
this->min_delay = min_delay;
this->min_delay_samples = (int)(min_delay * 65536.0 * sample_rate);
}
float get_mod_depth() {
return mod_depth;
}
void set_mod_depth(float mod_depth) {
this->mod_depth = mod_depth;
// 128 because it's then multiplied by (hopefully) a value of 32768..-32767
this->mod_depth_samples = (int)(mod_depth * 32.0 * sample_rate);
}
};
/**
* Single-tap chorus without feedback.
* Perhaps MaxDelay should be a bit longer!
*/
template<class T, int MaxDelay=512>
class simple_chorus: public chorus_base
{
protected:
simple_delay<MaxDelay,T> delay;
public:
simple_chorus() {
rate = 0.63f;
dry = 0.5f;
wet = 0.5f;
min_delay = 0.005f;
mod_depth = 0.0025f;
setup(44100);
}
void reset() {
delay.reset();
}
virtual void setup(int sample_rate) {
modulation_effect::setup(sample_rate);
delay.reset();
set_min_delay(get_min_delay());
set_mod_depth(get_mod_depth());
}
template<class OutIter, class InIter>
void process(OutIter buf_out, InIter buf_in, int nsamples) {
int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
int mdepth = mod_depth_samples;
for (int i=0; i<nsamples; i++) {
phase += dphase;
unsigned int ipart = phase.ipart();
float in = *buf_in++;
int lfo = phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]);
int v = mds + (mdepth * lfo >> 6);
// if (!(i & 7)) printf("%d\n", v);
int ifv = v >> 16;
delay.put(in);
T fd; // signal from delay's output
delay.get_interp(fd, ifv, (v & 0xFFFF)*(1.0/65536.0));
T sdry = in * gs_dry.get();
T swet = fd * gs_wet.get();
*buf_out++ = sdry + swet;
}
}
};
/**
* Single-tap flanger (chorus plus feedback).
*/
template<class T, int MaxDelay=1024>
class simple_flanger: public chorus_base
{
protected:
simple_delay<MaxDelay,T> delay;
float fb;
int last_delay_pos, last_actual_delay_pos;
int ramp_pos, ramp_delay_pos;
public:
simple_flanger()
: fb(0) {}
void reset() {
delay.reset();
last_delay_pos = last_actual_delay_pos = ramp_delay_pos = 0;
ramp_pos = 1024;
}
virtual void setup(int sample_rate) {
this->sample_rate = sample_rate;
this->odsr = 1.0 / sample_rate;
delay.reset();
phase = 0;
set_rate(get_rate());
set_min_delay(get_min_delay());
}
float get_fb() {
return fb;
}
void set_fb(float fb) {
this->fb = fb;
}
template<class OutIter, class InIter>
void process(OutIter buf_out, InIter buf_in, int nsamples) {
if (!nsamples)
return;
int mds = this->min_delay_samples + this->mod_depth_samples * 1024 + 2 * 65536;
int mdepth = this->mod_depth_samples;
int delay_pos;
unsigned int ipart = this->phase.ipart();
int lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
delay_pos = mds + (mdepth * lfo >> 6);
if (delay_pos != last_delay_pos || ramp_pos < 1024)
{
if (delay_pos != last_delay_pos) {
// we need to ramp from what the delay tap length actually was,
// not from old (ramp_delay_pos) or desired (delay_pos) tap length
ramp_delay_pos = last_actual_delay_pos;
ramp_pos = 0;
}
int64_t dp = 0;
for (int i=0; i<nsamples; i++) {
float in = *buf_in++;
T fd; // signal from delay's output
dp = (((int64_t)ramp_delay_pos) * (1024 - ramp_pos) + ((int64_t)delay_pos) * ramp_pos) >> 10;
ramp_pos++;
if (ramp_pos > 1024) ramp_pos = 1024;
this->delay.get_interp(fd, dp >> 16, (dp & 0xFFFF)*(1.0/65536.0));
sanitize(fd);
T sdry = in * this->dry;
T swet = fd * this->wet;
*buf_out++ = sdry + swet;
this->delay.put(in+fb*fd);
this->phase += this->dphase;
ipart = this->phase.ipart();
lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
delay_pos = mds + (mdepth * lfo >> 6);
}
last_actual_delay_pos = dp;
}
else {
for (int i=0; i<nsamples; i++) {
float in = *buf_in++;
T fd; // signal from delay's output
this->delay.get_interp(fd, delay_pos >> 16, (delay_pos & 0xFFFF)*(1.0/65536.0));
sanitize(fd);
T sdry = in * this->gs_dry.get();
T swet = fd * this->gs_wet.get();
*buf_out++ = sdry + swet;
this->delay.put(in+fb*fd);
this->phase += this->dphase;
ipart = this->phase.ipart();
lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
delay_pos = mds + (mdepth * lfo >> 6);
}
last_actual_delay_pos = delay_pos;
}
last_delay_pos = delay_pos;
}
float freq_gain(float freq, float sr)
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
float ldp = last_delay_pos / 65536.0;
float fldp = floor(ldp);
cfloat zn = std::pow(z, fldp); // z^-N
cfloat zn1 = zn * z; // z^-(N+1)
// simulate a lerped comb filter - H(z) = 1 / (1 + fb * (lerp(z^-N, z^-(N+1), fracpos))), N = int(pos), fracpos = pos - int(pos)
cfloat delayed = zn + (zn1 - zn) * cfloat(ldp - fldp);
cfloat h = cfloat(delayed) / (cfloat(1.0) - cfloat(fb) * delayed);
// mix with dry signal
float v = std::abs(cfloat(gs_dry.get_last()) + cfloat(gs_wet.get_last()) * h);
return v;
}
};
/**
* A classic allpass loop reverb with modulated allpass filter.
* Just started implementing it, so there is no control over many
* parameters.
*/
template<class T>
class reverb: public audio_effect
{
simple_delay<2048, T> apL1, apL2, apL3, apL4, apL5, apL6;
simple_delay<2048, T> apR1, apR2, apR3, apR4, apR5, apR6;
fixed_point<unsigned int, 25> phase, dphase;
sine_table<int, 128, 10000> sine;
onepole<T> lp_left, lp_right;
T old_left, old_right;
int type;
float time, fb, cutoff, diffusion;
int tl[6], tr[6];
float ldec[6], rdec[6];
int sr;
public:
reverb()
{
phase = 0.0;
time = 1.0;
cutoff = 9000;
type = 2;
diffusion = 1.f;
setup(44100);
}
virtual void setup(int sample_rate) {
sr = sample_rate;
set_time(time);
set_cutoff(cutoff);
phase = 0.0;
dphase = 0.5*128/sr;
update_times();
}
void update_times()
{
switch(type)
{
case 0:
tl[0] = 397 << 16, tr[0] = 383 << 16;
tl[1] = 457 << 16, tr[1] = 429 << 16;
tl[2] = 549 << 16, tr[2] = 631 << 16;
tl[3] = 649 << 16, tr[3] = 756 << 16;
tl[4] = 773 << 16, tr[4] = 803 << 16;
tl[5] = 877 << 16, tr[5] = 901 << 16;
break;
case 1:
tl[0] = 697 << 16, tr[0] = 783 << 16;
tl[1] = 957 << 16, tr[1] = 929 << 16;
tl[2] = 649 << 16, tr[2] = 531 << 16;
tl[3] = 1049 << 16, tr[3] = 1177 << 16;
tl[4] = 473 << 16, tr[4] = 501 << 16;
tl[5] = 587 << 16, tr[5] = 681 << 16;
break;
case 2:
default:
tl[0] = 697 << 16, tr[0] = 783 << 16;
tl[1] = 957 << 16, tr[1] = 929 << 16;
tl[2] = 649 << 16, tr[2] = 531 << 16;
tl[3] = 1249 << 16, tr[3] = 1377 << 16;
tl[4] = 1573 << 16, tr[4] = 1671 << 16;
tl[5] = 1877 << 16, tr[5] = 1781 << 16;
break;
case 3:
tl[0] = 1097 << 16, tr[0] = 1087 << 16;
tl[1] = 1057 << 16, tr[1] = 1031 << 16;
tl[2] = 1049 << 16, tr[2] = 1039 << 16;
tl[3] = 1083 << 16, tr[3] = 1055 << 16;
tl[4] = 1075 << 16, tr[4] = 1099 << 16;
tl[5] = 1003 << 16, tr[5] = 1073 << 16;
break;
case 4:
tl[0] = 197 << 16, tr[0] = 133 << 16;
tl[1] = 357 << 16, tr[1] = 229 << 16;
tl[2] = 549 << 16, tr[2] = 431 << 16;
tl[3] = 949 << 16, tr[3] = 1277 << 16;
tl[4] = 1173 << 16, tr[4] = 1671 << 16;
tl[5] = 1477 << 16, tr[5] = 1881 << 16;
break;
case 5:
tl[0] = 197 << 16, tr[0] = 133 << 16;
tl[1] = 257 << 16, tr[1] = 179 << 16;
tl[2] = 549 << 16, tr[2] = 431 << 16;
tl[3] = 619 << 16, tr[3] = 497 << 16;
tl[4] = 1173 << 16, tr[4] = 1371 << 16;
tl[5] = 1577 << 16, tr[5] = 1881 << 16;
break;
}
float fDec=1000 + 2400.f * diffusion;
for (int i = 0 ; i < 6; i++) {
ldec[i]=exp(-float(tl[i] >> 16) / fDec),
rdec[i]=exp(-float(tr[i] >> 16) / fDec);
}
}
float get_time() {
return time;
}
void set_time(float time) {
this->time = time;
// fb = pow(1.0f/4096.0f, (float)(1700/(time*sr)));
fb = 1.0 - 0.3 / (time * sr / 44100.0);
}
float get_type() {
return type;
}
void set_type(int type) {
this->type = type;
update_times();
}
float get_diffusion() {
return diffusion;
}
void set_diffusion(float diffusion) {
this->diffusion = diffusion;
update_times();
}
void set_type_and_diffusion(int type, float diffusion) {
this->type = type;
this->diffusion = diffusion;
update_times();
}
float get_fb()
{
return this->fb;
}
void set_fb(float fb)
{
this->fb = fb;
}
float get_cutoff() {
return cutoff;
}
void set_cutoff(float cutoff) {
this->cutoff = cutoff;
lp_left.set_lp(cutoff,sr);
lp_right.set_lp(cutoff,sr);
}
void reset()
{
apL1.reset();apR1.reset();
apL2.reset();apR2.reset();
apL3.reset();apR3.reset();
apL4.reset();apR4.reset();
apL5.reset();apR5.reset();
apL6.reset();apR6.reset();
lp_left.reset();lp_right.reset();
old_left = 0; old_right = 0;
}
void process(T &left, T &right)
{
unsigned int ipart = phase.ipart();
// the interpolated LFO might be an overkill here
int lfo = phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]) >> 2;
phase += dphase;
left += old_right;
left = apL1.process_allpass_comb_lerp16(left, tl[0] - 45*lfo, ldec[0]);
left = apL2.process_allpass_comb_lerp16(left, tl[1] + 47*lfo, ldec[1]);
float out_left = left;
left = apL3.process_allpass_comb_lerp16(left, tl[2] + 54*lfo, ldec[2]);
left = apL4.process_allpass_comb_lerp16(left, tl[3] - 69*lfo, ldec[3]);
left = apL5.process_allpass_comb_lerp16(left, tl[4] + 69*lfo, ldec[4]);
left = apL6.process_allpass_comb_lerp16(left, tl[5] - 46*lfo, ldec[5]);
old_left = lp_left.process(left * fb);
sanitize(old_left);
right += old_left;
right = apR1.process_allpass_comb_lerp16(right, tr[0] - 45*lfo, rdec[0]);
right = apR2.process_allpass_comb_lerp16(right, tr[1] + 47*lfo, rdec[1]);
float out_right = right;
right = apR3.process_allpass_comb_lerp16(right, tr[2] + 54*lfo, rdec[2]);
right = apR4.process_allpass_comb_lerp16(right, tr[3] - 69*lfo, rdec[3]);
right = apR5.process_allpass_comb_lerp16(right, tr[4] + 69*lfo, rdec[4]);
right = apR6.process_allpass_comb_lerp16(right, tr[5] - 46*lfo, rdec[5]);
old_right = lp_right.process(right * fb);
sanitize(old_right);
left = out_left, right = out_right;
}
void extra_sanitize()
{
lp_left.sanitize();
lp_right.sanitize();
}
};
class filter_module_iface
{
public:
virtual void calculate_filter(float freq, float q, int mode, float gain = 1.0) = 0;
virtual void filter_activate() = 0;
virtual void sanitize() = 0;
virtual int process_channel(uint16_t channel_no, float *in, float *out, uint32_t numsamples, int inmask) = 0;
virtual float freq_gain(int subindex, float freq, float srate) = 0;
virtual ~filter_module_iface() {}
};
class biquad_filter_module: public filter_module_iface
{
private:
dsp::biquad_d1<float> left[3], right[3];
int order;
public:
uint32_t srate;
enum { mode_12db_lp = 0, mode_24db_lp = 1, mode_36db_lp = 2,
mode_12db_hp = 3, mode_24db_hp = 4, mode_36db_hp = 5,
mode_6db_bp = 6, mode_12db_bp = 7, mode_18db_bp = 8,
mode_6db_br = 9, mode_12db_br = 10, mode_18db_br = 11,
mode_count
};
public:
biquad_filter_module() : order(0) {}
void calculate_filter(float freq, float q, int mode, float gain = 1.0)
{
if (mode <= mode_36db_lp) {
order = mode + 1;
left[0].set_lp_rbj(freq, pow(q, 1.0 / order), srate, gain);
} else if ( mode_12db_hp <= mode && mode <= mode_36db_hp ) {
order = mode - mode_12db_hp + 1;
left[0].set_hp_rbj(freq, pow(q, 1.0 / order), srate, gain);
} else if ( mode_6db_bp <= mode && mode <= mode_18db_bp ) {
order = mode - mode_6db_bp + 1;
left[0].set_bp_rbj(freq, pow(q, 1.0 / order), srate, gain);
} else { // mode_6db_br <= mode <= mode_18db_br
order = mode - mode_6db_br + 1;
left[0].set_br_rbj(freq, order * 0.1 * q, srate, gain);
}
right[0].copy_coeffs(left[0]);
for (int i = 1; i < order; i++) {
left[i].copy_coeffs(left[0]);
right[i].copy_coeffs(left[0]);
}
}
void filter_activate()
{
for (int i=0; i < order; i++) {
left[i].reset();
right[i].reset();
}
}
void sanitize()
{
for (int i=0; i < order; i++) {
left[i].sanitize();
right[i].sanitize();
}
}
inline int process_channel(uint16_t channel_no, float *in, float *out, uint32_t numsamples, int inmask) {
dsp::biquad_d1<float> *filter;
switch (channel_no) {
case 0:
filter = left;
break;
case 1:
filter = right;
break;
default:
assert(false);
return 0;
}
if (inmask) {
switch(order) {
case 1:
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[0].process(in[i]);
break;
case 2:
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[1].process(filter[0].process(in[i]));
break;
case 3:
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[2].process(filter[1].process(filter[0].process(in[i])));
break;
}
} else {
if (filter[order - 1].empty())
return 0;
switch(order) {
case 1:
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[0].process_zeroin();
break;
case 2:
if (filter[0].empty())
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[1].process_zeroin();
else
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[1].process(filter[0].process_zeroin());
break;
case 3:
if (filter[1].empty())
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[2].process_zeroin();
else
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[2].process(filter[1].process(filter[0].process_zeroin()));
break;
}
}
for (int i = 0; i < order; i++)
filter[i].sanitize();
return filter[order - 1].empty() ? 0 : inmask;
}
float freq_gain(int subindex, float freq, float srate)
{
float level = 1.0;
for (int j = 0; j < order; j++)
level *= left[j].freq_gain(freq, srate);
return level;
}
};
class two_band_eq
{
private:
dsp::onepole<float> lowcut, highcut;
float low_gain, high_gain;
public:
void reset()
{
lowcut.reset();
highcut.reset();
}
inline float process(float v)
{
v = dsp::lerp(lowcut.process_hp(v), v, low_gain);
v = dsp::lerp(highcut.process_lp(v), v, high_gain);
return v;
}
inline void copy_coeffs(const two_band_eq &src)
{
lowcut.copy_coeffs(src.lowcut);
highcut.copy_coeffs(src.highcut);
low_gain = src.low_gain;
high_gain = src.high_gain;
}
void sanitize()
{
lowcut.sanitize();
highcut.sanitize();
}
void set(float _low_freq, float _low_gain, float _high_freq, float _high_gain, float sr)
{
lowcut.set_hp(_low_freq, sr);
highcut.set_lp(_high_freq, sr);
low_gain = _low_gain;
high_gain = _high_gain;
}
};
#if 0
{ to keep editor happy
#endif
}
#endif

View File

@@ -0,0 +1,580 @@
/* Calf DSP Library
* Biquad filters
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* Most of code in this file is based on freely
* available other work of other people (filter equations).
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __CALF_BIQUAD_H
#define __CALF_BIQUAD_H
#include <complex>
#include "primitives.h"
namespace dsp {
/**
* Coefficients for two-pole two-zero filter, for floating point values,
* plus a bunch of functions to set them to typical values.
*
* Coefficient calculation is based on famous Robert Bristow-Johnson's equations,
* except where it's not.
* The coefficient calculation is NOT mine, the only exception is the lossy
* optimization in Zoelzer and rbj HP filter code.
*
* See http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt for reference.
*
* don't use this for integers because it won't work
*/
template<class Coeff = float>
class biquad_coeffs
{
public:
// filter coefficients
Coeff a0, a1, a2, b1, b2;
typedef std::complex<double> cfloat;
biquad_coeffs()
{
set_null();
}
inline void set_null()
{
a0 = 1.0;
b1 = b2 = a1 = a2 = 0.f;
}
/** Lowpass filter based on Robert Bristow-Johnson's equations
* Perhaps every synth code that doesn't use SVF uses these
* equations :)
* @param fc resonant frequency
* @param q resonance (gain at fc)
* @param sr sample rate
* @param gain amplification (gain at 0Hz)
*/
inline void set_lp_rbj(float fc, float q, float sr, float gain = 1.0)
{
float omega=(float)(2*M_PI*fc/sr);
float sn=sin(omega);
float cs=cos(omega);
float alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a2 = a0 = (float)(gain*inv*(1 - cs)*0.5f);
a1 = a0 + a0;
b1 = (float)(-2*cs*inv);
b2 = (float)((1 - alpha)*inv);
}
// different lowpass filter, based on Zoelzer's equations, modified by
// me (kfoltman) to use polynomials to approximate tangent function
// not very accurate, but perhaps good enough for synth work :)
// odsr is "one divided by samplerate"
// from how it looks, it perhaps uses bilinear transform - but who knows :)
inline void set_lp_zoelzer(float fc, float q, float odsr, float gain=1.0)
{
Coeff omega=(Coeff)(M_PI*fc*odsr);
Coeff omega2=omega*omega;
Coeff K=omega*(1+omega2*omega2*Coeff(1.0/1.45));
Coeff KK=K*K;
Coeff QK=q*(KK+1.f);
Coeff iQK=1.0f/(QK+K);
Coeff inv=q*iQK;
b2 = (Coeff)(iQK*(QK-K));
b1 = (Coeff)(2.f*(KK-1.f)*inv);
a2 = a0 = (Coeff)(inv*gain*KK);
a1 = a0 + a0;
}
/** Highpass filter based on Robert Bristow-Johnson's equations
* @param fc resonant frequency
* @param q resonance (gain at fc)
* @param sr sample rate
* @param gain amplification (gain at sr/2)
*/
inline void set_hp_rbj(float fc, float q, float esr, float gain=1.0)
{
Coeff omega=(float)(2*M_PI*fc/esr);
Coeff sn=sin(omega);
Coeff cs=cos(omega);
Coeff alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a0 = (Coeff)(gain*inv*(1 + cs)/2);
a1 = -2.f * a0;
a2 = a0;
b1 = (Coeff)(-2*cs*inv);
b2 = (Coeff)((1 - alpha)*inv);
}
// this replaces sin/cos with polynomial approximation
inline void set_hp_rbj_optimized(float fc, float q, float esr, float gain=1.0)
{
Coeff omega=(float)(2*M_PI*fc/esr);
Coeff sn=omega+omega*omega*omega*(1.0/6.0)+omega*omega*omega*omega*omega*(1.0/120);
Coeff cs=1-omega*omega*(1.0/2.0)+omega*omega*omega*omega*(1.0/24);
Coeff alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a0 = (Coeff)(gain*inv*(1 + cs)*(1.0/2.0));
a1 = -2.f * a0;
a2 = a0;
b1 = (Coeff)(-2*cs*inv);
b2 = (Coeff)((1 - alpha)*inv);
}
/** Bandpass filter based on Robert Bristow-Johnson's equations (normalized to 1.0 at center frequency)
* @param fc center frequency (gain at fc = 1.0)
* @param q =~ fc/bandwidth (not quite, but close) - 1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0))
* @param sr sample rate
* @param gain amplification (gain at sr/2)
*/
inline void set_bp_rbj(double fc, double q, double esr, double gain=1.0)
{
float omega=(float)(2*M_PI*fc/esr);
float sn=sin(omega);
float cs=cos(omega);
float alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a0 = (float)(gain*inv*alpha);
a1 = 0.f;
a2 = (float)(-gain*inv*alpha);
b1 = (float)(-2*cs*inv);
b2 = (float)((1 - alpha)*inv);
}
// rbj's bandreject
inline void set_br_rbj(double fc, double q, double esr, double gain=1.0)
{
float omega=(float)(2*M_PI*fc/esr);
float sn=sin(omega);
float cs=cos(omega);
float alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a0 = (Coeff)(gain*inv);
a1 = (Coeff)(-gain*inv*2*cs);
a2 = (Coeff)(gain*inv);
b1 = (Coeff)(-2*cs*inv);
b2 = (Coeff)((1 - alpha)*inv);
}
// this is mine (and, I guess, it sucks/doesn't work)
void set_allpass(float freq, float pole_r, float sr)
{
float a=prewarp(freq, sr);
float q=pole_r;
set_bilinear(a*a+q*q, -2.0f*a, 1, a*a+q*q, 2.0f*a, 1);
}
/// prewarping for bilinear transform, maps given digital frequency to analog counterpart for analog filter design
static inline float prewarp(float freq, float sr)
{
if (freq>sr*0.49) freq=(float)(sr*0.49);
return (float)(tan(M_PI*freq/sr));
}
/// convert analog angular frequency value to digital
static inline float unwarp(float omega, float sr)
{
float T = 1.0 / sr;
return (2 / T) * atan(omega * T / 2);
}
/// convert analog filter time constant to digital counterpart
static inline float unwarpf(float t, float sr)
{
// this is most likely broken and works by pure accident!
float omega = 1.0 / t;
omega = unwarp(omega, sr);
// I really don't know why does it have to be M_PI and not 2 * M_PI!
float f = M_PI / omega;
return f / sr;
}
/// set digital filter parameters based on given analog filter parameters
void set_bilinear(float aa0, float aa1, float aa2, float ab0, float ab1, float ab2)
{
float q=(float)(1.0/(ab0+ab1+ab2));
a0 = (aa0+aa1+aa2)*q;
a1 = 2*(aa0-aa2)*q;
a2 = (aa0-aa1+aa2)*q;
b1 = 2*(ab0-ab2)*q;
b2 = (ab0-ab1+ab2)*q;
}
/// RBJ peaking EQ
/// @param freq peak frequency
/// @param q q (correlated to freq/bandwidth, @see set_bp_rbj)
/// @param peak peak gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
inline void set_peakeq_rbj(float freq, float q, float peak, float sr)
{
float A = sqrt(peak);
float w0 = freq * 2 * M_PI * (1.0 / sr);
float alpha = sin(w0) / (2 * q);
float ib0 = 1.0 / (1 + alpha/A);
a1 = b1 = -2*cos(w0) * ib0;
a0 = ib0 * (1 + alpha*A);
a2 = ib0 * (1 - alpha*A);
b2 = ib0 * (1 - alpha/A);
}
/// RBJ low shelf EQ - amplitication of 'peak' at 0 Hz and of 1.0 (0dB) at sr/2 Hz
/// @param freq corner frequency (gain at freq is sqrt(peak))
/// @param q q (relates bandwidth and peak frequency), the higher q, the louder the resonant peak (situated below fc) is
/// @param peak shelf gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
inline void set_lowshelf_rbj(float freq, float q, float peak, float sr)
{
float A = sqrt(peak);
float w0 = freq * 2 * M_PI * (1.0 / sr);
float alpha = sin(w0) / (2 * q);
float cw0 = cos(w0);
float tmp = 2 * sqrt(A) * alpha;
float b0 = 0.f, ib0 = 0.f;
a0 = A*( (A+1) - (A-1)*cw0 + tmp);
a1 = 2*A*( (A-1) - (A+1)*cw0);
a2 = A*( (A+1) - (A-1)*cw0 - tmp);
b0 = (A+1) + (A-1)*cw0 + tmp;
b1 = -2*( (A-1) + (A+1)*cw0);
b2 = (A+1) + (A-1)*cw0 - tmp;
ib0 = 1.0 / b0;
b1 *= ib0;
b2 *= ib0;
a0 *= ib0;
a1 *= ib0;
a2 *= ib0;
}
/// RBJ high shelf EQ - amplitication of 0dB at 0 Hz and of peak at sr/2 Hz
/// @param freq corner frequency (gain at freq is sqrt(peak))
/// @param q q (relates bandwidth and peak frequency), the higher q, the louder the resonant peak (situated above fc) is
/// @param peak shelf gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
inline void set_highshelf_rbj(float freq, float q, float peak, float sr)
{
float A = sqrt(peak);
float w0 = freq * 2 * M_PI * (1.0 / sr);
float alpha = sin(w0) / (2 * q);
float cw0 = cos(w0);
float tmp = 2 * sqrt(A) * alpha;
float b0 = 0.f, ib0 = 0.f;
a0 = A*( (A+1) + (A-1)*cw0 + tmp);
a1 = -2*A*( (A-1) + (A+1)*cw0);
a2 = A*( (A+1) + (A-1)*cw0 - tmp);
b0 = (A+1) - (A-1)*cw0 + tmp;
b1 = 2*( (A-1) - (A+1)*cw0);
b2 = (A+1) - (A-1)*cw0 - tmp;
ib0 = 1.0 / b0;
b1 *= ib0;
b2 *= ib0;
a0 *= ib0;
a1 *= ib0;
a2 *= ib0;
}
/// copy coefficients from another biquad
template<class U>
inline void copy_coeffs(const biquad_coeffs<U> &src)
{
a0 = src.a0;
a1 = src.a1;
a2 = src.a2;
b1 = src.b1;
b2 = src.b2;
}
/// Return the filter's gain at frequency freq
/// @param freq Frequency to look up
/// @param sr Filter sample rate (used to convert frequency to angular frequency)
float freq_gain(float freq, float sr)
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq));
return std::abs(h_z(z));
}
/// Return H(z) the filter's gain at frequency freq
/// @param z Z variable (e^jw)
cfloat h_z(const cfloat &z)
{
return (cfloat(a0) + double(a1) * z + double(a2) * z*z) / (cfloat(1.0) + double(b1) * z + double(b2) * z*z);
}
};
/**
* Two-pole two-zero filter, for floating point values.
* Uses "traditional" Direct I form (separate FIR and IIR halves).
* don't use this for integers because it won't work
*/
template<class Coeff = float, class T = float>
struct biquad_d1: public biquad_coeffs<Coeff>
{
using biquad_coeffs<Coeff>::a0;
using biquad_coeffs<Coeff>::a1;
using biquad_coeffs<Coeff>::a2;
using biquad_coeffs<Coeff>::b1;
using biquad_coeffs<Coeff>::b2;
/// input[n-1]
T x1;
/// input[n-2]
T x2;
/// output[n-1]
T y1;
/// output[n-2]
T y2;
/// Constructor (initializes state to all zeros)
biquad_d1()
{
reset();
}
/// direct I form with four state variables
inline T process(T in)
{
T out = in * a0 + x1 * a1 + x2 * a2 - y1 * b1 - y2 * b2;
x2 = x1;
y2 = y1;
x1 = in;
y1 = out;
return out;
}
/// direct I form with zero input
inline T process_zeroin()
{
T out = - y1 * b1 - y2 * b2;
y2 = y1;
y1 = out;
return out;
}
/// simplified version for lowpass case with two zeros at -1
inline T process_lp(T in)
{
T out = a0*(in + x1 + x1 + x2) - y1 * b1 - y2 * b2;
x2 = x1;
y2 = y1;
x1 = in;
y1 = out;
return out;
}
/// Sanitize (set to 0 if potentially denormal) filter state
inline void sanitize()
{
dsp::sanitize(x1);
dsp::sanitize(y1);
dsp::sanitize(x2);
dsp::sanitize(y2);
}
/// Reset state variables
inline void reset()
{
dsp::zero(x1);
dsp::zero(y1);
dsp::zero(x2);
dsp::zero(y2);
}
inline bool empty() {
return (y1 == 0.f && y2 == 0.f);
}
};
/**
* Two-pole two-zero filter, for floating point values.
* Uses slightly faster Direct II form (combined FIR and IIR halves).
* However, when used with wildly varying coefficients, it may
* make more zipper noise than Direct I form, so it's better to
* use it when filter coefficients are not changed mid-stream.
*/
template<class Coeff = float, class T = float>
struct biquad_d2: public biquad_coeffs<Coeff>
{
using biquad_coeffs<Coeff>::a0;
using biquad_coeffs<Coeff>::a1;
using biquad_coeffs<Coeff>::a2;
using biquad_coeffs<Coeff>::b1;
using biquad_coeffs<Coeff>::b2;
/// state[n-1]
float w1;
/// state[n-2]
float w2;
/// Constructor (initializes state to all zeros)
biquad_d2()
{
reset();
}
/// direct II form with two state variables
inline T process(T in)
{
T tmp = in - w1 * b1 - w2 * b2;
T out = tmp * a0 + w1 * a1 + w2 * a2;
w2 = w1;
w1 = tmp;
return out;
}
// direct II form with two state variables, lowpass version
// interesting fact: this is actually slower than the general version!
inline T process_lp(T in)
{
T tmp = in - w1 * b1 - w2 * b2;
T out = (tmp + w2 + w1* 2) * a0;
w2 = w1;
w1 = tmp;
return out;
}
/// Is the filter state completely silent? (i.e. set to 0 by sanitize function)
inline bool empty() {
return (w1 == 0.f && w2 == 0.f);
}
/// Sanitize (set to 0 if potentially denormal) filter state
inline void sanitize()
{
dsp::sanitize(w1);
dsp::sanitize(w2);
}
/// Reset state variables
inline void reset()
{
dsp::zero(w1);
dsp::zero(w2);
}
};
/**
* Two-pole two-zero filter, for floating point values.
* Uses "traditional" Direct I form (separate FIR and IIR halves).
* don't use this for integers because it won't work
*/
template<class Coeff = float, class T = float>
struct biquad_d1_lerp: public biquad_coeffs<Coeff>
{
using biquad_coeffs<Coeff>::a0;
using biquad_coeffs<Coeff>::a1;
using biquad_coeffs<Coeff>::a2;
using biquad_coeffs<Coeff>::b1;
using biquad_coeffs<Coeff>::b2;
Coeff a0cur, a1cur, a2cur, b1cur, b2cur;
Coeff a0delta, a1delta, a2delta, b1delta, b2delta;
/// input[n-1]
T x1;
/// input[n-2]
T x2;
/// output[n-1]
T y1;
/// output[n-2]
T y2;
/// Constructor (initializes state to all zeros)
biquad_d1_lerp()
{
reset();
}
#define _DO_COEFF(coeff) coeff##delta = (coeff - coeff##cur) * (frac)
void big_step(Coeff frac)
{
_DO_COEFF(a0);
_DO_COEFF(a1);
_DO_COEFF(a2);
_DO_COEFF(b1);
_DO_COEFF(b2);
}
#undef _DO_COEFF
/// direct I form with four state variables
inline T process(T in)
{
T out = in * a0cur + x1 * a1cur + x2 * a2cur - y1 * b1cur - y2 * b2cur;
x2 = x1;
y2 = y1;
x1 = in;
y1 = out;
a0cur += a0delta;
a1cur += a1delta;
a2cur += a2delta;
b1cur += b1delta;
b2cur += b2delta;
return out;
}
/// direct I form with zero input
inline T process_zeroin()
{
T out = - y1 * b1 - y2 * b2;
y2 = y1;
y1 = out;
b1cur += b1delta;
b2cur += b2delta;
return out;
}
/// simplified version for lowpass case with two zeros at -1
inline T process_lp(T in)
{
T out = a0*(in + x1 + x1 + x2) - y1 * b1 - y2 * b2;
x2 = x1;
y2 = y1;
x1 = in;
y1 = out;
return out;
}
/// Sanitize (set to 0 if potentially denormal) filter state
inline void sanitize()
{
dsp::sanitize(x1);
dsp::sanitize(y1);
dsp::sanitize(x2);
dsp::sanitize(y2);
dsp::sanitize(a0cur);
dsp::sanitize(a1cur);
dsp::sanitize(a2cur);
dsp::sanitize(b1cur);
dsp::sanitize(b2cur);
}
/// Reset state variables
inline void reset()
{
dsp::zero(x1);
dsp::zero(y1);
dsp::zero(x2);
dsp::zero(y2);
dsp::zero(a0cur);
dsp::zero(a1cur);
dsp::zero(a2cur);
dsp::zero(b1cur);
dsp::zero(b2cur);
}
inline bool empty() {
return (y1 == 0.f && y2 == 0.f);
}
};
};
#endif

View File

@@ -0,0 +1,229 @@
/* Calf DSP Library
* Buffer abstractions.
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __BUFFER_H
#define __BUFFER_H
namespace dsp {
/// decrease by N if >= N (useful for circular buffers)
template<int N> inline int wrap_around(int a) {
return (a >= N) ? a - N : a;
}
// provide fast specializations for powers of 2
template<> inline int wrap_around<2>(int a) { return a & 1; }
template<> inline int wrap_around<4>(int a) { return a & 3; }
template<> inline int wrap_around<8>(int a) { return a & 7; }
template<> inline int wrap_around<16>(int a) { return a & 15; }
template<> inline int wrap_around<32>(int a) { return a & 31; }
template<> inline int wrap_around<64>(int a) { return a & 63; }
template<> inline int wrap_around<128>(int a) { return a & 127; }
template<> inline int wrap_around<256>(int a) { return a & 255; }
template<> inline int wrap_around<512>(int a) { return a & 511; }
template<> inline int wrap_around<1024>(int a) { return a & 1023; }
template<> inline int wrap_around<2048>(int a) { return a & 2047; }
template<> inline int wrap_around<4096>(int a) { return a & 4095; }
template<> inline int wrap_around<8192>(int a) { return a & 8191; }
template<> inline int wrap_around<16384>(int a) { return a & 16383; }
template<> inline int wrap_around<32768>(int a) { return a & 32767; }
template<> inline int wrap_around<65536>(int a) { return a & 65535; }
template<class Buf, class T>
void fill(Buf &buf, T value) {
T* data = buf.data();
int size = buf.size();
for (int i=0; i<size; i++)
*data++ = value;
}
template<class T>
void fill(T *data, int size, T value) {
for (int i=0; i<size; i++)
*data++ = value;
}
template<class T, class U>
void copy(T *dest, U *src, int size, T scale = 1, T add = 0) {
for (int i=0; i<size; i++)
*dest++ = (*src++) * scale + add;
}
template<class T>
struct sample_traits {
enum {
channels = 1,
bps = sizeof(T)*8
};
};
template<class T>
struct sample_traits<stereo_sample<T> > {
enum {
channels = 2,
bps = sizeof(T)*8
};
};
template<int N, class T = float>
class fixed_size_buffer {
public:
typedef T data_type;
enum { buffer_size = N };
inline int size() { return N; }
};
template<int N, class T = float>
class mem_fixed_size_buffer: public fixed_size_buffer<N, T> {
T *buf;
public:
mem_fixed_size_buffer(T ubuf[N]) { buf = ubuf; }
void set_data(T buf[N]) { this->buf = buf; }
inline T* data() { return buf; }
inline const T* data() const { return buf; }
inline T& operator[](int pos) { return buf[pos]; }
inline const T& operator[](int pos) const { return buf[pos]; }
};
template<int N, class T = float>
class auto_buffer: public fixed_size_buffer<N, T> {
T buf[N];
public:
T* data() const { return buf; }
inline T& operator[](int pos) { return buf[pos]; }
inline const T& operator[](int pos) const { return buf[pos]; }
};
template<class T = float>
class dynamic_buffer {
T *buf;
int buf_size;
bool owns;
public:
dynamic_buffer() { owns = false; }
dynamic_buffer(T *_buf, int _buf_size, bool _own)
: buf(_buf), buf_size(_buf_size), owns(_own) {
}
dynamic_buffer(int _size) {
buf = new T[_size];
buf_size = _size;
owns = true;
}
inline T* data() { return buf; }
inline const T* data() const { return buf; }
inline int size() { return buf_size; }
void resize(int new_size, bool fill_with_zeros = false) {
T *new_buf = new T[new_size];
memcpy(new_buf, buf, std::min(buf_size, new_size));
if (fill_with_zeros && buf_size < new_size)
dsp::zero(new_buf + buf_size, new_size - buf_size);
if (owns)
delete []buf;
buf = new_buf;
buf_size = new_size;
owns = true;
}
inline T& operator[](int pos) { return buf[pos]; }
inline const T& operator[](int pos) const { return buf[pos]; }
~dynamic_buffer() {
if (owns)
delete []buf;
}
};
template<class T, class U>
void copy_buf(T &dest_buf, const U &src_buf, T scale = 1, T add = 0) {
typedef typename T::data_type data_type;
data_type *dest = dest_buf.data();
const data_type *src = src_buf.data();
int size = src.size();
for (int i=0; i<size; i++)
*dest++ = (*src++) * scale + add;
}
template<class T>
struct buffer_traits {
};
/// this class template defines some basic position operations for fixed_size_buffers
template<int N, class T>
struct buffer_traits<fixed_size_buffer<N, T> > {
int inc_wrap(int pos) const {
return wrap_around<T::size>(pos+1);
}
int pos_diff(int pos1, int pos2) const {
int pos = pos1 - pos2;
if (pos < 0) pos += T::size;
return pos;
}
};
/// this is useless for now (and untested too)
template<class B>
class circular_buffer: public B {
typedef typename B::data_type data_type;
typedef class buffer_traits<B> traits;
B buffer;
int rpos, wpos;
circular_buffer() {
clear();
}
void clear() {
rpos = 0;
wpos = 0;
}
inline void put(data_type data) {
buffer[wpos] = data;
wpos = traits::inc_wrap(wpos);
}
inline bool empty() {
return rpos == wpos;
}
inline bool full() {
return rpos == traits::inc_wrap(wpos);
}
inline const data_type& get() {
int oldrpos = rpos;
rpos = traits::inc_wrap(rpos);
return buffer[oldrpos];
}
inline int get_rbytes() {
return traits::pos_diff(wpos, rpos);
}
inline int get_wbytes() {
if (full()) return 0;
return traits::pos_diff(rpos, wpos);
}
};
/// this is useless for now
template<int N, class T = float>
class mono_auto_buffer: public auto_buffer<N, T> {
};
/// this is useless for now
template<int N, class T = float>
class stereo_auto_buffer: public auto_buffer<N, stereo_sample<T> > {
};
};
#endif

View File

@@ -0,0 +1,185 @@
/* Calf DSP Library
* Reusable audio effect classes.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __CALF_DELAY_H
#define __CALF_DELAY_H
#include "primitives.h"
#include "buffer.h"
#include "onepole.h"
namespace dsp {
/**
* Delay primitive. Can be used for most delay stuff, including
* variable (modulated) delays like chorus/flanger. Note that
* for modulated delay effects use of GetInterp is preferred,
* because it handles fractional positions and uses linear
* interpolation, which sounds better most of the time.
*
* @param N maximum length
* @param C number of channels read/written for each sample (1 mono, 2 stereo etc)
*/
template<int N, class T>
struct simple_delay {
auto_buffer<N, T> data;
int pos;
simple_delay() {
reset();
}
void reset() {
pos = 0;
for (int i=0; i<N; i++)
zero(data[i]);
}
/** Write one C-channel sample from idata[0], idata[1] etc into buffer */
inline void put(T idata) {
data[pos] = idata;
pos = wrap_around<N>(pos+1);
}
/**
* Read one C-channel sample into odata[0], odata[1] etc into buffer.
* Don't use for modulated delays (chorus/flanger etc) unless you
* want them to crackle and generally sound ugly
* @param odata pointer to write into
* @param delay delay relative to current writing pos
*/
template<class U>
inline void get(U &odata, int delay) {
assert(delay >= 0 && delay < N);
int ppos = wrap_around<N>(pos + N - delay);
odata = data[ppos];
}
/**
* Read and write during the same function call
*/
inline T process(T idata, int delay)
{
assert(delay >= 0 && delay < N);
int ppos = wrap_around<N>(pos + N - delay);
T odata = data[ppos];
data[pos] = idata;
pos = wrap_around<N>(pos+1);
return odata;
}
/** Read one C-channel sample at fractional position.
* This version can be used for modulated delays, because
* it uses linear interpolation.
* @param odata value to write into
* @param delay delay relative to current writing pos
* @param udelay fractional delay (0..1)
*/
template<class U>
inline void get_interp(U &odata, int delay, float udelay) {
// assert(delay >= 0 && delay < N-1);
int ppos = wrap_around<N>(pos + N - delay);
int pppos = wrap_around<N>(ppos + N - 1);
odata = lerp(data[ppos], data[pppos], udelay);
}
/** Read one C-channel sample at fractional position.
* This version can be used for modulated delays, because
* it uses linear interpolation.
* @param odata value to write into
* @param delay delay relative to current writing pos
* @param udelay fractional delay (0..1)
*/
inline T get_interp_1616(unsigned int delay) {
float udelay = (float)((delay & 0xFFFF) * (1.0 / 65536.0));
delay = delay >> 16;
// assert(delay >= 0 && delay < N-1);
int ppos = wrap_around<N>(pos + N - delay);
int pppos = wrap_around<N>(ppos + N - 1);
return lerp(data[ppos], data[pppos], udelay);
}
/**
* Comb filter. Feedback delay line with given delay and feedback values
* @param in input signal
* @param delay delay length (must be <N and integer)
* @param fb feedback (must be <1 or it will be unstable)
*/
inline T process_comb(T in, unsigned int delay, float fb)
{
T old, cur;
get(old, delay);
cur = in + fb*old;
sanitize(cur);
put(cur);
return old;
}
/**
* Comb filter with linear interpolation. Feedback delay line with given delay and feedback values
* Note that linear interpolation introduces some weird effects in frequency response.
* @param in input signal
* @param delay fractional delay length (must be < 65536 * N)
* @param fb feedback (must be <1 or it will be unstable)
*/
inline T process_comb_lerp16(T in, unsigned int delay, float udelay, float fb)
{
T old, cur;
get_interp(old, delay>>16, dsp::fract16(delay));
cur = in + fb*old;
sanitize(cur);
put(cur);
return old;
}
/**
* Comb allpass filter. The comb filter with additional direct path, which is supposed to cancel the coloration.
* @param in input signal
* @param delay delay length (must be <N and integer)
* @param fb feedback (must be <1 or it will be unstable)
*/
inline T process_allpass_comb(T in, unsigned int delay, float fb)
{
T old, cur;
get(old, delay);
cur = in + fb*old;
sanitize(cur);
put(cur);
return old - fb * cur;
}
/**
* Comb allpass filter. The comb filter with additional direct path, which is supposed to cancel the coloration.
* @param in input signal
* @param delay fractional delay length (must be < 65536 * N)
* @param fb feedback (must be <1 or it will be unstable)
*/
inline T process_allpass_comb_lerp16(T in, unsigned int delay, float fb)
{
T old, cur;
get_interp(old, delay>>16, dsp::fract16(delay));
cur = in + fb*old;
sanitize(cur);
put(cur);
return old - fb * cur;
}
};
};
#endif

View File

@@ -0,0 +1,206 @@
/* Calf DSP Library
* ADSR envelope class (and other envelopes in future)
*
* Copyright (C) 2007-2008 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __CALF_ENVELOPE_H
#define __CALF_ENVELOPE_H
#include "primitives.h"
namespace dsp {
/// Rate-based ADSFR envelope class. Note that if release rate is slower than decay
/// rate, this envelope won't use release rate until output level falls below sustain level
/// it's different to what certain hardware synth companies did, but it prevents the very
/// un-musical (IMHO) behaviour known from (for example) SoundFont 2.
class adsr
{
public:
enum env_state {
STOP, ///< envelope is stopped
ATTACK, ///< attack - rise from 0 to 1
DECAY, ///< decay - fall from 1 to sustain level
SUSTAIN, ///< sustain - remain at sustain level (unless sustain is 0 - then it gets stopped); with fade != 0 it goes towards 0% (positive fade) or 100% (negative fade)
RELEASE, ///< release - fall from sustain (or pre-sustain) level to 0
LOCKDECAY, ///< locked decay
};
/// Current envelope stage
env_state state;
/// @note these are *rates*, not times
double attack, decay, sustain, release, fade;
/// Requested release time (not the rate!) in frames, used for recalculating the rate if sustain is changed
double release_time;
/// Current envelope (output) level
double value;
/// Release rate used for the current note (calculated from this note's sustain level, and not the current sustain level,
/// which may have changed after note has been released)
double thisrelease;
/// Sustain level used for the current note (used to calculate release rate if sustain changed during release stage
/// of the current note)
double thiss;
/// Value from the time before advance() was called last time
double old_value;
adsr()
{
attack = decay = sustain = release = thisrelease = thiss = 0.f;
reset();
}
/// Stop (reset) the envelope
inline void reset()
{
old_value = value = 0.0;
thiss = 0.0;
state = STOP;
}
/// Set the envelope parameters (updates rate member variables based on values passed)
/// @param a attack time
/// @param d decay time
/// @param s sustain level
/// @param r release time
/// @param er Envelope (update) rate
/// @param f fade time (if applicable)
inline void set(float a, float d, float s, float r, float er, float f = 0.f)
{
attack = 1.0 / (a * er);
decay = (1 - s) / (d * er);
sustain = s;
release_time = r * er;
release = s / release_time;
if (fabs(f) > small_value<float>())
fade = 1.0 / (f * er);
else
fade = 0.0;
// in release:
// lock thiss setting (start of release for current note) and unlock thisrelease setting (current note's release rate)
if (state != RELEASE)
thiss = s;
else
thisrelease = thiss / release_time;
}
/// @retval true if envelope is in released state (forced decay, release or stopped)
inline bool released() const
{
return state == LOCKDECAY || state == RELEASE || state == STOP;
}
/// @retval true if envelope is stopped (has not been started or has run till its end)
inline bool stopped() const
{
return state == STOP;
}
/// Start the envelope
inline void note_on()
{
state = ATTACK;
thiss = sustain;
}
/// Release the envelope
inline void note_off()
{
// Do nothing if envelope is already stopped
if (state == STOP)
return;
// XXXKF what if envelope is already released? (doesn't happen in any current synth, but who knows?)
// Raise sustain value if it has been changed... I'm not sure if it's needed
thiss = std::max(sustain, value);
// Calculate release rate from sustain level
thisrelease = thiss / release_time;
// we're in attack or decay, and if decay is faster than release
if (value > sustain && decay > thisrelease) {
// use standard release time later (because we'll be switching at sustain point)
thisrelease = release;
state = LOCKDECAY;
} else {
// in attack/decay, but use fixed release time
// in case value fell below sustain, assume it didn't (for the purpose of calculating release rate only)
state = RELEASE;
}
}
/// Calculate next envelope value
inline void advance()
{
old_value = value;
// XXXKF This may use a state array instead of a switch some day (at least for phases other than attack and possibly sustain)
switch(state)
{
case ATTACK:
value += attack;
if (value >= 1.0) {
value = 1.0;
state = DECAY;
}
break;
case DECAY:
value -= decay;
if (value < sustain)
{
value = sustain;
state = SUSTAIN;
}
break;
case LOCKDECAY:
value -= decay;
if (value < sustain)
{
if (value < 0.f)
value = 0.f;
state = RELEASE;
thisrelease = release;
}
break;
case SUSTAIN:
if (fade != 0.f)
{
value -= fade;
if (value > 1.f)
value = 1.f;
}
else
value = sustain;
if (value < 0.00001f) {
value = 0;
state = STOP;
}
break;
case RELEASE:
value -= thisrelease;
if (value <= 0.f) {
value = 0.f;
state = STOP;
}
break;
case STOP:
value = 0.f;
break;
}
}
/// Return a value between old_value (previous step) and value (current step)
/// @param pos between 0 and 1
inline double interpolate(double pos)
{
return old_value + (value - old_value) * pos;
}
};
};
#endif

View File

@@ -0,0 +1,113 @@
/* Calf DSP Library
* FFT class
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_FFT_H
#define __CALF_FFT_H
#include <complex>
namespace dsp {
/// FFT routine copied from my old OneSignal library, modified to
/// match Calf's style. It's not fast at all, just a straightforward
/// implementation.
template<class T, int O>
class fft
{
typedef typename std::complex<T> complex;
int scramble[1<<O];
complex sines[1<<O];
public:
fft()
{
int N=1<<O;
assert(N >= 4);
for (int i=0; i<N; i++)
{
int v=0;
for (int j=0; j<O; j++)
if (i&(1<<j))
v+=(N>>(j+1));
scramble[i]=v;
}
int N90 = N >> 2;
T divN = 2 * M_PI / N;
// use symmetry
for (int i=0; i<N90; i++)
{
T angle = divN * i;
T c = cos(angle), s = sin(angle);
sines[i + 3 * N90] = -(sines[i + N90] = complex(-s, c));
sines[i + 2 * N90] = -(sines[i] = complex(c, s));
}
}
void calculate(complex *input, complex *output, bool inverse)
{
int N=1<<O;
int N1=N-1;
int i;
// Scramble the input data
if (inverse)
{
float mf=1.0/N;
for (i=0; i<N; i++)
{
complex &c=input[scramble[i]];
output[i]=mf*complex(c.imag(),c.real());
}
}
else
for (i=0; i<N; i++)
output[i]=input[scramble[i]];
// O butterfiles
for (i=0; i<O; i++)
{
int PO=1<<i, PNO=1<<(O-i-1);
int j,k;
for (j=0; j<PNO; j++)
{
int base=j<<(i+1);
for (k=0; k<PO; k++)
{
int B1=base+k;
int B2=base+k+(1<<i);
complex r1=output[B1];
complex r2=output[B2];
output[B1]=r1+r2*sines[(B1<<(O-i-1))&N1];
output[B2]=r1+r2*sines[(B2<<(O-i-1))&N1];
}
}
}
if (inverse)
{
for (i=0; i<N; i++)
{
const complex &c=output[i];
output[i]=complex(c.imag(),c.real());
}
}
}
};
};
#endif

View File

@@ -0,0 +1,269 @@
/* Calf DSP Library
* DSP primitives.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_FIXED_POINT_H
#define __CALF_FIXED_POINT_H
namespace dsp {
inline uint32_t shr(uint32_t v, int bits = 1) { return v>>bits; };
inline int32_t shr(int32_t v, int bits = 1) { return v>>bits; };
inline uint64_t shr(uint64_t v, int bits = 1) { return v>>bits; };
inline int64_t shr(int64_t v, int bits = 1) { return v>>bits; };
inline float shr(float v, int bits = 1) { return v*(1.0/(1<<bits)); };
inline double shr(double v, int bits = 1) { return v*(1.0/(1<<bits)); };
template<class T, int FracBits>
inline T shr(T v, int bits = 1) {
v.set(v >> bits);
return v;
}
template<class T, int FracBits> class fixed_point {
T value;
enum { IntBits = (sizeof(T)/8) - FracBits };
public:
/// default constructor, does not initialize the value, just like - say - float doesn't
inline fixed_point() {
}
/// copy constructor from any other fixed_point value
template<class U, int FracBits2> inline fixed_point(const fixed_point<U, FracBits2> &v) {
if (FracBits == FracBits2) value = v.get();
else if (FracBits > FracBits2) value = v.get() << abs(FracBits - FracBits2);
else value = v.get() >> abs(FracBits - FracBits2);
}
/* this would be way too confusing, it wouldn't be obvious if it expects a whole fixed point or an integer part
explicit inline fixed_point(T v) {
this->value = v;
}
*/
explicit inline fixed_point(double v) {
value = (T)(v*one());
}
/// Makes an instance from a representation value (ie. same type of value as is used for internal storage and get/set)
static inline fixed_point from_base(const T &v)
{
fixed_point result;
result.value = v;
return result;
}
inline static T one() {
return (T)(1) << FracBits;
}
inline void set(T value) {
this->value = value;
}
inline T get() const {
return value;
}
inline operator double() const {
return value * (1.0/one());
}
inline fixed_point &operator=(double v) {
value = (T)(v*one());
return *this;
}
template<class U, int FracBits2> static inline T rebase(const fixed_point<U, FracBits2> &v) {
if (FracBits == FracBits2)
return v.get();
if (FracBits > FracBits2)
return v.get() << abs(FracBits - FracBits2);
return v.get() >> abs(FracBits2 - FracBits);
}
template<class U, int FracBits2> inline fixed_point &operator=(const fixed_point<U, FracBits2> &v) {
value = rebase<U, FracBits2>(v);
return *this;
}
template<class U, int FracBits2> inline fixed_point &operator+=(const fixed_point<U, FracBits2> &v) {
value += rebase<U, FracBits2>(v);
return *this;
}
template<class U, int FracBits2> inline fixed_point &operator-=(const fixed_point<U, FracBits2> &v) {
value -= rebase<U, FracBits2>(v);
return *this;
}
template<class U, int FracBits2> inline fixed_point operator+(const fixed_point<U, FracBits2> &v) const {
fixed_point fpv;
fpv.set(value + rebase<U, FracBits2>(v));
return fpv;
}
template<class U, int FracBits2> inline fixed_point operator-(const fixed_point<U, FracBits2> &v) const {
fixed_point fpv;
fpv.set(value - rebase<U, FracBits2>(v));
return fpv;
}
/// multiply two fixed point values, using long long int to store the temporary multiplication result
template<class U, int FracBits2> inline fixed_point operator*(const fixed_point<U, FracBits2> &v) const {
fixed_point tmp;
tmp.set(((int64_t)value) * v.get() >> FracBits2);
return tmp;
}
/// multiply two fixed point values, using BigType (usually 64-bit int) to store the temporary multiplication result
template<class U, int FracBits2, class BigType> inline fixed_point& operator*=(const fixed_point<U, FracBits2> &v) {
value = (T)(((BigType)value) * v.get() >> FracBits2);
return *this;
}
inline fixed_point operator+(int v) const {
fixed_point tmp;
tmp.set(value + (v << FracBits));
return tmp;
}
inline fixed_point operator-(int v) const {
fixed_point tmp;
tmp.set(value - (v << FracBits));
return tmp;
}
inline fixed_point operator*(int v) const {
fixed_point tmp;
tmp.value = value*v;
return tmp;
}
inline fixed_point& operator+=(int v) {
value += (v << FracBits);
return *this;
}
inline fixed_point& operator-=(int v) {
value -= (v << FracBits);
return *this;
}
inline fixed_point& operator*=(int v) {
value *= v;
return *this;
}
/// return integer part
inline T ipart() const {
return value >> FracBits;
}
/// return integer part as unsigned int
inline unsigned int uipart() const {
return ((unsigned)value) >> FracBits;
}
/// return integer part as unsigned int
inline unsigned int ui64part() const {
return ((uint64_t)value) >> FracBits;
}
/// return fractional part as 0..(2^FracBits-1)
inline T fpart() const {
return value & ((1 << FracBits)-1);
}
/// return fractional part as 0..(2^Bits-1)
template<int Bits>
inline T fpart() const {
int fbits = value & ((1 << FracBits)-1);
if (Bits == FracBits) return fbits;
int shift = abs(Bits-FracBits);
return (Bits < FracBits) ? (fbits >> shift) : (fbits << shift);
}
/// return fractional part as 0..1
inline double fpart_as_double() const {
return (value & ((1 << FracBits)-1)) * (1.0 / (1 << FracBits));
}
/// use fractional part (either whole or given number of most significant bits) for interpolating between two values
/// note that it uses integer arithmetic only, and isn't suitable for floating point or fixed point U!
/// @param UseBits can be used when there's a risk of exceeding range of U because max(fpart)*max(v1 or v2) > range of U
template<class U, int UseBits, class MulType>
inline U lerp_by_fract_int(U v1, U v2) const {
int fp = fpart<UseBits>();
assert ( fp >=0 && fp <= (1<<UseBits));
// printf("diff =
return v1 + shr(((MulType)(v2-v1) * fp), UseBits);
}
template<class U, int UseBits>
inline U lerp_table_lookup_int(U data[(1<<IntBits)+1]) const {
unsigned int pos = uipart();
return lerp_by_fract_int<U, UseBits>(data[pos], data[pos+1]);
}
/// Untested... I've started it to get a sin/cos readout for rotaryorgan, but decided to use table-less solution instead
/// Do not assume it works, because it most probably doesn't
template<class U, int UseBits>
inline U lerp_table_lookup_int_shift(U data[(1<<IntBits)+1], unsigned int shift) {
unsigned int pos = (uipart() + shift) & ((1 << IntBits) - 1);
return lerp_by_fract_int<U, UseBits>(data[pos], data[pos+1]);
}
template<class U>
inline U lerp_table_lookup_float(U data[(1<<IntBits)+1]) const {
unsigned int pos = uipart();
return data[pos] + (data[pos+1]-data[pos]) * fpart_as_double();
}
template<class U>
inline U lerp_table_lookup_float_mask(U data[(1<<IntBits)+1], unsigned int mask) const {
unsigned int pos = ui64part() & mask;
// printf("full = %lld pos = %d + %f\n", value, pos, fpart_as_double());
return data[pos] + (data[pos+1]-data[pos]) * fpart_as_double();
}
template<class U, int UseBits, class MulType>
inline U lerp_ptr_lookup_int(U *data) const {
unsigned int pos = ui64part();
return lerp_by_fract_int<U, UseBits, MulType>(data[pos], data[pos+1]);
}
template<class U>
inline U lerp_ptr_lookup_float(U *data) const {
unsigned int pos = ui64part();
return data[pos] + (data[pos+1]-data[pos]) * fpart_as_double();
}
};
template<class T, int FractBits>
inline fixed_point<T, FractBits> operator*(int v, fixed_point<T, FractBits> v2) {
v2 *= v;
return v2;
}
/// wave position (unsigned 64-bit int including 24-bit fractional part)
typedef fixed_point<unsigned long long int, 24> wpos;
};
#endif

View File

@@ -0,0 +1,561 @@
/* Calf DSP Library
* Common plugin interface definitions (shared between LADSPA/LV2/DSSI/standalone).
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_GIFACE_H
#define __CALF_GIFACE_H
#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
#include <exception>
#include <string>
#include "primitives.h"
#include "preset.h"
namespace osctl {
struct osc_client;
}
namespace calf_plugins {
enum {
MAX_SAMPLE_RUN = 256
};
/// Values ORed together for flags field in parameter_properties
enum parameter_flags
{
PF_TYPEMASK = 0x000F, ///< bit mask for type
PF_FLOAT = 0x0000, ///< any float value
PF_INT = 0x0001, ///< integer value (still represented as float)
PF_BOOL = 0x0002, ///< bool value (usually >=0.5f is treated as TRUE, which is inconsistent with LV2 etc. which treats anything >0 as TRUE)
PF_ENUM = 0x0003, ///< enum value (min, min+1, ..., max, only guaranteed to work when min = 0)
PF_ENUM_MULTI = 0x0004, ///< SET / multiple-choice
PF_STRING = 0x0005, ///< see: http://lv2plug.in/docs/index.php?title=String_port
PF_SCALEMASK = 0xF0, ///< bit mask for scale
PF_SCALE_DEFAULT = 0x00, ///< no scale given
PF_SCALE_LINEAR = 0x10, ///< linear scale
PF_SCALE_LOG = 0x20, ///< log scale
PF_SCALE_GAIN = 0x30, ///< gain = -96dB..0 or -inf dB
PF_SCALE_PERC = 0x40, ///< percent
PF_SCALE_QUAD = 0x50, ///< quadratic scale (decent for some gain/amplitude values)
PF_SCALE_LOG_INF = 0x60, ///< log scale + +inf (FAKE_INFINITY)
PF_CTLMASK = 0x0F00, ///< bit mask for control type
PF_CTL_DEFAULT = 0x0000, ///< try to figure out automatically
PF_CTL_KNOB = 0x0100, ///< knob
PF_CTL_FADER = 0x0200, ///< fader (slider)
PF_CTL_TOGGLE = 0x0300, ///< toggle button
PF_CTL_COMBO = 0x0400, ///< combo box
PF_CTL_RADIO = 0x0500, ///< radio button
PF_CTL_BUTTON = 0x0600, ///< push button
PF_CTL_METER = 0x0700, ///< volume meter
PF_CTL_LED = 0x0800, ///< light emitting diode
PF_CTLOPTIONS = 0x00F000, ///< bit mask for control (widget) options
PF_CTLO_HORIZ = 0x001000, ///< horizontal version of the control (unused)
PF_CTLO_VERT = 0x002000, ///< vertical version of the control (unused)
PF_CTLO_LABEL = 0x004000, ///< add a text display to the control (meters only)
PF_CTLO_REVERSE = 0x008000, ///< use VU_MONOCHROME_REVERSE mode (meters only)
PF_PROP_NOBOUNDS = 0x010000, ///< no epp:hasStrictBounds
PF_PROP_EXPENSIVE = 0x020000, ///< epp:expensive, may trigger expensive calculation
PF_PROP_OUTPUT_GAIN=0x050000, ///< epp:outputGain + skip epp:hasStrictBounds
PF_PROP_OUTPUT = 0x080000, ///< output port
PF_PROP_OPTIONAL = 0x100000, ///< connection optional
PF_PROP_GRAPH = 0x200000, ///< add graph
PF_PROP_MSGCONTEXT= 0x400000, ///< message context
PF_UNITMASK = 0xFF000000, ///< bit mask for units \todo reduce to use only 5 bits
PF_UNIT_DB = 0x01000000, ///< decibels
PF_UNIT_COEF = 0x02000000, ///< multiply-by factor
PF_UNIT_HZ = 0x03000000, ///< Hertz
PF_UNIT_SEC = 0x04000000, ///< second
PF_UNIT_MSEC = 0x05000000, ///< millisecond
PF_UNIT_CENTS = 0x06000000, ///< cents (1/100 of a semitone, 1/1200 of an octave)
PF_UNIT_SEMITONES = 0x07000000,///< semitones
PF_UNIT_BPM = 0x08000000, ///< beats per minute
PF_UNIT_DEG = 0x09000000, ///< degrees
PF_UNIT_NOTE = 0x0A000000, ///< MIDI note number
PF_UNIT_RPM = 0x0B000000, ///< revolutions per minute
};
/// A fake infinity value (because real infinity may break some hosts)
#define FAKE_INFINITY (65536.0 * 65536.0)
/// Check for infinity (with appropriate-ish tolerance)
#define IS_FAKE_INFINITY(value) (fabs(value-FAKE_INFINITY) < 1.0)
/// Information record about plugin's menu command
struct plugin_command_info
{
const char *label; ///< short command name / label
const char *name; ///< human-readable command name
const char *description; ///< description (for status line etc.)
};
/// Range, default value, flags and names for a parameter
struct parameter_properties
{
/// default value
float def_value;
/// minimum value
float min;
/// maximum value
float max;
/// number of steps (for an integer value from 0 to 100 this will be 101; for 0/90/180/270/360 this will be 5), or 0 for continuous
float step;
/// logical OR of parameter_flags
uint32_t flags;
/// for PF_ENUM: array of text values (from min to max step 1), otherwise NULL
const char **choices;
/// parameter label (for use in LV2 label field etc.)
const char *short_name;
/// parameter human-readable name
const char *name;
/// convert from [0, 1] range to [min, max] (applying scaling)
float from_01(double value01) const;
/// convert from [min, max] to [0, 1] range (applying reverse scaling)
double to_01(float value) const;
/// stringify (in sensible way)
std::string to_string(float value) const;
/// get required width (for reserving GUI space)
int get_char_count() const;
/// get increment step based on step value (if specified) and other factors
float get_increment() const;
};
struct cairo_iface
{
virtual void set_source_rgba(float r, float g, float b, float a = 1.f) = 0;
virtual void set_line_width(float width) = 0;
virtual ~cairo_iface() {}
};
struct progress_report_iface
{
virtual void report_progress(float percentage, const std::string &message) = 0;
virtual ~progress_report_iface() {}
};
/// 'provides live line graph values' interface
struct line_graph_iface
{
/// Obtain subindex'th graph of parameter 'index'
/// @param index parameter/graph number (usually tied to particular plugin control port)
/// @param subindex graph number (there may be multiple overlaid graphs for one parameter, eg. for monosynth 2x12dB filters)
/// @param data buffer for normalized output values
/// @param points number of points to fill
/// @param context cairo context to adjust (for multicolour graphs etc.)
/// @retval true graph data was returned; subindex+1 graph may or may not be available
/// @retval false graph data was not returned; subindex+1 graph does not exist either
virtual bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) { return false; }
/// Obtain subindex'th dot of parameter 'index'
/// @param index parameter/dot number (usually tied to particular plugin control port)
/// @param subindex dot number (there may be multiple dots graphs for one parameter)
virtual bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) { return false; }
/// Obtain subindex'th dot of parameter 'index'
/// @param index parameter/dot number (usually tied to particular plugin control port)
/// @param subindex dot number (there may be multiple dots graphs for one parameter)
virtual bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) { return false; }
/// Obtain subindex'th static graph of parameter index (static graphs are only dependent on parameter value, not plugin state)
/// @param index parameter/graph number (usually tied to particular plugin control port)
/// @param subindex graph number (there may be multiple overlaid graphs for one parameter, eg. for monosynth 2x12dB filters)
/// @param value parameter value to pick the graph for
/// @param data buffer for normalized output values
/// @param points number of points to fill
/// @param context cairo context to adjust (for multicolour graphs etc.)
/// @retval true graph data was returned; subindex+1 graph may or may not be available
/// @retval false graph data was not returned; subindex+1 graph does not exist either
virtual bool get_static_graph(int index, int subindex, float value, float *data, int points, cairo_iface *context) { return false; }
/// Return which graphs need to be redrawn and which can be cached for later reuse
/// @param generation 0 (at start) or the last value returned by the function (corresponds to a set of input values)
/// @param subindex_graph First graph that has to be redrawn (because it depends on values that might have changed)
/// @param subindex_dot First dot that has to be redrawn
/// @param subindex_gridline First gridline/legend that has to be redrawn
/// @retval Current generation (to pass when calling the function next time); if different than passed generation value, call the function again to retrieve which graph offsets should be put into cache
virtual int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) { subindex_graph = subindex_dot = subindex_gridline = 0; return 0; }
/// Standard destructor to make compiler happy
virtual ~line_graph_iface() {}
};
enum table_column_type
{
TCT_UNKNOWN, ///< guard invalid type
TCT_FLOAT, ///< float value (encoded as C locale string)
TCT_ENUM, ///< enum value (see: 'values' array in table_column_info) - encoded as string base 10 representation of integer
TCT_STRING, ///< string value (encoded as C-escaped string)
TCT_OBJECT, ///< external object, encoded as string
TCT_LABEL, ///< string value (encoded as C-escaped string)
};
/// parameters of
struct table_column_info
{
const char *name; ///< column label
table_column_type type; ///< column data type
float min; ///< minimum value (for float)
float max; ///< maximum value (for float and enum)
float def_value; ///< default value (for float and enum)
const char **values; ///< NULL unless a TCT_ENUM, where it represents a NULL-terminated list of choices
};
/// 'has string parameters containing tabular data' interface
struct table_edit_iface
{
/// retrieve the table layout for specific parameter
virtual const table_column_info *get_table_columns(int param) = 0;
/// return the current number of rows
virtual uint32_t get_table_rows(int param) = 0;
/// retrieve data item from the plugin
virtual std::string get_cell(int param, int row, int column) { return calf_utils::i2s(row)+":"+calf_utils::i2s(column); }
/// set data item to the plugin
virtual void set_cell(int param, int row, int column, const std::string &src, std::string &error) { error.clear(); }
/// return a line graph interface for a specific parameter/column (unused for now)
virtual line_graph_iface *get_graph_iface(int param, int column) { return NULL; }
/// return an editor name for a specific grid cell (unused for now - I don't even know how editors be implemented)
virtual const char *get_cell_editor(int param, int column) { return NULL; }
virtual ~table_edit_iface() {}
};
/// 'may receive configure variables' interface
struct send_configure_iface
{
/// Called to set configure variable
/// @param key variable name
/// @param value variable content
virtual void send_configure(const char *key, const char *value) = 0;
virtual ~send_configure_iface() {}
};
/// 'may receive new status values' interface
struct send_updates_iface
{
/// Called to set configure variable
/// @param key variable name
/// @param value variable content
virtual void send_status(const char *key, const char *value) = 0;
virtual ~send_updates_iface() {}
};
struct plugin_command_info;
/// General information about the plugin - @todo XXXKF lacks the "new" id-label-name triple
struct ladspa_plugin_info
{
/// LADSPA ID
uint32_t unique_id;
/// plugin short name (camel case)
const char *label;
/// plugin human-readable name
const char *name;
/// maker (author)
const char *maker;
/// copyright notice
const char *copyright;
/// plugin type for LRDF/LV2
const char *plugin_type;
};
/// An interface returning metadata about a plugin
struct plugin_metadata_iface
{
/// @return plugin long name
virtual const char *get_name() = 0;
/// @return plugin LV2 label
virtual const char *get_id() = 0;
/// @return plugin human-readable label
virtual const char *get_label() = 0;
/// @return total number of parameters
virtual int get_param_count() = 0;
/// Return custom XML
virtual const char *get_gui_xml() = 0;
/// @return number of audio inputs
virtual int get_input_count()=0;
/// @return number of audio outputs
virtual int get_output_count()=0;
/// @return true if plugin can work in hard-realtime conditions
virtual bool is_rt_capable()=0;
/// @return true if plugin has MIDI input
virtual bool get_midi()=0;
/// @return true if plugin has MIDI input
virtual bool requires_midi()=0;
/// @return port offset of first control (parameter) port (= number of audio inputs + number of audio outputs in all existing plugins as for 1 Aug 2008)
virtual int get_param_port_offset() = 0;
/// @return line_graph_iface if any
virtual line_graph_iface *get_line_graph_iface() = 0;
/// @return table_edit_iface if any
virtual table_edit_iface *get_table_edit_iface() = 0;
/// @return NULL-terminated list of menu commands
virtual plugin_command_info *get_commands() { return NULL; }
/// @return description structure for given parameter
virtual parameter_properties *get_param_props(int param_no) = 0;
/// @return retrieve names of audio ports (@note control ports are named in parameter_properties, not here)
virtual const char **get_port_names() = 0;
/// @return description structure for the plugin
virtual const ladspa_plugin_info &get_plugin_info() = 0;
/// is a given parameter a control voltage?
virtual bool is_cv(int param_no) = 0;
/// is the given parameter non-interpolated?
virtual bool is_noisy(int param_no) = 0;
/// does the plugin require message context? (or DSSI configure) may be slow
virtual bool requires_message_context() = 0;
/// does the plugin require string port extension? (or DSSI configure) may be slow
virtual bool requires_string_ports() = 0;
/// add all message context parameter numbers to the ports vector
virtual void get_message_context_parameters(std::vector<int> &ports) = 0;
/// Do-nothing destructor to silence compiler warning
virtual ~plugin_metadata_iface() {}
};
/// Interface for host-GUI-plugin interaction (should be really split in two, but ... meh)
struct plugin_ctl_iface: public virtual plugin_metadata_iface
{
/// @return value of given parameter
virtual float get_param_value(int param_no) = 0;
/// Set value of given parameter
virtual void set_param_value(int param_no, float value) = 0;
/// Load preset with given number
virtual bool activate_preset(int bank, int program) = 0;
/// @return volume level for port'th port (if supported by the implementation, currently only jack_host<Module> implements that by measuring signal level on plugin ports)
virtual float get_level(unsigned int port)=0;
/// Execute menu command with given number
virtual void execute(int cmd_no)=0;
/// Set a configure variable on a plugin
virtual char *configure(const char *key, const char *value) { return NULL; }
/// Send all configure variables set within a plugin to given destination (which may be limited to only those that plugin understands)
virtual void send_configures(send_configure_iface *)=0;
/// Restore all state (parameters and configure vars) to default values - implemented in giface.cpp
virtual void clear_preset();
/// Call a named function in a plugin - this will most likely be redesigned soon - and never used
/// @retval false call has failed, result contains an error message
virtual bool blobcall(const char *command, const std::string &request, std::string &result) { result = "Call not supported"; return false; }
/// Update status variables changed since last_serial
/// @return new last_serial
virtual int send_status_updates(send_updates_iface *sui, int last_serial) { return last_serial; }
/// Do-nothing destructor to silence compiler warning
virtual ~plugin_ctl_iface() {}
};
struct plugin_list_info_iface;
/// Get a list of all "large" (effect/synthesizer) plugins
extern void get_all_plugins(std::vector<plugin_metadata_iface *> &plugins);
/// Get a list of all "small" (module) plugins
extern void get_all_small_plugins(plugin_list_info_iface *plii);
/// Load and strdup a text file with GUI definition
extern const char *load_gui_xml(const std::string &plugin_id);
/// Empty implementations for plugin functions. Note, that functions aren't virtual, because they're called via the particular
/// subclass (flanger_audio_module etc) via template wrappers (ladspa_wrapper<> etc), not via base class pointer/reference
template<class Metadata>
class audio_module: public Metadata
{
public:
typedef Metadata metadata_type;
progress_report_iface *progress_report;
audio_module() {
progress_report = NULL;
}
/// Handle MIDI Note On
inline void note_on(int note, int velocity) {}
/// Handle MIDI Note Off
inline void note_off(int note, int velocity) {}
/// Handle MIDI Program Change
inline void program_change(int program) {}
/// Handle MIDI Control Change
inline void control_change(int controller, int value) {}
/// Handle MIDI Pitch Bend
/// @param value pitch bend value (-8192 to 8191, defined as in MIDI ie. 8191 = 200 ct by default)
inline void pitch_bend(int value) {}
/// Handle MIDI Channel Pressure
/// @param value channel pressure (0 to 127)
inline void channel_pressure(int value) {}
/// Called when params are changed (before processing)
inline void params_changed() {}
/// LADSPA-esque activate function, except it is called after ports are connected, not before
inline void activate() {}
/// LADSPA-esque deactivate function
inline void deactivate() {}
/// Set sample rate for the plugin
inline void set_sample_rate(uint32_t sr) { }
/// Execute menu command with given number
inline void execute(int cmd_no) {}
/// DSSI configure call
virtual char *configure(const char *key, const char *value) { return NULL; }
/// Send all understood configure vars (none by default)
inline void send_configures(send_configure_iface *sci) {}
/// Send all supported status vars (none by default)
inline int send_status_updates(send_updates_iface *sui, int last_serial) { return last_serial; }
/// Reset parameter values for epp:trigger type parameters (ones activated by oneshot push button instead of check box)
inline void params_reset() {}
/// Called after instantiating (after all the feature pointers are set - including interfaces like progress_report_iface)
inline void post_instantiate() {}
/// Handle 'message context' port message
/// @arg output_ports pointer to bit array of output port "changed" flags, note that 0 = first audio input, not first parameter (use input_count + output_count)
inline uint32_t message_run(const void *valid_ports, void *output_ports) {
fprintf(stderr, "ERROR: message run not implemented\n");
return 0;
}
};
extern bool check_for_message_context_ports(parameter_properties *parameters, int count);
extern bool check_for_string_ports(parameter_properties *parameters, int count);
#if USE_DSSI
enum line_graph_item
{
LGI_END = 0,
LGI_GRAPH,
LGI_SUBGRAPH,
LGI_LEGEND,
LGI_DOT,
LGI_END_ITEM,
LGI_SET_RGBA,
LGI_SET_WIDTH,
};
/// A class to send status updates via OSC
struct dssi_feedback_sender
{
/// OSC client object used to send updates
osctl::osc_client *client;
/// Background thread handle
pthread_t bg_thread;
/// Quit flag (used to terminate the thread)
bool quit;
/// Indices of graphs to send
std::vector<int> indices;
/// Source for the graph data (interface to marshal)
calf_plugins::line_graph_iface *graph;
dssi_feedback_sender(const char *URI, line_graph_iface *_graph, calf_plugins::parameter_properties *props, int num_params);
void update();
~dssi_feedback_sender();
};
#endif
/// Metadata base class template, to provide default versions of interface functions
template<class Metadata>
class plugin_metadata: public virtual plugin_metadata_iface
{
public:
static const char *port_names[];
static parameter_properties param_props[];
static ladspa_plugin_info plugin_info;
// These below are stock implementations based on enums and static members in Metadata classes
// they may be overridden to provide more interesting functionality
const char *get_name() { return Metadata::impl_get_name(); }
const char *get_id() { return Metadata::impl_get_id(); }
const char *get_label() { return Metadata::impl_get_label(); }
int get_input_count() { return Metadata::in_count; }
int get_output_count() { return Metadata::out_count; }
int get_param_count() { return Metadata::param_count; }
bool get_midi() { return Metadata::support_midi; }
bool requires_midi() { return Metadata::require_midi; }
bool is_rt_capable() { return Metadata::rt_capable; }
line_graph_iface *get_line_graph_iface() { return dynamic_cast<line_graph_iface *>(this); }
table_edit_iface *get_table_edit_iface() { return dynamic_cast<table_edit_iface *>(this); }
int get_param_port_offset() { return Metadata::in_count + Metadata::out_count; }
const char *get_gui_xml() { static const char *data_ptr = calf_plugins::load_gui_xml(get_id()); return data_ptr; }
plugin_command_info *get_commands() { return NULL; }
parameter_properties *get_param_props(int param_no) { return &param_props[param_no]; }
const char **get_port_names() { return port_names; }
bool is_cv(int param_no) { return true; }
bool is_noisy(int param_no) { return false; }
const ladspa_plugin_info &get_plugin_info() { return plugin_info; }
bool requires_message_context() { return check_for_message_context_ports(param_props, Metadata::param_count); }
bool requires_string_ports() { return check_for_string_ports(param_props, Metadata::param_count); }
void get_message_context_parameters(std::vector<int> &ports) {
for (int i = 0; i < get_param_count(); ++i) {
if (get_param_props(i)->flags & PF_PROP_MSGCONTEXT)
ports.push_back(i);
}
}
};
/// A class for delegating metadata implementation to a "remote" metadata class.
/// Used for GUI wrappers that cannot have a dependency on actual classes,
/// and which instead take an "external" metadata object pointer, obtained
/// through get_all_plugins.
class plugin_metadata_proxy: public virtual plugin_metadata_iface
{
public:
plugin_metadata_iface *impl;
public:
plugin_metadata_proxy(plugin_metadata_iface *_impl) { impl = _impl; }
const char *get_name() { return impl->get_name(); }
const char *get_id() { return impl->get_id(); }
const char *get_label() { return impl->get_label(); }
int get_input_count() { return impl->get_input_count(); }
int get_output_count() { return impl->get_output_count(); }
int get_param_count() { return impl->get_param_count(); }
bool get_midi() { return impl->get_midi(); }
bool requires_midi() { return impl->requires_midi(); }
bool is_rt_capable() { return impl->is_rt_capable(); }
line_graph_iface *get_line_graph_iface() { return impl->get_line_graph_iface(); }
table_edit_iface *get_table_edit_iface() { return impl->get_table_edit_iface(); }
int get_param_port_offset() { return impl->get_param_port_offset(); }
const char *get_gui_xml() { return impl->get_gui_xml(); }
plugin_command_info *get_commands() { return impl->get_commands(); }
parameter_properties *get_param_props(int param_no) { return impl->get_param_props(param_no); }
const char **get_port_names() { return impl->get_port_names(); }
bool is_cv(int param_no) { return impl->is_cv(param_no); }
bool is_noisy(int param_no) { return impl->is_noisy(param_no); }
const ladspa_plugin_info &get_plugin_info() { return impl->get_plugin_info(); }
bool requires_message_context() { return impl->requires_message_context(); }
bool requires_string_ports() { return impl->requires_string_ports(); }
void get_message_context_parameters(std::vector<int> &ports) { impl->get_message_context_parameters(ports); }
};
#define CALF_PORT_NAMES(name) template<> const char *::plugin_metadata<name##_metadata>::port_names[]
#define CALF_PORT_PROPS(name) template<> parameter_properties plugin_metadata<name##_metadata>::param_props[]
#define CALF_PLUGIN_INFO(name) template<> calf_plugins::ladspa_plugin_info plugin_metadata<name##_metadata>::plugin_info
#define PLUGIN_NAME_ID_LABEL(name, id, label) \
static const char *impl_get_name() { return name; } \
static const char *impl_get_id() { return id; } \
static const char *impl_get_label() { return label; } \
extern const char *calf_copyright_info;
};
#endif

View File

@@ -0,0 +1,256 @@
/* Calf DSP Library
* Basic "inertia" (parameter smoothing) classes.
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_INERTIA_H
#define __CALF_INERTIA_H
#include "primitives.h"
namespace dsp {
/// Algorithm for a constant time linear ramp
class linear_ramp
{
public:
int ramp_len;
float mul, delta;
public:
/// Construct for given ramp length
linear_ramp(int _ramp_len) {
ramp_len = _ramp_len;
mul = (float)(1.0f / ramp_len);
}
/// Change ramp length
inline void set_length(int _ramp_len) {
ramp_len = _ramp_len;
mul = (float)(1.0f / ramp_len);
}
inline int length()
{
return ramp_len;
}
inline void start_ramp(float start, float end)
{
delta = mul * (end - start);
}
/// Return value after single step
inline float ramp(float value)
{
return value + delta;
}
/// Return value after many steps
inline float ramp_many(float value, int count)
{
return value + delta * count;
}
};
/// Algorithm for a constant time linear ramp
class exponential_ramp
{
public:
int ramp_len;
float root, delta;
public:
exponential_ramp(int _ramp_len) {
ramp_len = _ramp_len;
root = (float)(1.0f / ramp_len);
}
inline void set_length(int _ramp_len) {
ramp_len = _ramp_len;
root = (float)(1.0f / ramp_len);
}
inline int length()
{
return ramp_len;
}
inline void start_ramp(float start, float end)
{
delta = pow(end / start, root);
}
/// Return value after single step
inline float ramp(float value)
{
return value * delta;
}
/// Return value after many steps
inline float ramp_many(float value, float count)
{
return value * pow(delta, count);
}
};
/// Generic inertia using ramping algorithm specified as template argument. The basic idea
/// is producing smooth(ish) output for discrete input, using specified algorithm to go from
/// last output value to input value. It is not the same as classic running average lowpass
/// filter, because ramping time is finite and pre-determined (it calls ramp algorithm's length()
/// function to obtain the expected ramp length)
template<class Ramp>
class inertia
{
public:
float old_value;
float value;
unsigned int count;
Ramp ramp;
public:
inertia(const Ramp &_ramp, float init_value = 0.f)
: ramp(_ramp)
{
value = old_value = init_value;
count = 0;
}
/// Set value immediately (no inertia)
void set_now(float _value)
{
value = old_value = _value;
count = 0;
}
/// Set with inertia
void set_inertia(float source)
{
if (source != old_value) {
ramp.start_ramp(value, source);
count = ramp.length();
old_value = source;
}
}
/// Get smoothed value of given source value
inline float get(float source)
{
if (source != old_value) {
ramp.start_ramp(value, source);
count = ramp.length();
old_value = source;
}
if (!count)
return old_value;
value = ramp.ramp(value);
count--;
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
value = old_value;
return value;
}
/// Get smoothed value assuming no new input
inline float get()
{
if (!count)
return old_value;
value = ramp.ramp(value);
count--;
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
value = old_value;
return value;
}
/// Do one inertia step, without returning the new value and without changing destination value
inline void step()
{
if (count) {
value = ramp.ramp(value);
count--;
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
value = old_value;
}
}
/// Do many inertia steps, without returning the new value and without changing destination value
inline void step_many(unsigned int steps)
{
if (steps < count) {
// Skip only a part of the current ramping period
value = ramp.ramp_many(value, steps);
count -= steps;
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
value = old_value;
}
else
{
// The whole ramping period has been skipped, just go to destination
value = old_value;
count = 0;
}
}
/// Get last smoothed value, without affecting anything
inline float get_last() const
{
return value;
}
/// Is it still ramping?
inline bool active() const
{
return count > 0;
}
};
class once_per_n
{
public:
unsigned int frequency;
unsigned int left;
public:
once_per_n(unsigned int _frequency)
: frequency(_frequency), left(_frequency)
{}
inline void start()
{
left = frequency;
}
/// Set timer to "elapsed" state (elapsed() will return true during next call)
inline void signal()
{
left = 0;
}
inline unsigned int get(unsigned int desired)
{
if (desired > left) {
desired = left;
left = 0;
return desired;
}
left -= desired;
return desired;
}
inline bool elapsed()
{
if (!left) {
left = frequency;
return true;
}
return false;
}
};
class gain_smoothing: public inertia<linear_ramp>
{
public:
gain_smoothing()
: inertia<linear_ramp>(linear_ramp(64))
{
}
void set_sample_rate(int sr)
{
ramp = linear_ramp(sr / 441);
}
// to change param, use set_inertia(value)
// to read param, use get()
};
}
#endif

View File

@@ -0,0 +1,516 @@
/* Calf DSP Library
* API wrappers for LADSPA/DSSI
*
* Copyright (C) 2007-2008 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_LADSPA_WRAP_H
#define __CALF_LADSPA_WRAP_H
#if USE_LADSPA
#include <ladspa.h>
#if USE_DSSI
#include <dssi.h>
#endif
#include "giface.h"
namespace calf_plugins {
template<class Module>
inline int calc_real_param_count()
{
for (int i=0; i < Module::param_count; i++)
{
if ((Module::param_props[i].flags & PF_TYPEMASK) >= PF_STRING)
return i;
}
return Module::param_count;
}
/// A template implementing plugin_ctl_iface for a given plugin
template<class Module>
struct ladspa_instance: public Module, public plugin_ctl_iface
{
bool activate_flag;
#if USE_DSSI
dssi_feedback_sender *feedback_sender;
#endif
static int real_param_count()
{
static int _real_param_count = calc_real_param_count<Module>();
return _real_param_count;
}
ladspa_instance()
{
for (int i=0; i < Module::in_count; i++)
Module::ins[i] = NULL;
for (int i=0; i < Module::out_count; i++)
Module::outs[i] = NULL;
int rpc = real_param_count();
for (int i=0; i < rpc; i++)
Module::params[i] = NULL;
activate_flag = true;
#if USE_DSSI
feedback_sender = NULL;
#endif
}
virtual parameter_properties *get_param_props(int param_no)
{
return &Module::param_props[param_no];
}
virtual float get_param_value(int param_no)
{
// XXXKF hack
if (param_no >= real_param_count())
return 0;
return *Module::params[param_no];
}
virtual void set_param_value(int param_no, float value)
{
// XXXKF hack
if (param_no >= real_param_count())
return;
*Module::params[param_no] = value;
}
virtual int get_param_count()
{
return real_param_count();
}
virtual int get_param_port_offset()
{
return Module::in_count + Module::out_count;
}
virtual const char *get_gui_xml() {
return Module::get_gui_xml();
}
virtual line_graph_iface *get_line_graph_iface()
{
return dynamic_cast<line_graph_iface *>(this);
}
virtual bool activate_preset(int bank, int program) {
return false;
}
virtual const char *get_name()
{
return Module::get_name();
}
virtual const char *get_id()
{
return Module::get_id();
}
virtual const char *get_label()
{
return Module::get_label();
}
virtual char *configure(const char *key, const char *value)
{
#if USE_DSSI
if (!strcmp(key, "OSC:FEEDBACK_URI"))
{
line_graph_iface *lgi = dynamic_cast<line_graph_iface *>(this);
if (!lgi)
return NULL;
if (*value)
{
if (feedback_sender) {
delete feedback_sender;
feedback_sender = NULL;
}
feedback_sender = new dssi_feedback_sender(value, lgi, get_param_props(0), get_param_count());
}
else
{
if (feedback_sender) {
delete feedback_sender;
feedback_sender = NULL;
}
}
return NULL;
}
else
if (!strcmp(key, "OSC:UPDATE"))
{
if (feedback_sender)
feedback_sender->update();
return NULL;
}
else
#endif
if (!strcmp(key, "ExecCommand"))
{
if (*value)
{
execute(atoi(value));
}
return NULL;
}
return Module::configure(key, value);
}
virtual int get_input_count() { return Module::in_count; }
virtual int get_output_count() { return Module::out_count; }
virtual bool get_midi() { return Module::support_midi; }
virtual float get_level(unsigned int port) { return 0.f; }
virtual void execute(int cmd_no) {
Module::execute(cmd_no);
}
virtual void send_configures(send_configure_iface *sci) {
Module::send_configures(sci);
}
};
/// A wrapper class for plugin class object (there is only one ladspa_wrapper for many instances of the same plugin)
template<class Module>
struct ladspa_wrapper
{
typedef ladspa_instance<Module> instance;
/// LADSPA descriptor
static LADSPA_Descriptor descriptor;
/// LADSPA descriptor for DSSI (uses a different name for the plugin, otherwise same as descriptor)
static LADSPA_Descriptor descriptor_for_dssi;
#if USE_DSSI
/// Extended DSSI descriptor (points to descriptor_for_dssi for things like name/label/port info etc.)
static DSSI_Descriptor dssi_descriptor;
static DSSI_Program_Descriptor dssi_default_program;
static std::vector<plugin_preset> *presets;
static std::vector<DSSI_Program_Descriptor> *preset_descs;
#endif
ladspa_wrapper()
{
int ins = Module::in_count;
int outs = Module::out_count;
int params = ladspa_instance<Module>::real_param_count();
ladspa_plugin_info &plugin_info = Module::plugin_info;
descriptor.UniqueID = plugin_info.unique_id;
descriptor.Label = plugin_info.label;
descriptor.Name = strdup((std::string(plugin_info.name) + " LADSPA").c_str());
descriptor.Maker = plugin_info.maker;
descriptor.Copyright = plugin_info.copyright;
descriptor.Properties = Module::rt_capable ? LADSPA_PROPERTY_HARD_RT_CAPABLE : 0;
descriptor.PortCount = ins + outs + params;
descriptor.PortNames = new char *[descriptor.PortCount];
descriptor.PortDescriptors = new LADSPA_PortDescriptor[descriptor.PortCount];
descriptor.PortRangeHints = new LADSPA_PortRangeHint[descriptor.PortCount];
int i;
for (i = 0; i < ins + outs; i++)
{
LADSPA_PortRangeHint &prh = ((LADSPA_PortRangeHint *)descriptor.PortRangeHints)[i];
((int *)descriptor.PortDescriptors)[i] = i < ins ? LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO
: i < ins + outs ? LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO
: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
prh.HintDescriptor = 0;
((const char **)descriptor.PortNames)[i] = Module::port_names[i];
}
for (; i < ins + outs + params; i++)
{
LADSPA_PortRangeHint &prh = ((LADSPA_PortRangeHint *)descriptor.PortRangeHints)[i];
parameter_properties &pp = Module::param_props[i - ins - outs];
((int *)descriptor.PortDescriptors)[i] =
LADSPA_PORT_CONTROL | (pp.flags & PF_PROP_OUTPUT ? LADSPA_PORT_OUTPUT : LADSPA_PORT_INPUT);
prh.HintDescriptor = LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW;
((const char **)descriptor.PortNames)[i] = pp.name;
prh.LowerBound = pp.min;
prh.UpperBound = pp.max;
switch(pp.flags & PF_TYPEMASK) {
case PF_BOOL:
prh.HintDescriptor |= LADSPA_HINT_TOGGLED;
prh.HintDescriptor &= ~(LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW);
break;
case PF_INT:
case PF_ENUM:
prh.HintDescriptor |= LADSPA_HINT_INTEGER;
break;
default: {
int defpt = (int)(100 * (pp.def_value - pp.min) / (pp.max - pp.min));
if ((pp.flags & PF_SCALEMASK) == PF_SCALE_LOG)
defpt = (int)(100 * log(pp.def_value / pp.min) / log(pp.max / pp.min));
if (defpt < 12)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM;
else if (defpt < 37)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_LOW;
else if (defpt < 63)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE;
else if (defpt < 88)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH;
else
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM;
}
}
if (pp.def_value == 0 || pp.def_value == 1 || pp.def_value == 100 || pp.def_value == 440 ) {
prh.HintDescriptor &= ~LADSPA_HINT_DEFAULT_MASK;
if (pp.def_value == 1)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_1;
else if (pp.def_value == 100)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_100;
else if (pp.def_value == 440)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_440;
else
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_0;
}
switch(pp.flags & PF_SCALEMASK) {
case PF_SCALE_LOG:
prh.HintDescriptor |= LADSPA_HINT_LOGARITHMIC;
break;
}
}
descriptor.ImplementationData = this;
descriptor.instantiate = cb_instantiate;
descriptor.connect_port = cb_connect;
descriptor.activate = cb_activate;
descriptor.run = cb_run;
descriptor.run_adding = NULL;
descriptor.set_run_adding_gain = NULL;
descriptor.deactivate = cb_deactivate;
descriptor.cleanup = cb_cleanup;
#if USE_DSSI
memcpy(&descriptor_for_dssi, &descriptor, sizeof(descriptor));
descriptor_for_dssi.Name = strdup((std::string(plugin_info.name) + " DSSI").c_str());
memset(&dssi_descriptor, 0, sizeof(dssi_descriptor));
dssi_descriptor.DSSI_API_Version = 1;
dssi_descriptor.LADSPA_Plugin = &descriptor_for_dssi;
dssi_descriptor.configure = cb_configure;
dssi_descriptor.get_program = cb_get_program;
dssi_descriptor.select_program = cb_select_program;
if (Module::support_midi)
dssi_descriptor.run_synth = cb_run_synth;
presets = new std::vector<plugin_preset>;
preset_descs = new std::vector<DSSI_Program_Descriptor>;
preset_list plist_tmp, plist;
plist.load_defaults(true);
plist_tmp.load_defaults(false);
plist.presets.insert(plist.presets.end(), plist_tmp.presets.begin(), plist_tmp.presets.end());
// XXXKF this assumes that plugin name in preset is case-insensitive equal to plugin label
// if I forget about this, I'll be in a deep trouble
dssi_default_program.Bank = 0;
dssi_default_program.Program = 0;
dssi_default_program.Name = "default";
int pos = 1;
for (unsigned int i = 0; i < plist.presets.size(); i++)
{
plugin_preset &pp = plist.presets[i];
if (strcasecmp(pp.plugin.c_str(), descriptor.Label))
continue;
DSSI_Program_Descriptor pd;
pd.Bank = pos >> 7;
pd.Program = pos++;
pd.Name = pp.name.c_str();
preset_descs->push_back(pd);
presets->push_back(pp);
}
// printf("presets = %p:%d name = %s\n", presets, presets->size(), descriptor.Label);
#endif
}
~ladspa_wrapper()
{
delete []descriptor.PortNames;
delete []descriptor.PortDescriptors;
delete []descriptor.PortRangeHints;
#if USE_DSSI
presets->clear();
preset_descs->clear();
delete presets;
delete preset_descs;
#endif
}
/// LADSPA instantiation function (create a plugin instance)
static LADSPA_Handle cb_instantiate(const struct _LADSPA_Descriptor * Descriptor, unsigned long sample_rate)
{
instance *mod = new instance();
mod->set_sample_rate(sample_rate);
mod->post_instantiate();
return mod;
}
#if USE_DSSI
/// DSSI get program descriptor function; for 0, it returns the default program (from parameter properties table), for others, it uses global or user preset
static const DSSI_Program_Descriptor *cb_get_program(LADSPA_Handle Instance, unsigned long index) {
if (index > presets->size())
return NULL;
if (index)
return &(*preset_descs)[index - 1];
return &dssi_default_program;
}
/// DSSI select program function; for 0, it sets the defaults, for others, it sets global or user preset
static void cb_select_program(LADSPA_Handle Instance, unsigned long Bank, unsigned long Program) {
instance *mod = (instance *)Instance;
unsigned int no = (Bank << 7) + Program - 1;
// printf("no = %d presets = %p:%d\n", no, presets, presets->size());
if (no == -1U) {
int rpc = ladspa_instance<Module>::real_param_count();
for (int i =0 ; i < rpc; i++)
*mod->params[i] = Module::param_props[i].def_value;
return;
}
if (no >= presets->size())
return;
plugin_preset &p = (*presets)[no];
// printf("activating preset %s\n", p.name.c_str());
p.activate(mod);
}
#endif
/// LADSPA port connection function
static void cb_connect(LADSPA_Handle Instance, unsigned long port, LADSPA_Data *DataLocation) {
unsigned long ins = Module::in_count;
unsigned long outs = Module::out_count;
unsigned long params = ladspa_instance<Module>::real_param_count();
instance *const mod = (instance *)Instance;
if (port < ins)
mod->ins[port] = DataLocation;
else if (port < ins + outs)
mod->outs[port - ins] = DataLocation;
else if (port < ins + outs + params) {
int i = port - ins - outs;
mod->params[i] = DataLocation;
*mod->params[i] = Module::param_props[i].def_value;
}
}
/// LADSPA activate function (note that at this moment the ports are not set)
static void cb_activate(LADSPA_Handle Instance) {
instance *const mod = (instance *)Instance;
mod->activate_flag = true;
}
/// utility function: zero port values if mask is 0
static inline void zero_by_mask(Module *module, uint32_t mask, uint32_t offset, uint32_t nsamples)
{
for (int i=0; i<Module::out_count; i++) {
if ((mask & (1 << i)) == 0) {
dsp::zero(module->outs[i] + offset, nsamples);
}
}
}
/// LADSPA run function - does set sample rate / activate logic when it's run first time after activation
static void cb_run(LADSPA_Handle Instance, unsigned long SampleCount) {
instance *const mod = (instance *)Instance;
if (mod->activate_flag)
{
mod->activate();
mod->activate_flag = false;
}
mod->params_changed();
process_slice(mod, 0, SampleCount);
}
/// utility function: call process, and if it returned zeros in output masks, zero out the relevant output port buffers
static inline void process_slice(Module *mod, uint32_t offset, uint32_t end)
{
while(offset < end)
{
uint32_t newend = std::min(offset + MAX_SAMPLE_RUN, end);
uint32_t out_mask = mod->process(offset, newend - offset, -1, -1);
zero_by_mask(mod, out_mask, offset, newend - offset);
offset = newend;
}
}
#if USE_DSSI
/// DSSI "run synth" function, same as run() except it allows for event delivery
static void cb_run_synth(LADSPA_Handle Instance, unsigned long SampleCount,
snd_seq_event_t *Events, unsigned long EventCount) {
instance *const mod = (instance *)Instance;
if (mod->activate_flag)
{
mod->activate();
mod->activate_flag = false;
}
mod->params_changed();
uint32_t offset = 0;
for (uint32_t e = 0; e < EventCount; e++)
{
uint32_t timestamp = Events[e].time.tick;
if (timestamp != offset)
process_slice(mod, offset, timestamp);
process_dssi_event(mod, Events[e]);
offset = timestamp;
}
if (offset != SampleCount)
process_slice(mod, offset, SampleCount);
}
/// DSSI configure function (named properties)
static char *cb_configure(LADSPA_Handle Instance,
const char *Key,
const char *Value)
{
instance *const mod = (instance *)Instance;
return mod->configure(Key, Value);
}
/// Utility function: handle MIDI event (only handles a subset in this version)
static void process_dssi_event(Module *module, snd_seq_event_t &event)
{
switch(event.type) {
case SND_SEQ_EVENT_NOTEON:
module->note_on(event.data.note.note, event.data.note.velocity);
break;
case SND_SEQ_EVENT_NOTEOFF:
module->note_off(event.data.note.note, event.data.note.velocity);
break;
case SND_SEQ_EVENT_PGMCHANGE:
module->program_change(event.data.control.value);
break;
case SND_SEQ_EVENT_CONTROLLER:
module->control_change(event.data.control.param, event.data.control.value);
break;
case SND_SEQ_EVENT_PITCHBEND:
module->pitch_bend(event.data.control.value);
break;
}
}
#endif
/// LADSPA deactivate function
static void cb_deactivate(LADSPA_Handle Instance) {
instance *const mod = (instance *)Instance;
mod->deactivate();
}
/// LADSPA cleanup (delete instance) function
static void cb_cleanup(LADSPA_Handle Instance) {
instance *const mod = (instance *)Instance;
delete mod;
}
/// Get a wrapper singleton - used to prevent initialization order problems which were present in older versions
static ladspa_wrapper &get() {
static ladspa_wrapper instance;
return instance;
}
};
};
#endif
#endif

View File

@@ -0,0 +1,90 @@
/* Calf DSP Library
* A-weighting filter for
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* Most of code in this file is based on freely
* available other work of other people (filter equations).
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_LOUDNESS_H
#define __CALF_LOUDNESS_H
#include "biquad.h"
namespace dsp {
class aweighter {
public:
biquad_d2<float> bq1, bq2, bq3;
/// Produce one output sample from one input sample
float process(float sample)
{
return bq1.process(bq2.process(bq3.process(sample)));
}
/// Set sample rate (updates filter coefficients)
void set(float sr)
{
// analog coeffs taken from: http://www.diracdelta.co.uk/science/source/a/w/aweighting/source.html
// first we need to adjust them by doing some obscene sort of reverse pre-warping (a broken one, too!)
float f1 = biquad_coeffs<float>::unwarpf(20.6f, sr);
float f2 = biquad_coeffs<float>::unwarpf(107.7f, sr);
float f3 = biquad_coeffs<float>::unwarpf(738.f, sr);
float f4 = biquad_coeffs<float>::unwarpf(12200.f, sr);
// then map s domain to z domain using bilinear transform
// note: f1 and f4 are double poles
bq1.set_bilinear(0, 0, 1, f1*f1, 2 * f1, 1);
bq2.set_bilinear(1, 0, 0, f2*f3, f2 + f3, 1);
bq3.set_bilinear(0, 0, 1, f4*f4, 2 * f4, 1);
// the coeffs above give non-normalized value, so it should be normalized to produce 0dB at 1 kHz
// find actual gain
float gain1kHz = freq_gain(1000.0, sr);
// divide one filter's x[n-m] coefficients by that value
float gc = 1.0 / gain1kHz;
bq1.a0 *= gc;
bq1.a1 *= gc;
bq1.a2 *= gc;
}
/// Reset to zero if at risk of denormals
void sanitize()
{
bq1.sanitize();
bq2.sanitize();
bq3.sanitize();
}
/// Reset state to zero
void reset()
{
bq1.reset();
bq2.reset();
bq3.reset();
}
/// Gain and a given frequency
float freq_gain(float freq, float sr)
{
return bq1.freq_gain(freq, sr) * bq2.freq_gain(freq, sr) * bq3.freq_gain(freq, sr);
}
};
};
#endif

View File

@@ -0,0 +1,281 @@
/* Calf DSP Library
* LV2-related helper classes and functions
*
* Copyright (C) 2001-2008 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_LV2HELPERS_H
#define CALF_LV2HELPERS_H
#if USE_LV2
#include <calf/lv2_event.h>
#include <calf/lv2_uri_map.h>
class uri_map_access
{
public:
/// URI map feature pointer (previously in a mixin, but polymorphic ports made it necessary for most plugins)
LV2_URI_Map_Feature *uri_map;
uri_map_access()
: uri_map(NULL)
{}
/// Map an URI through an URI map
uint32_t map_uri(const char *ns, const char *URI)
{
if (uri_map)
return uri_map->uri_to_id(uri_map->callback_data, ns, URI);
return 0;
}
/// Called on instantiation for every LV2 feature sent by a host
void use_feature(const char *URI, void *data) {
if (!strcmp(URI, LV2_URI_MAP_URI))
{
uri_map = (LV2_URI_Map_Feature *)data;
map_uris();
}
}
virtual void map_uris()
{
}
virtual ~uri_map_access() {}
};
/// A mixin for adding the event feature and URI map to the small plugin
template<class T>
class event_mixin: public T
{
public:
/// Event feature pointer
LV2_Event_Feature *event_feature;
virtual void use_feature(const char *URI, void *data) {
if (!strcmp(URI, LV2_EVENT_URI))
{
event_feature = (LV2_Event_Feature *)data;
}
T::use_feature(URI, data);
}
/// Create a reference
inline void ref_event(LV2_Event *event) { event_feature->lv2_event_ref(event_feature->callback_data, event); }
/// Destroy a reference
inline void unref_event(LV2_Event *event) { event_feature->lv2_event_unref(event_feature->callback_data, event); }
};
/// A mixin for adding the URI map and MIDI event type retrieval to small plugins
template<class T>
class midi_mixin: public virtual event_mixin<T>
{
public:
/// MIDI event ID, as resolved using the URI map feature
uint32_t midi_event_type;
virtual void map_uris() {
midi_event_type = this->map_uri("http://lv2plug.in/ns/ext/event", "http://lv2plug.in/ns/ext/midi#MidiEvent");
printf("MIDI event type = %d\n", midi_event_type);
event_mixin<T>::map_uris();
}
};
/// A mixin for adding the URI map and MIDI event type retrieval to small plugins
template<class T>
class message_mixin: public virtual event_mixin<T>
{
public:
/// MIDI event ID, as resolved using the URI map feature
uint32_t message_event_type;
virtual void map_uris() {
message_event_type = this->map_uri("http://lv2plug.in/ns/ext/event", "http://lv2plug.in/ns/dev/msg#MessageEvent");
printf("Message event type = %d\n", message_event_type);
event_mixin<T>::map_uris();
}
};
/// LV2 event structure + payload as 0-length array for easy access
struct lv2_event: public LV2_Event
{
uint8_t data[];
inline lv2_event &operator=(const lv2_event &src) {
*(LV2_Event *)this = (const LV2_Event &)src;
memcpy(data, src.data, src.size);
return *this;
}
/// Returns a 64-bit timestamp for easy and inefficient comparison
inline uint64_t timestamp() const {
return ((uint64_t)frames << 32) | subframes;
}
private:
/// forbid default constructor - this object cannot be constructed, only obtained via cast from LV2_Event* (or &) to lv2_event* (or &)
lv2_event() {}
/// forbid copy constructor - see default constructor
lv2_event(const lv2_event &) {}
};
/// A read-only iterator-like object for reading from event buffers
class event_port_read_iterator
{
protected:
const LV2_Event_Buffer *buffer;
uint32_t offset;
public:
/// Default constructor creating a useless iterator you can assign to
event_port_read_iterator()
: buffer(NULL)
, offset(0)
{
}
/// Create an iterator based on specified buffer and index/offset values
event_port_read_iterator(const LV2_Event_Buffer *_buffer, uint32_t _offset = 0)
: buffer(_buffer)
, offset(0)
{
}
/// Are any data left to be read?
inline operator bool() const {
return offset < buffer->size;
}
/// Read pointer
inline const lv2_event &operator*() const {
return *(const lv2_event *)(buffer->data + offset);
}
/// Pointer to member
inline const lv2_event *operator->() const {
return &**this;
}
/// Move to the next element
inline event_port_read_iterator operator++() {
offset += ((**this).size + 19) &~7;
return *this;
}
/// Move to the next element
inline event_port_read_iterator operator++(int) {
event_port_read_iterator old = *this;
offset += ((**this).size + 19) &~7;
return old;
}
};
/// A write-only iterator-like object for writing to event buffers
class event_port_write_iterator
{
protected:
LV2_Event_Buffer *buffer;
public:
/// Default constructor creating a useless iterator you can assign to
event_port_write_iterator()
: buffer(NULL)
{
}
/// Create a write iterator based on specified buffer and index/offset values
event_port_write_iterator(LV2_Event_Buffer *_buffer)
: buffer(_buffer)
{
}
/// @return the remaining buffer space
inline uint32_t space_left() const {
return buffer->capacity - buffer->size;
}
/// @return write pointer
inline lv2_event &operator*() {
return *(lv2_event *)(buffer->data + buffer->size);
}
/// Pointer to member
inline lv2_event *operator->() {
return &**this;
}
/// Move to the next element after the current one has been written (must be called after each write)
inline event_port_write_iterator operator++() {
buffer->size += ((**this).size + 19) &~7;
buffer->event_count ++;
return *this;
}
/// Move to the next element after the current one has been written
inline lv2_event *operator++(int) {
lv2_event *ptr = &**this;
buffer->size += ((**this).size + 19) &~7;
buffer->event_count ++;
return ptr;
}
};
template<class Iter1, class Iter2>
class event_port_merge_iterator
{
public:
Iter1 first;
Iter2 second;
public:
event_port_merge_iterator() {}
event_port_merge_iterator(const Iter1 &_first, const Iter2 &_second)
: first(_first)
, second(_second)
{
}
/// @retval true if any of the iterators have any data left
inline operator bool() const {
return ((bool)first) || ((bool)second);
}
inline bool select_first() const
{
if (!(bool)second)
return true;
if (!(bool)first)
return false;
return first->timestamp() < second->timestamp();
}
/// Returns the earliest of (*first, *second)
inline const lv2_event &operator*() const {
if (select_first())
{
assert((bool)first);
return *first;
}
assert((bool)second);
return *second;
}
/// Pointer to member
inline const lv2_event *operator->() const {
return &**this;
}
/// Prefix increment
inline event_port_merge_iterator operator++() {
if (select_first())
first++;
else
second++;
return *this;
}
/// Postfix increment
inline event_port_merge_iterator operator++(int) {
event_port_merge_iterator ptr = *this;
if (select_first())
first++;
else
second++;
return ptr;
}
};
#endif
#endif

View File

@@ -0,0 +1,349 @@
/* Calf DSP Library
* LV2 wrapper templates
*
* Copyright (C) 2001-2008 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_LV2WRAP_H
#define CALF_LV2WRAP_H
#if USE_LV2
#include <string>
#include <vector>
#include <lv2.h>
#include <calf/giface.h>
#include <calf/lv2-midiport.h>
#include <calf/lv2_contexts.h>
#include <calf/lv2_event.h>
#include <calf/lv2_progress.h>
#include <calf/lv2_string_port.h>
#include <calf/lv2_uri_map.h>
namespace calf_plugins {
template<class Module>
struct lv2_instance: public plugin_ctl_iface, public progress_report_iface, public Module
{
bool set_srate;
int srate_to_set;
LV2_MIDI *midi_data;
LV2_Event_Buffer *event_data;
LV2_URI_Map_Feature *uri_map;
LV2_Event_Feature *event_feature;
uint32_t midi_event_type;
std::vector<int> message_params;
LV2_Progress *progress_report_feature;
lv2_instance()
{
for (int i=0; i < Module::in_count; i++)
Module::ins[i] = NULL;
for (int i=0; i < Module::out_count; i++)
Module::outs[i] = NULL;
for (int i=0; i < Module::param_count; i++)
Module::params[i] = NULL;
uri_map = NULL;
midi_data = NULL;
event_data = NULL;
midi_event_type = 0xFFFFFFFF;
set_srate = true;
srate_to_set = 44100;
get_message_context_parameters(message_params);
progress_report_feature = NULL;
// printf("message params %d\n", (int)message_params.size());
}
/// This, and not Module::post_instantiate, is actually called by lv2_wrapper class
void post_instantiate()
{
if (progress_report_feature)
Module::progress_report = this;
Module::post_instantiate();
}
virtual parameter_properties *get_param_props(int param_no)
{
return &Module::param_props[param_no];
}
virtual float get_param_value(int param_no)
{
return *Module::params[param_no];
}
virtual void set_param_value(int param_no, float value)
{
*Module::params[param_no] = value;
}
virtual int get_param_count()
{
return Module::param_count;
}
virtual int get_param_port_offset()
{
return Module::in_count + Module::out_count;
}
virtual const char *get_gui_xml() {
return Module::get_gui_xml();
}
virtual line_graph_iface *get_line_graph_iface()
{
return dynamic_cast<line_graph_iface *>(this);
}
virtual bool activate_preset(int bank, int program) {
return false;
}
virtual const char *get_name()
{
return Module::get_name();
}
virtual const char *get_id()
{
return Module::get_id();
}
virtual const char *get_label()
{
return Module::get_label();
}
virtual int get_input_count() { return Module::in_count; }
virtual int get_output_count() { return Module::out_count; }
virtual bool get_midi() { return Module::support_midi; }
virtual float get_level(unsigned int port) { return 0.f; }
virtual void execute(int cmd_no) {
Module::execute(cmd_no);
}
virtual void report_progress(float percentage, const std::string &message) {
if (progress_report_feature)
(*progress_report_feature->progress)(progress_report_feature->context, percentage, !message.empty() ? message.c_str() : NULL);
}
void send_configures(send_configure_iface *sci) {
Module::send_configures(sci);
}
uint32_t impl_message_run(const void *valid_inputs, void *output_ports) {
for (unsigned int i = 0; i < message_params.size(); i++)
{
int pn = message_params[i];
parameter_properties &pp = *get_param_props(pn);
if ((pp.flags & PF_TYPEMASK) == PF_STRING
&& (((LV2_String_Data *)Module::params[pn])->flags & LV2_STRING_DATA_CHANGED_FLAG)) {
printf("Calling configure on %s\n", pp.short_name);
configure(pp.short_name, ((LV2_String_Data *)Module::params[pn])->data);
}
}
return Module::message_run(valid_inputs, output_ports);
}
char *configure(const char *key, const char *value) {
// disambiguation - the plugin_ctl_iface version is just a stub, so don't use it
return Module::configure(key, value);
}
#if 0
// the default implementation should be fine
virtual void clear_preset() {
// This is never called in practice, at least for now
// However, it will change when presets are implemented
for (int i=0; i < Module::param_count; i++)
*Module::params[i] = Module::param_props[i].def_value;
/*
const char **p = Module::get_default_configure_vars();
if (p)
{
for(; p[0]; p += 2)
configure(p[0], p[1]);
}
*/
}
#endif
};
struct LV2_Calf_Descriptor {
plugin_ctl_iface *(*get_pci)(LV2_Handle Instance);
};
template<class Module>
struct lv2_wrapper
{
typedef lv2_instance<Module> instance;
static LV2_Descriptor descriptor;
static LV2_Calf_Descriptor calf_descriptor;
static LV2MessageContext message_context;
std::string uri;
lv2_wrapper()
{
ladspa_plugin_info &info = Module::plugin_info;
uri = "http://calf.sourceforge.net/plugins/" + std::string(info.label);
descriptor.URI = uri.c_str();
descriptor.instantiate = cb_instantiate;
descriptor.connect_port = cb_connect;
descriptor.activate = cb_activate;
descriptor.run = cb_run;
descriptor.deactivate = cb_deactivate;
descriptor.cleanup = cb_cleanup;
descriptor.extension_data = cb_ext_data;
calf_descriptor.get_pci = cb_get_pci;
message_context.message_connect_port = cb_connect;
message_context.message_run = cb_message_run;
}
static void cb_connect(LV2_Handle Instance, uint32_t port, void *DataLocation) {
unsigned long ins = Module::in_count;
unsigned long outs = Module::out_count;
unsigned long params = Module::param_count;
instance *const mod = (instance *)Instance;
if (port < ins)
mod->ins[port] = (float *)DataLocation;
else if (port < ins + outs)
mod->outs[port - ins] = (float *)DataLocation;
else if (port < ins + outs + params) {
int i = port - ins - outs;
mod->params[i] = (float *)DataLocation;
}
else if (Module::support_midi && port == ins + outs + params) {
mod->event_data = (LV2_Event_Buffer *)DataLocation;
}
}
static void cb_activate(LV2_Handle Instance) {
instance *const mod = (instance *)Instance;
mod->set_srate = true;
}
static void cb_deactivate(LV2_Handle Instance) {
instance *const mod = (instance *)Instance;
mod->deactivate();
}
static LV2_Handle cb_instantiate(const LV2_Descriptor * Descriptor, double sample_rate, const char *bundle_path, const LV2_Feature *const *features)
{
instance *mod = new instance();
// XXXKF some people use fractional sample rates; we respect them ;-)
mod->srate_to_set = (uint32_t)sample_rate;
mod->set_srate = true;
while(*features)
{
if (!strcmp((*features)->URI, LV2_URI_MAP_URI))
{
mod->uri_map = (LV2_URI_Map_Feature *)((*features)->data);
mod->midi_event_type = mod->uri_map->uri_to_id(
mod->uri_map->callback_data,
"http://lv2plug.in/ns/ext/event",
"http://lv2plug.in/ns/ext/midi#MidiEvent");
}
else if (!strcmp((*features)->URI, LV2_EVENT_URI))
{
mod->event_feature = (LV2_Event_Feature *)((*features)->data);
}
else if (!strcmp((*features)->URI, LV2_PROGRESS_URI))
{
mod->progress_report_feature = (LV2_Progress *)((*features)->data);
}
features++;
}
mod->post_instantiate();
return mod;
}
static inline void zero_by_mask(Module *module, uint32_t mask, uint32_t offset, uint32_t nsamples)
{
for (int i=0; i<Module::out_count; i++) {
if ((mask & (1 << i)) == 0) {
dsp::zero(module->outs[i] + offset, nsamples);
}
}
}
static plugin_ctl_iface *cb_get_pci(LV2_Handle Instance)
{
return static_cast<plugin_ctl_iface *>(Instance);
}
static inline void process_slice(Module *mod, uint32_t offset, uint32_t end)
{
while(offset < end)
{
uint32_t newend = std::min(offset + MAX_SAMPLE_RUN, end);
uint32_t out_mask = mod->process(offset, newend - offset, -1, -1);
zero_by_mask(mod, out_mask, offset, newend - offset);
offset = newend;
}
}
static uint32_t cb_message_run(LV2_Handle Instance, const void *valid_inputs, void *outputs_written) {
instance *mod = (instance *)Instance;
return mod->impl_message_run(valid_inputs, outputs_written);
}
static void cb_run(LV2_Handle Instance, uint32_t SampleCount) {
instance *const mod = (instance *)Instance;
if (mod->set_srate) {
mod->set_sample_rate(mod->srate_to_set);
mod->activate();
mod->set_srate = false;
}
mod->params_changed();
uint32_t offset = 0;
if (mod->event_data)
{
// printf("Event data: count %d\n", mod->event_data->event_count);
struct LV2_Midi_Event: public LV2_Event {
unsigned char data[1];
};
unsigned char *data = (unsigned char *)(mod->event_data->data);
for (uint32_t i = 0; i < mod->event_data->event_count; i++) {
LV2_Midi_Event *item = (LV2_Midi_Event *)data;
uint32_t ts = item->frames;
// printf("Event: timestamp %d subframes %d type %d vs %d\n", item->frames, item->subframes, item->type, mod->midi_event_type);
if (ts > offset)
{
process_slice(mod, offset, ts);
offset = ts;
}
if (item->type == mod->midi_event_type)
{
// printf("Midi message %x %x %x %x %d\n", item->data[0], item->data[1], item->data[2], item->data[3], item->size);
switch(item->data[0] >> 4)
{
case 8: mod->note_off(item->data[1], item->data[2]); break;
case 9: mod->note_on(item->data[1], item->data[2]); break;
case 11: mod->control_change(item->data[1], item->data[2]); break;
case 12: mod->program_change(item->data[1]); break;
case 14: mod->pitch_bend(item->data[1] + 128 * item->data[2] - 8192); break;
}
}
else
if (item->type == 0 && mod->event_feature)
mod->event_feature->lv2_event_unref(mod->event_feature->callback_data, item);
// printf("timestamp %f item size %d first byte %x\n", item->timestamp, item->size, item->data[0]);
data += ((sizeof(LV2_Event) + item->size + 7))&~7;
}
}
process_slice(mod, offset, SampleCount);
}
static void cb_cleanup(LV2_Handle Instance) {
instance *const mod = (instance *)Instance;
delete mod;
}
static const void *cb_ext_data(const char *URI) {
if (!strcmp(URI, "http://foltman.com/ns/calf-plugin-instance"))
return &calf_descriptor;
if (!strcmp(URI, LV2_CONTEXT_MESSAGE))
return &message_context;
return NULL;
}
static lv2_wrapper &get() {
static lv2_wrapper instance;
return instance;
}
};
};
#endif
#endif

View File

@@ -0,0 +1,312 @@
/* Calf DSP Library
* Audio module (plugin) metadata - header file
*
* Copyright (C) 2007-2008 Krzysztof Foltman
* Copyright (C) 2008 Thor Harald Johansen <thj@thj.no>
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_METADATA_H
#define __CALF_METADATA_H
#include "giface.h"
namespace calf_plugins {
struct flanger_metadata: public plugin_metadata<flanger_metadata>
{
public:
enum { par_delay, par_depth, par_rate, par_fb, par_stereo, par_reset, par_amount, par_dryamount, param_count };
enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
PLUGIN_NAME_ID_LABEL("flanger", "flanger", "Flanger")
};
struct phaser_metadata: public plugin_metadata<phaser_metadata>
{
enum { par_freq, par_depth, par_rate, par_fb, par_stages, par_stereo, par_reset, par_amount, par_dryamount, param_count };
enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
PLUGIN_NAME_ID_LABEL("phaser", "phaser", "Phaser")
};
struct filter_metadata: public plugin_metadata<filter_metadata>
{
enum { par_cutoff, par_resonance, par_mode, par_inertia, param_count };
enum { in_count = 2, out_count = 2, rt_capable = true, require_midi = false, support_midi = false };
PLUGIN_NAME_ID_LABEL("filter", "filter", "Filter")
/// do not export mode and inertia as CVs, as those are settings and not parameters
bool is_cv(int param_no) { return param_no != par_mode && param_no != par_inertia; }
};
/// Filterclavier - metadata
struct filterclavier_metadata: public plugin_metadata<filterclavier_metadata>
{
enum { par_transpose, par_detune, par_max_resonance, par_mode, par_inertia, param_count };
enum { in_count = 2, out_count = 2, rt_capable = true, require_midi = true, support_midi = true };
PLUGIN_NAME_ID_LABEL("filterclavier", "filterclavier", "Filterclavier")
/// do not export mode and inertia as CVs, as those are settings and not parameters
bool is_cv(int param_no) { return param_no != par_mode && param_no != par_inertia; }
};
struct reverb_metadata: public plugin_metadata<reverb_metadata>
{
enum { par_decay, par_hfdamp, par_roomsize, par_diffusion, par_amount, par_dry, par_predelay, par_basscut, par_treblecut, param_count };
enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
PLUGIN_NAME_ID_LABEL("reverb", "reverb", "Reverb")
};
struct vintage_delay_metadata: public plugin_metadata<vintage_delay_metadata>
{
enum { par_bpm, par_divide, par_time_l, par_time_r, par_feedback, par_amount, par_mixmode, par_medium, par_dryamount, param_count };
enum { in_count = 2, out_count = 2, rt_capable = true, support_midi = false, require_midi = false };
PLUGIN_NAME_ID_LABEL("vintage_delay", "vintagedelay", "Vintage Delay")
};
struct rotary_speaker_metadata: public plugin_metadata<rotary_speaker_metadata>
{
public:
enum { par_speed, par_spacing, par_shift, par_moddepth, par_treblespeed, par_bassspeed, par_micdistance, par_reflection, param_count };
enum { in_count = 2, out_count = 2, support_midi = true, require_midi = false, rt_capable = true };
PLUGIN_NAME_ID_LABEL("rotary_speaker", "rotaryspeaker", "Rotary Speaker")
};
/// A multitap stereo chorus thing - metadata
struct multichorus_metadata: public plugin_metadata<multichorus_metadata>
{
public:
enum { par_delay, par_depth, par_rate, par_stereo, par_voices, par_vphase, par_amount, par_dryamount, par_freq, par_freq2, par_q, par_overlap, param_count };
enum { in_count = 2, out_count = 2, rt_capable = true, support_midi = false, require_midi = false };
PLUGIN_NAME_ID_LABEL("multichorus", "multichorus", "Multi Chorus")
};
/// Monosynth - metadata
struct monosynth_metadata: public plugin_metadata<monosynth_metadata>
{
enum { wave_saw, wave_sqr, wave_pulse, wave_sine, wave_triangle, wave_varistep, wave_skewsaw, wave_skewsqr, wave_test1, wave_test2, wave_test3, wave_test4, wave_test5, wave_test6, wave_test7, wave_test8, wave_count };
enum { flt_lp12, flt_lp24, flt_2lp12, flt_hp12, flt_lpbr, flt_hpbr, flt_bp6, flt_2bp6 };
enum { par_wave1, par_wave2, par_pw1, par_pw2, par_detune, par_osc2xpose, par_oscmode, par_oscmix, par_filtertype, par_cutoff, par_resonance, par_cutoffsep, par_envmod, par_envtores, par_envtoamp, par_attack, par_decay, par_sustain, par_fade, par_release,
par_keyfollow, par_legato, par_portamento, par_vel2filter, par_vel2amp, par_master, par_pwhlrange,
par_lforate, par_lfodelay, par_lfofilter, par_lfopitch, par_lfopw, par_mwhl_lfo, par_scaledetune,
param_count };
enum { in_count = 0, out_count = 2, support_midi = true, require_midi = true, rt_capable = true };
enum { step_size = 64, step_shift = 6 };
enum {
modsrc_none,
modsrc_velocity,
modsrc_pressure,
modsrc_modwheel,
modsrc_env1,
modsrc_lfo1,
modsrc_count,
};
enum {
moddest_none,
moddest_attenuation,
moddest_oscmix,
moddest_cutoff,
moddest_resonance,
moddest_o1detune,
moddest_o2detune,
moddest_o1pw,
moddest_o2pw,
moddest_count,
};
PLUGIN_NAME_ID_LABEL("monosynth", "monosynth", "Monosynth")
};
/// Thor's compressor - metadata
struct compressor_metadata: public plugin_metadata<compressor_metadata>
{
enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
enum { param_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_aweighting, param_compression, param_peak, param_clip, param_bypass, // param_freq, param_bw,
param_count };
PLUGIN_NAME_ID_LABEL("compressor", "compressor", "Compressor")
};
/// Organ - enums for parameter IDs etc. (this mess is caused by organ split between plugin and generic class - which was
/// a bad design decision and should be sorted out some day) XXXKF @todo
struct organ_enums
{
enum {
par_drawbar1, par_drawbar2, par_drawbar3, par_drawbar4, par_drawbar5, par_drawbar6, par_drawbar7, par_drawbar8, par_drawbar9,
par_frequency1, par_frequency2, par_frequency3, par_frequency4, par_frequency5, par_frequency6, par_frequency7, par_frequency8, par_frequency9,
par_waveform1, par_waveform2, par_waveform3, par_waveform4, par_waveform5, par_waveform6, par_waveform7, par_waveform8, par_waveform9,
par_detune1, par_detune2, par_detune3, par_detune4, par_detune5, par_detune6, par_detune7, par_detune8, par_detune9,
par_phase1, par_phase2, par_phase3, par_phase4, par_phase5, par_phase6, par_phase7, par_phase8, par_phase9,
par_pan1, par_pan2, par_pan3, par_pan4, par_pan5, par_pan6, par_pan7, par_pan8, par_pan9,
par_routing1, par_routing2, par_routing3, par_routing4, par_routing5, par_routing6, par_routing7, par_routing8, par_routing9,
par_foldover,
par_percdecay, par_perclevel, par_percwave, par_percharm, par_percvel2amp,
par_percfmdecay, par_percfmdepth, par_percfmwave, par_percfmharm, par_percvel2fm,
par_perctrigger, par_percstereo,
par_filterchain,
par_filter1type,
par_master,
par_f1cutoff, par_f1res, par_f1env1, par_f1env2, par_f1env3, par_f1keyf,
par_f2cutoff, par_f2res, par_f2env1, par_f2env2, par_f2env3, par_f2keyf,
par_eg1attack, par_eg1decay, par_eg1sustain, par_eg1release, par_eg1velscl, par_eg1ampctl,
par_eg2attack, par_eg2decay, par_eg2sustain, par_eg2release, par_eg2velscl, par_eg2ampctl,
par_eg3attack, par_eg3decay, par_eg3sustain, par_eg3release, par_eg3velscl, par_eg3ampctl,
par_lforate, par_lfoamt, par_lfowet, par_lfophase, par_lfomode,
par_transpose, par_detune,
par_polyphony,
par_quadenv,
par_pwhlrange,
par_bassfreq,
par_bassgain,
par_treblefreq,
par_treblegain,
par_var_mapcurve,
param_count
};
enum {
var_count = 1
};
enum organ_waveform {
wave_sine,
wave_sinepl1, wave_sinepl2, wave_sinepl3,
wave_ssaw, wave_ssqr, wave_spls, wave_saw, wave_sqr, wave_pulse, wave_sinepl05, wave_sqr05, wave_halfsin, wave_clvg, wave_bell, wave_bell2,
wave_w1, wave_w2, wave_w3, wave_w4, wave_w5, wave_w6, wave_w7, wave_w8, wave_w9,
wave_dsaw, wave_dsqr, wave_dpls,
wave_count_small,
wave_strings = wave_count_small,
wave_strings2,
wave_sinepad,
wave_bellpad,
wave_space,
wave_choir,
wave_choir2,
wave_choir3,
wave_count,
wave_count_big = wave_count - wave_count_small
};
enum {
ampctl_none,
ampctl_direct,
ampctl_f1,
ampctl_f2,
ampctl_all,
ampctl_count
};
enum {
lfomode_off = 0,
lfomode_direct,
lfomode_filter1,
lfomode_filter2,
lfomode_voice,
lfomode_global,
lfomode_count
};
enum {
perctrig_first = 0,
perctrig_each,
perctrig_eachplus,
perctrig_polyphonic,
perctrig_count
};
};
/// Organ - metadata
struct organ_metadata: public organ_enums, public plugin_metadata<organ_metadata>
{
enum { in_count = 0, out_count = 2, support_midi = true, require_midi = true, rt_capable = true };
PLUGIN_NAME_ID_LABEL("organ", "organ", "Organ")
plugin_command_info *get_commands();
const char **get_default_configure_vars();
};
/// FluidSynth - metadata
struct fluidsynth_metadata: public plugin_metadata<fluidsynth_metadata>
{
enum { par_master, par_soundfont, par_interpolation, par_reverb, par_chorus, param_count };
enum { in_count = 0, out_count = 2, support_midi = true, require_midi = true, rt_capable = false };
PLUGIN_NAME_ID_LABEL("fluidsynth", "fluidsynth", "Fluidsynth")
const char **get_default_configure_vars();
};
/// Wavetable - metadata
struct wavetable_metadata: public plugin_metadata<wavetable_metadata>
{
enum {
wt_fmshiny,
wt_fmshiny2,
wt_rezo,
wt_metal,
wt_bell,
wt_blah,
wt_pluck,
wt_stretch,
wt_stretch2,
wt_hardsync,
wt_hardsync2,
wt_softsync,
wt_bell2,
wt_bell3,
wt_tine,
wt_tine2,
wt_clav,
wt_clav2,
wt_gtr,
wt_gtr2,
wt_gtr3,
wt_gtr4,
wt_gtr5,
wt_reed,
wt_reed2,
wt_silver,
wt_brass,
wt_multi,
wt_multi2,
wt_count
};
enum {
modsrc_none,
modsrc_velocity,
modsrc_pressure,
modsrc_modwheel,
modsrc_env1,
modsrc_env2,
modsrc_env3,
modsrc_count,
};
enum {
moddest_none,
moddest_attenuation,
moddest_oscmix,
moddest_cutoff,
moddest_resonance,
moddest_o1shift,
moddest_o2shift,
moddest_o1detune,
moddest_o2detune,
moddest_count,
};
enum {
par_o1wave, par_o1offset, par_o1transpose, par_o1detune, par_o1level,
par_o2wave, par_o2offset, par_o2transpose, par_o2detune, par_o2level,
par_eg1attack, par_eg1decay, par_eg1sustain, par_eg1fade, par_eg1release, par_eg1velscl,
par_eg2attack, par_eg2decay, par_eg2sustain, par_eg2fade, par_eg2release, par_eg2velscl,
par_eg3attack, par_eg3decay, par_eg3sustain, par_eg3fade, par_eg3release, par_eg3velscl,
par_pwhlrange,
param_count };
enum { in_count = 0, out_count = 2, support_midi = true, require_midi = true, rt_capable = true };
enum { step_size = 64 };
PLUGIN_NAME_ID_LABEL("wavetable", "wavetable", "Wavetable")
};
};
#endif

View File

@@ -0,0 +1,112 @@
/* Calf DSP Library
* Modulation matrix boilerplate code.
*
* Copyright (C) 2009 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_MODMATRIX_H
#define __CALF_MODMATRIX_H
#include "giface.h"
namespace dsp {
/// Mapping modes
enum mapping_mode {
map_positive, ///< 0..100%
map_bipolar, ///< -100%..100%
map_negative, ///< -100%..0%
map_squared, ///< x^2
map_squared_bipolar, ///< x^2 scaled to -100%..100%
map_antisquared, ///< 1-(1-x)^2 scaled to 0..100%
map_antisquared_bipolar, ///< 1-(1-x)^2 scaled to -100..100%
map_parabola, ///< inverted parabola (peaks at 0.5, then decreases to 0)
map_type_count
};
/// Single entry in modulation matrix
struct modulation_entry
{
/// Mapped source
int src1;
/// Source mapping mode
mapping_mode mapping;
/// Unmapped modulating source
int src2;
/// Modulation amount
float amount;
/// Modulation destination
int dest;
modulation_entry() {
reset();
}
/// Reset the row to default
void reset() {
src1 = 0;
src2 = 0;
mapping = map_positive;
amount = 0.f;
dest = 0;
}
};
};
namespace calf_plugins {
class mod_matrix: public table_edit_iface
{
protected:
/// Polynomials for different scaling modes (1, x, x^2)
static const float scaling_coeffs[dsp::map_type_count][3];
/// Column descriptions for table widget
table_column_info table_columns[6];
dsp::modulation_entry *matrix;
unsigned int matrix_rows;
const char **mod_src_names, **mod_dest_names;
mod_matrix(dsp::modulation_entry *_matrix, unsigned int _rows, const char **_src_names, const char **_dest_names);
public:
virtual const table_column_info *get_table_columns(int param);
virtual uint32_t get_table_rows(int param);
virtual std::string get_cell(int param, int row, int column);
virtual void set_cell(int param, int row, int column, const std::string &src, std::string &error);
/// Process modulation matrix, calculate outputs from inputs
inline void calculate_modmatrix(float *moddest, int moddest_count, float *modsrc)
{
for (int i = 0; i < moddest_count; i++)
moddest[i] = 0;
for (unsigned int i = 0; i < matrix_rows; i++)
{
dsp::modulation_entry &slot = matrix[i];
if (slot.dest) {
float value = modsrc[slot.src1];
const float *c = scaling_coeffs[slot.mapping];
value = c[0] + c[1] * value + c[2] * value * value;
moddest[slot.dest] += value * modsrc[slot.src2] * slot.amount;
}
}
}
};
};
#endif

View File

@@ -0,0 +1,95 @@
#ifdef PER_MODULE_ITEM
PER_MODULE_ITEM(filter, false, "filter")
PER_MODULE_ITEM(filterclavier, false, "filterclavier")
PER_MODULE_ITEM(flanger, false, "flanger")
PER_MODULE_ITEM(reverb, false, "reverb")
PER_MODULE_ITEM(monosynth, true, "monosynth")
PER_MODULE_ITEM(vintage_delay, false, "vintagedelay")
PER_MODULE_ITEM(organ, true, "organ")
PER_MODULE_ITEM(rotary_speaker, false, "rotaryspeaker")
PER_MODULE_ITEM(phaser, false, "phaser")
PER_MODULE_ITEM(multichorus, false, "multichorus")
PER_MODULE_ITEM(compressor, false, "compressor")
#ifdef ENABLE_EXPERIMENTAL
PER_MODULE_ITEM(fluidsynth, true, "fluidsynth")
PER_MODULE_ITEM(wavetable, true, "wavetable")
#endif
#undef PER_MODULE_ITEM
#endif
#ifdef PER_SMALL_MODULE_ITEM
#ifdef ENABLE_EXPERIMENTAL
PER_SMALL_MODULE_ITEM(lp_filter, "lowpass12")
PER_SMALL_MODULE_ITEM(hp_filter, "highpass12")
PER_SMALL_MODULE_ITEM(bp_filter, "bandpass6")
PER_SMALL_MODULE_ITEM(br_filter, "notch6")
PER_SMALL_MODULE_ITEM(onepole_lp_filter, "lowpass6")
PER_SMALL_MODULE_ITEM(onepole_hp_filter, "highpass6")
PER_SMALL_MODULE_ITEM(onepole_ap_filter, "allpass")
PER_SMALL_MODULE_ITEM(min, "min")
PER_SMALL_MODULE_ITEM(max, "max")
PER_SMALL_MODULE_ITEM(minus, "minus")
PER_SMALL_MODULE_ITEM(mul, "mul")
PER_SMALL_MODULE_ITEM(neg, "neg")
PER_SMALL_MODULE_ITEM(min_c, "min_c")
PER_SMALL_MODULE_ITEM(max_c, "max_c")
PER_SMALL_MODULE_ITEM(minus_c, "minus_c")
PER_SMALL_MODULE_ITEM(mul_c, "mul_c")
PER_SMALL_MODULE_ITEM(neg_c, "neg_c")
PER_SMALL_MODULE_ITEM(level2edge_c, "level2edge_c")
PER_SMALL_MODULE_ITEM(map_lin2exp, "lin2exp")
PER_SMALL_MODULE_ITEM(square_osc, "square_osc")
PER_SMALL_MODULE_ITEM(saw_osc, "saw_osc")
PER_SMALL_MODULE_ITEM(square_lfo, "square_lfo")
PER_SMALL_MODULE_ITEM(saw_lfo, "saw_lfo")
PER_SMALL_MODULE_ITEM(pulse_lfo, "pulse_lfo")
PER_SMALL_MODULE_ITEM(print_a, "print_a")
PER_SMALL_MODULE_ITEM(print_c, "print_c")
PER_SMALL_MODULE_ITEM(print_e, "print_e")
PER_SMALL_MODULE_ITEM(print_em, "print_em")
PER_SMALL_MODULE_ITEM(copy_em, "copy_em")
PER_SMALL_MODULE_ITEM(notefilter_m, "notefilter_m")
PER_SMALL_MODULE_ITEM(ccfilter_m, "ccfilter_m")
PER_SMALL_MODULE_ITEM(pcfilter_m, "pcfilter_m")
PER_SMALL_MODULE_ITEM(pressurefilter_m, "pressurefilter_m")
PER_SMALL_MODULE_ITEM(pitchbendfilter_m, "pitchbendfilter_m")
PER_SMALL_MODULE_ITEM(systemfilter_m, "systemfilter_m")
PER_SMALL_MODULE_ITEM(channelfilter_m, "channelfilter_m")
PER_SMALL_MODULE_ITEM(keyfilter_m, "keyfilter_m")
PER_SMALL_MODULE_ITEM(setchannel_m, "setchannel_m")
PER_SMALL_MODULE_ITEM(key_less_than_m, "key_less_than_m")
PER_SMALL_MODULE_ITEM(channel_less_than_m, "channel_less_than_m")
PER_SMALL_MODULE_ITEM(transpose_m, "transpose_m")
PER_SMALL_MODULE_ITEM(eventmerge_e, "eventmerge_e")
PER_SMALL_MODULE_ITEM(quadpower_a, "quadpower_a")
PER_SMALL_MODULE_ITEM(quadpower_c, "quadpower_c")
PER_SMALL_MODULE_ITEM(crossfader2_a, "crossfader2_a")
PER_SMALL_MODULE_ITEM(crossfader2_c, "crossfader2_c")
PER_SMALL_MODULE_ITEM(linear_inertia_c, "linear_inertia_c")
PER_SMALL_MODULE_ITEM(exp_inertia_c, "exp_inertia_c")
PER_SMALL_MODULE_ITEM(sample_hold_edge_c, "sample_hold_edge_c")
PER_SMALL_MODULE_ITEM(sample_hold_level_c, "sample_hold_level_c")
PER_SMALL_MODULE_ITEM(bit_and_c, "bit_and_c")
PER_SMALL_MODULE_ITEM(bit_or_c, "bit_or_c")
PER_SMALL_MODULE_ITEM(bit_xor_c, "bit_xor_c")
PER_SMALL_MODULE_ITEM(logical_and_c, "logical_and_c")
PER_SMALL_MODULE_ITEM(logical_or_c, "logical_or_c")
PER_SMALL_MODULE_ITEM(logical_xor_c, "logical_xor_c")
PER_SMALL_MODULE_ITEM(logical_not_c, "logical_not_c")
PER_SMALL_MODULE_ITEM(flipflop_c, "flipflop_c")
PER_SMALL_MODULE_ITEM(schmitt_c, "schmitt_c")
PER_SMALL_MODULE_ITEM(between_c, "between_c")
PER_SMALL_MODULE_ITEM(less_c, "less_c")
PER_SMALL_MODULE_ITEM(clip_c, "clip_c")
PER_SMALL_MODULE_ITEM(trigger_a2c, "trigger_a2c")
PER_SMALL_MODULE_ITEM(timer_c, "timer_c")
PER_SMALL_MODULE_ITEM(prio_mux_c, "prio_mux_c")
PER_SMALL_MODULE_ITEM(prio_enc8_c, "prio_enc8_c")
PER_SMALL_MODULE_ITEM(ifthenelse_c, "ifthenelse_c")
PER_SMALL_MODULE_ITEM(counter_c, "counter_c")
PER_SMALL_MODULE_ITEM(mux4_c, "mux4_c")
PER_SMALL_MODULE_ITEM(mux8_c, "mux8_c")
PER_SMALL_MODULE_ITEM(mux16_c, "mux16_c")
PER_SMALL_MODULE_ITEM(msgread_e, "msgread_e")
#endif
#undef PER_SMALL_MODULE_ITEM
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
/* Calf DSP Library
* Prototype audio modules
*
* Copyright (C) 2008 Thor Harald Johansen <thj@thj.no>
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_MODULES_DEV_H
#define __CALF_MODULES_DEV_H
#include <calf/metadata.h>
#include <calf/modules.h>
#if ENABLE_EXPERIMENTAL
#include <fluidsynth.h>
#endif
namespace calf_plugins {
#if ENABLE_EXPERIMENTAL
/// Tiny wrapper for fluidsynth
class fluidsynth_audio_module: public audio_module<fluidsynth_metadata>
{
public:
float *ins[in_count];
float *outs[out_count];
float *params[param_count];
protected:
/// Current sample rate
uint32_t srate;
/// FluidSynth Settings object
fluid_settings_t *settings;
/// FluidSynth Synth object
fluid_synth_t *synth;
/// Soundfont filename
std::string soundfont;
/// Soundfont filename (as received from Fluidsynth)
std::string soundfont_name;
/// TAB-separated preset list (preset+128*bank TAB preset name LF)
std::string soundfont_preset_list;
/// FluidSynth assigned SoundFont ID
int sfid;
/// Map of preset+128*bank to preset name
std::map<uint32_t, std::string> sf_preset_names;
/// Last selected preset+128*bank
uint32_t last_selected_preset;
/// Serial number of status data
int status_serial;
/// Preset number to set on next process() call
volatile int set_preset;
/// Update last_selected_preset based on synth object state
void update_preset_num();
/// Create a fluidsynth object and load the current soundfont
fluid_synth_t *create_synth(int &new_sfid);
public:
/// Constructor to initialize handles to NULL
fluidsynth_audio_module();
void post_instantiate();
void set_sample_rate(uint32_t sr) { srate = sr; }
/// Handle MIDI Note On message (by sending it to fluidsynth)
void note_on(int note, int vel);
/// Handle MIDI Note Off message (by sending it to fluidsynth)
void note_off(int note, int vel);
/// Handle pitch bend message.
inline void pitch_bend(int value)
{
fluid_synth_pitch_bend(synth, 0, value + 0x2000);
}
/// Handle control change messages.
void control_change(int controller, int value);
/// Handle program change messages.
void program_change(int program);
/// Update variables from control ports.
void params_changed() {
}
void activate();
void deactivate();
/// No CV inputs for now
bool is_cv(int param_no) { return false; }
/// Practically all the stuff here is noisy... for now
bool is_noisy(int param_no) { return true; }
/// Main processing function
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
/// DSSI-style configure function for handling string port data
char *configure(const char *key, const char *value);
void send_configures(send_configure_iface *sci);
int send_status_updates(send_updates_iface *sui, int last_serial);
uint32_t message_run(const void *valid_inputs, void *output_ports) {
// silence a default printf (which is kind of a warning about unhandled message_run)
return 0;
}
~fluidsynth_audio_module();
};
#endif
};
#endif

View File

@@ -0,0 +1,202 @@
/* Calf DSP Library
* "Small" audio modules for modular synthesis
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_MODULES_SMALL_H
#define __CALF_MODULES_SMALL_H
#if USE_LV2
#include <lv2.h>
#include "plugininfo.h"
#include "lv2_polymorphic_port.h"
#include "lv2helpers.h"
namespace calf_plugins {
/// Empty implementations for plugin functions. Note, that some functions aren't virtual, because they're called via the particular
/// subclass via template wrappers (ladspa_small_wrapper<> etc), not via base class pointer/reference. On the other hand,
/// other functions are virtual when overhead is acceptable (instantiation time functions etc.)
class null_small_audio_module: public uri_map_access
{
public:
uint32_t srate;
double odsr;
uint32_t poly_type_control, poly_type_audio;
/// for polymorphic ports: "is audio" flags for first 32 ports (should be sufficient for most plugins)
uint32_t poly_port_types;
null_small_audio_module()
: srate((uint32_t)-1)
, odsr(0.)
, poly_type_control(0)
, poly_type_audio(0)
, poly_port_types(0)
{
}
/// Called when host changes type of the polymorphic port
inline void set_port_type(uint32_t port, uint32_t type, void *type_data) {
if (port >= 32)
return;
uint32_t port_mask = 1 << port;
if (type == poly_type_control)
poly_port_types &= ~port_mask;
else if (type == poly_type_audio)
poly_port_types |= port_mask;
on_port_types_changed();
}
/// Returns 1 for audio ports and 0 for control ports
inline unsigned int port_is_audio(unsigned int port) {
return (poly_port_types >> port) & 1;
}
/// Returns (unsigned)-1 for audio ports and 0 for control ports
inline unsigned int port_audio_mask(unsigned int port) {
return 0 - ((poly_port_types >> port) & 1);
}
/// Returns (unsigned)-1 for audio ports and 0 for control ports
static inline unsigned int port_audio_mask(unsigned int port, uint32_t poly_port_types) {
return 0 - ((poly_port_types >> port) & 1);
}
virtual void on_port_types_changed() {}
inline void set_bundle_path(const char *path) {}
/// Called to map all the necessary URIs
virtual void map_uris()
{
poly_type_control = map_uri(LV2_POLYMORPHIC_PORT_URI, "http://lv2plug.in/ns/lv2core#ControlPort");
poly_type_audio = map_uri(LV2_POLYMORPHIC_PORT_URI, "http://lv2plug.in/ns/lv2core#AudioPort");
}
/// Called on instantiation with the list of LV2 features called
virtual void use_features(const LV2_Feature *const *features) {
while(*features)
{
use_feature((*features)->URI, (*features)->data);
features++;
}
}
/// LADSPA-esque activate function, except it is called after ports are connected, not before
inline void activate() {}
/// LADSPA-esque deactivate function
inline void deactivate() {}
/// Set sample rate for the plugin
inline void set_sample_rate(uint32_t sr) { srate = sr; odsr = 1.0 / sr; }
static inline const void *ext_data(const char *URI) { return NULL; }
};
/// Templatized version useful when the number of inputs and outputs is small
template<unsigned int Inputs, unsigned int Outputs>
class small_audio_module_base: public null_small_audio_module
{
public:
enum { in_count = Inputs, out_count = Outputs };
/// Input pointers
float *ins[in_count];
/// Output pointers
float *outs[out_count];
};
template<class Module>
struct lv2_small_wrapper
{
typedef Module instance;
static LV2_Descriptor descriptor;
std::string uri;
static uint32_t poly_port_types;
lv2_small_wrapper(const char *id)
{
uri = "http://calf.sourceforge.net/small_plugins/" + std::string(id);
descriptor.URI = uri.c_str();
descriptor.instantiate = cb_instantiate;
descriptor.connect_port = cb_connect;
descriptor.activate = cb_activate;
descriptor.run = cb_run;
descriptor.deactivate = cb_deactivate;
descriptor.cleanup = cb_cleanup;
descriptor.extension_data = cb_ext_data;
plugin_port_type_grabber ptg(poly_port_types);
Module::plugin_info(&ptg);
}
static void cb_connect(LV2_Handle Instance, uint32_t port, void *DataLocation) {
unsigned long ins = Module::in_count;
unsigned long outs = Module::out_count;
instance *const mod = (instance *)Instance;
if (port < ins)
mod->ins[port] = (float *)DataLocation;
else if (port < ins + outs)
mod->outs[port - ins] = (float *)DataLocation;
}
static void cb_activate(LV2_Handle Instance) {
// Note the changed semantics (now more LV2-like)
instance *const mod = (instance *)Instance;
mod->activate();
}
static void cb_deactivate(LV2_Handle Instance) {
instance *const mod = (instance *)Instance;
mod->deactivate();
}
static uint32_t cb_set_type(LV2_Handle Instance, uint32_t port, uint32_t type, void *type_data)
{
instance *const mod = (instance *)Instance;
mod->set_port_type(port, type, type_data);
return 0;
}
static LV2_Handle cb_instantiate(const LV2_Descriptor * Descriptor, double sample_rate, const char *bundle_path, const LV2_Feature *const *features)
{
instance *mod = new instance();
mod->poly_port_types = poly_port_types;
// XXXKF some people use fractional sample rates; we respect them ;-)
mod->set_bundle_path(bundle_path);
mod->use_features(features);
mod->set_sample_rate((uint32_t)sample_rate);
return mod;
}
static void cb_run(LV2_Handle Instance, uint32_t SampleCount) {
instance *const mod = (instance *)Instance;
mod->process(SampleCount);
}
static void cb_cleanup(LV2_Handle Instance) {
instance *const mod = (instance *)Instance;
delete mod;
}
static const void *cb_ext_data(const char *URI) {
return Module::ext_data(URI);
}
};
extern const LV2_Descriptor *lv2_small_descriptor(uint32_t index);
};
#endif
#endif

View File

@@ -0,0 +1,264 @@
/* Calf DSP Library
* Audio modules - synthesizers
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_MODULES_SYNTHS_H
#define __CALF_MODULES_SYNTHS_H
#include <assert.h>
#include "biquad.h"
#include "onepole.h"
#include "audio_fx.h"
#include "inertia.h"
#include "osc.h"
#include "synth.h"
#include "envelope.h"
#include "organ.h"
#include "modmatrix.h"
namespace calf_plugins {
#define MONOSYNTH_WAVE_BITS 12
/// Monosynth-in-making. Parameters may change at any point, so don't make songs with it!
/// It lacks inertia for parameters, even for those that really need it.
class monosynth_audio_module: public audio_module<monosynth_metadata>, public line_graph_iface, public mod_matrix
{
public:
enum { mod_matrix_slots = 10 };
float *ins[in_count];
float *outs[out_count];
float *params[param_count];
uint32_t srate, crate;
static dsp::waveform_family<MONOSYNTH_WAVE_BITS> *waves;
dsp::waveform_oscillator<MONOSYNTH_WAVE_BITS> osc1, osc2;
dsp::triangle_lfo lfo;
bool running, stopping, gate, force_fadeout;
int last_key;
float buffer[step_size], buffer2[step_size];
uint32_t output_pos;
dsp::onepole<float> phaseshifter;
dsp::biquad_d1_lerp<float> filter;
dsp::biquad_d1_lerp<float> filter2;
/// Waveform number - OSC1
int wave1;
/// Waveform number - OSC2
int wave2;
/// Last used waveform number - OSC1
int prev_wave1;
/// Last used waveform number - OSC2
int prev_wave2;
int filter_type, last_filter_type;
float freq, start_freq, target_freq, cutoff, decay_factor, fgain, fgain_delta, separation;
float detune, xpose, xfade, ampctl, fltctl, queue_vel;
float odcr, porta_time, lfo_bend, lfo_clock, last_lfov, modwheel_value;
/// Last value of phase shift for pulse width emulation for OSC1
int32_t last_pwshift1;
/// Last value of phase shift for pulse width emulation for OSC2
int32_t last_pwshift2;
int queue_note_on, stop_count, modwheel_value_int;
int legato;
dsp::adsr envelope;
dsp::keystack stack;
dsp::gain_smoothing master;
/// Smoothed cutoff value
dsp::inertia<dsp::exponential_ramp> inertia_cutoff;
/// Smoothed pitch bend value
dsp::inertia<dsp::exponential_ramp> inertia_pitchbend;
/// Smoothed channel pressure value
dsp::inertia<dsp::linear_ramp> inertia_pressure;
/// Rows of the modulation matrix
dsp::modulation_entry mod_matrix_data[mod_matrix_slots];
/// Currently used velocity
float velocity;
/// Last value of oscillator mix ratio
float last_xfade;
/// Current calculated mod matrix outputs
float moddest[moddest_count];
monosynth_audio_module();
static void precalculate_waves(progress_report_iface *reporter);
void set_sample_rate(uint32_t sr);
void delayed_note_on();
/// Handle MIDI Note On message (does not immediately trigger a note, as it must start on
/// boundary of step_size samples).
void note_on(int note, int vel);
/// Handle MIDI Note Off message
void note_off(int note, int vel);
/// Handle MIDI Channel Pressure
void channel_pressure(int value);
/// Handle pitch bend message.
inline void pitch_bend(int value)
{
inertia_pitchbend.set_inertia(pow(2.0, (value * *params[par_pwhlrange]) / (1200.0 * 8192.0)));
}
/// Update oscillator frequency based on base frequency, detune amount, pitch bend scaling factor and sample rate.
inline void set_frequency()
{
float detune_scaled = (detune - 1); // * log(freq / 440);
if (*params[par_scaledetune] > 0)
detune_scaled *= pow(20.0 / freq, *params[par_scaledetune]);
float p1 = 1, p2 = 1;
if (moddest[moddest_o1detune] != 0)
p1 = pow(2.0, moddest[moddest_o1detune] * (1.0 / 1200.0));
if (moddest[moddest_o2detune] != 0)
p2 = pow(2.0, moddest[moddest_o2detune] * (1.0 / 1200.0));
osc1.set_freq(freq * (1 - detune_scaled) * p1 * inertia_pitchbend.get_last() * lfo_bend, srate);
osc2.set_freq(freq * (1 + detune_scaled) * p2 * inertia_pitchbend.get_last() * lfo_bend * xpose, srate);
}
/// Handle control change messages.
void control_change(int controller, int value);
/// Update variables from control ports.
void params_changed() {
float sf = 0.001f;
envelope.set(*params[par_attack] * sf, *params[par_decay] * sf, std::min(0.999f, *params[par_sustain]), *params[par_release] * sf, srate / step_size, *params[par_fade] * sf);
filter_type = dsp::fastf2i_drm(*params[par_filtertype]);
decay_factor = odcr * 1000.0 / *params[par_decay];
separation = pow(2.0, *params[par_cutoffsep] / 1200.0);
wave1 = dsp::clip(dsp::fastf2i_drm(*params[par_wave1]), 0, (int)wave_count - 1);
wave2 = dsp::clip(dsp::fastf2i_drm(*params[par_wave2]), 0, (int)wave_count - 1);
detune = pow(2.0, *params[par_detune] / 1200.0);
xpose = pow(2.0, *params[par_osc2xpose] / 12.0);
xfade = *params[par_oscmix];
legato = dsp::fastf2i_drm(*params[par_legato]);
master.set_inertia(*params[par_master]);
set_frequency();
if (wave1 != prev_wave1 || wave2 != prev_wave2)
lookup_waveforms();
}
void activate();
void deactivate();
void post_instantiate()
{
precalculate_waves(progress_report);
}
/// Set waveform addresses for oscillators
void lookup_waveforms();
/// Run oscillators
void calculate_buffer_oscs(float lfo);
/// Run two filters in series to produce mono output samples.
void calculate_buffer_ser();
/// Run one filter to produce mono output samples.
void calculate_buffer_single();
/// Run two filters (one per channel) to produce stereo output samples.
void calculate_buffer_stereo();
/// Retrieve filter graph (which is 'live' so it cannot be generated by get_static_graph), or fall back to get_static_graph.
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
/// @retval true if the filter 1 is to be used for the left channel and filter 2 for the right channel
/// @retval false if filters are to be connected in series and sent (mono) to both channels
inline bool is_stereo_filter() const
{
return filter_type == flt_2lp12 || filter_type == flt_2bp6;
}
/// No CV inputs for now
bool is_cv(int param_no) { return false; }
/// Practically all the stuff here is noisy
bool is_noisy(int param_no) { return param_no != par_cutoff; }
/// Calculate control signals and produce step_size samples of output.
void calculate_step();
/// Main processing function
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
};
struct organ_audio_module: public audio_module<organ_metadata>, public dsp::drawbar_organ, public line_graph_iface
{
public:
using drawbar_organ::note_on;
using drawbar_organ::note_off;
using drawbar_organ::control_change;
enum { param_count = drawbar_organ::param_count};
float *ins[in_count];
float *outs[out_count];
float *params[param_count];
dsp::organ_parameters par_values;
uint32_t srate;
bool panic_flag;
/// Value for configure variable map_curve
std::string var_map_curve;
organ_audio_module()
: drawbar_organ(&par_values)
{
var_map_curve = "2\n0 1\n1 1\n"; // XXXKF hacky bugfix
}
void post_instantiate()
{
dsp::organ_voice_base::precalculate_waves(progress_report);
}
void set_sample_rate(uint32_t sr) {
srate = sr;
}
void params_changed() {
for (int i = 0; i < param_count - var_count; i++)
((float *)&par_values)[i] = *params[i];
unsigned int old_poly = polyphony_limit;
polyphony_limit = dsp::clip(dsp::fastf2i_drm(*params[par_polyphony]), 1, 32);
if (polyphony_limit < old_poly)
trim_voices();
update_params();
}
inline void pitch_bend(int amt)
{
drawbar_organ::pitch_bend(amt);
}
void activate() {
setup(srate);
panic_flag = false;
}
void deactivate();
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
float *o[2] = { outs[0] + offset, outs[1] + offset };
if (panic_flag)
{
control_change(120, 0); // stop all sounds
control_change(121, 0); // reset all controllers
panic_flag = false;
}
render_separate(o, nsamples);
return 3;
}
/// No CV inputs for now
bool is_cv(int param_no) { return false; }
/// Practically all the stuff here is noisy
bool is_noisy(int param_no) { return true; }
void execute(int cmd_no);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
char *configure(const char *key, const char *value);
void send_configures(send_configure_iface *);
uint32_t message_run(const void *valid_inputs, void *output_ports) {
// silence a default printf (which is kind of a warning about unhandled message_run)
return 0;
}
};
};
#if ENABLE_EXPERIMENTAL
#include "wavetable.h"
#endif
#endif

View File

@@ -0,0 +1,213 @@
/* Calf DSP Library
* Multitap chorus class.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_MULTICHORUS_H
#define __CALF_MULTICHORUS_H
#include "audio_fx.h"
namespace dsp {
typedef fixed_point<unsigned int, 20> chorus_phase;
template<class T, uint32_t Voices>
class sine_multi_lfo
{
protected:
sine_table<int, 4096, 65535> sine;
public:
/// Current LFO phase
chorus_phase phase;
/// LFO phase increment
chorus_phase dphase;
/// LFO phase per-voice increment
chorus_phase vphase;
/// Current number of voices
uint32_t voices;
/// Current scale (output multiplier)
T scale;
/// Per-voice offset unit (the value that says how much the voices are offset with respect to each other in non-100% 'overlap' mode), scaled so that full range = 131072
int32_t voice_offset;
/// LFO Range scaling for non-100% overlap
uint32_t voice_depth;
public:
sine_multi_lfo()
{
phase = dphase = vphase = 0.0;
voice_offset = 0;
voice_depth = 1U << 31;
set_voices(Voices);
}
inline uint32_t get_voices() const
{
return voices;
}
inline void set_voices(uint32_t value)
{
voices = value;
// use sqrt, because some phases will cancel each other - so 1 / N is usually too low
scale = sqrt(1.0 / voices);
}
inline void set_overlap(float overlap)
{
// If we scale the delay amount so that full range of a single LFO is 0..1, all the overlapped LFOs will cover 0..range
// How it's calculated:
// 1. First voice is assumed to always cover the range of 0..1
// 2. Each remaining voice contributes an interval of a width = 1 - overlap, starting from the end of the interval of the previous voice
// Coverage = non-overlapped part of the LFO range in the 1st voice
float range = 1.f + (1.f - overlap) * (voices - 1);
float scaling = 1.f / range;
voice_offset = (int)(131072 * (1 - overlap) / range);
voice_depth = (unsigned int)((1U << 30) * 1.0 * scaling);
}
/// Get LFO value for given voice, returns a values in range of [-65536, 65535] (or close)
inline int get_value(uint32_t voice) {
// find this voice's phase (= phase + voice * 360 degrees / number of voices)
chorus_phase voice_phase = phase + vphase * (int)voice;
// find table offset
unsigned int ipart = voice_phase.ipart();
// interpolate (use 14 bits of precision - because the table itself uses 17 bits and the result of multiplication must fit in int32_t)
// note, the result is still -65535 .. 65535, it's just interpolated
// it is never reaching -65536 - but that's acceptable
int intval = voice_phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]);
// apply the voice offset/depth (rescale from -65535..65535 to appropriate voice's "band")
return -65535 + voice * voice_offset + ((voice_depth >> (30-13)) * (65536 + intval) >> 13);
}
inline void step() {
phase += dphase;
}
inline T get_scale() const {
return scale;
}
void reset() {
phase = 0.f;
}
};
/**
* Multi-tap chorus without feedback.
* Perhaps MaxDelay should be a bit longer!
*/
template<class T, class MultiLfo, class Postprocessor, int MaxDelay=4096>
class multichorus: public chorus_base
{
protected:
simple_delay<MaxDelay,T> delay;
public:
MultiLfo lfo;
Postprocessor post;
public:
multichorus() {
rate = 0.63f;
dry = 0.5f;
wet = 0.5f;
min_delay = 0.005f;
mod_depth = 0.0025f;
setup(44100);
}
void reset() {
delay.reset();
lfo.reset();
}
void set_rate(float rate) {
chorus_base::set_rate(rate);
lfo.dphase = dphase;
}
virtual void setup(int sample_rate) {
modulation_effect::setup(sample_rate);
delay.reset();
lfo.reset();
set_min_delay(get_min_delay());
set_mod_depth(get_mod_depth());
}
template<class OutIter, class InIter>
void process(OutIter buf_out, InIter buf_in, int nsamples) {
int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
int mdepth = mod_depth_samples;
// 1 sample peak-to-peak = mod_depth_samples of 32 (this scaling stuff is tricky and may - but shouldn't - be wrong)
// with 192 kHz sample rate, 1 ms = 192 samples, and the maximum 20 ms = 3840 samples (so, 4096 will be used)
// 3840 samples of mod depth = mdepth of 122880 (which multiplied by 65536 doesn't fit in int32_t)
// so, it will be right-shifted by 2, which gives it a safe range of 30720
// NB: calculation of mod_depth_samples (and multiply-by-32) is in chorus_base::set_mod_depth
mdepth = mdepth >> 2;
T scale = lfo.get_scale();
for (int i=0; i<nsamples; i++) {
phase += dphase;
float in = *buf_in++;
delay.put(in);
unsigned int nvoices = lfo.get_voices();
T out = 0.f;
// add up values from all voices, each voice tell its LFO phase and the buffer value is picked at that location
for (unsigned int v = 0; v < nvoices; v++)
{
int lfo_output = lfo.get_value(v);
// 3 = log2(32 >> 2) + 1 because the LFO value is in range of [-65535, 65535] (17 bits)
int dv = mds + (mdepth * lfo_output >> (3 + 1));
int ifv = dv >> 16;
T fd; // signal from delay's output
delay.get_interp(fd, ifv, (dv & 0xFFFF)*(1.0/65536.0));
out += fd;
}
// apply the post filter
out = post.process(out);
T sdry = in * gs_dry.get();
T swet = out * gs_wet.get() * scale;
*buf_out++ = sdry + swet;
lfo.step();
}
post.sanitize();
}
float freq_gain(float freq, float sr)
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
cfloat h = 0.0;
int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
int mdepth = mod_depth_samples;
mdepth = mdepth >> 2;
T scale = lfo.get_scale();
unsigned int nvoices = lfo.get_voices();
for (unsigned int v = 0; v < nvoices; v++)
{
int lfo_output = lfo.get_value(v);
// 3 = log2(32 >> 2) + 1 because the LFO value is in range of [-65535, 65535] (17 bits)
int dv = mds + (mdepth * lfo_output >> (3 + 1));
int fldp = dv >> 16;
cfloat zn = std::pow(z, fldp); // z^-N
h += zn + (zn * z - zn) * cfloat(dv / 65536.0 - fldp);
}
// apply the post filter
h *= post.h_z(z);
// mix with dry signal
float v = std::abs(cfloat(gs_dry.get_last()) + cfloat(scale * gs_wet.get_last()) * h);
return v;
}
};
};
#endif

View File

@@ -0,0 +1,192 @@
/* Calf DSP Library
* Basic one-pole one-zero filters based on bilinear transform.
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_ONEPOLE_H
#define __CALF_ONEPOLE_H
#include "primitives.h"
namespace dsp {
/**
* one-pole filter, for floating point values
* coefficient calculation is based on bilinear transform, and the code itself is based on my very old OneSignal lib
* lp and hp are *somewhat* tested, allpass is not tested at all
* don't use this for integers because it won't work
*/
template<class T = float, class Coeff = float>
class onepole
{
public:
typedef std::complex<double> cfloat;
T x1, y1;
Coeff a0, a1, b1;
onepole()
{
reset();
}
/// Set coefficients for a lowpass filter
void set_lp(float fc, float sr)
{
// x x
// x+1 x-1
Coeff x = tan (M_PI * fc / (2 * sr));
Coeff q = 1/(1+x);
a0 = a1 = x*q;
b1 = (x-1)*q;
}
/// Set coefficients for an allpass filter
void set_ap(float fc, float sr)
{
// x-1 x+1
// x+1 x-1
Coeff x = tan (M_PI * fc / (2 * sr));
Coeff q = 1/(1+x);
b1 = a0 = (x-1)*q;
a1 = 1;
}
/// Set coefficients for an allpass filter, using omega instead of fc and sr
/// omega = (PI / 2) * fc / sr
void set_ap_w(float w)
{
// x-1 x+1
// x+1 x-1
Coeff x = tan (w);
Coeff q = 1/(1+x);
b1 = a0 = (x-1)*q;
a1 = 1;
}
/// Set coefficients for a highpass filter
void set_hp(float fc, float sr)
{
// x -x
// x+1 x-1
Coeff x = tan (M_PI * fc / (2 * sr));
Coeff q = 1/(1+x);
a0 = q;
a1 = -a0;
b1 = (x-1)*q;
}
/// Process one sample
inline T process(T in)
{
T out = in * a0 + x1 * a1 - y1 * b1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample, assuming it's a lowpass filter (optimized special case)
inline T process_lp(T in)
{
T out = (in + x1) * a0 - y1 * b1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample, assuming it's a highpass filter (optimized special case)
inline T process_hp(T in)
{
T out = (in - x1) * a0 - y1 * b1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample, assuming it's an allpass filter (optimized special case)
inline T process_ap(T in)
{
T out = (in - y1) * a0 + x1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample using external state variables
inline T process_ap(T in, float &x1, float &y1)
{
T out = (in - y1) * a0 + x1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample using external state variables, including filter coeff
inline T process_ap(T in, float &x1, float &y1, float a0)
{
T out = (in - y1) * a0 + x1;
x1 = in;
y1 = out;
return out;
}
inline bool empty() {
return y1 == 0;
}
inline void sanitize()
{
dsp::sanitize(x1);
dsp::sanitize(y1);
}
inline void reset()
{
dsp::zero(x1);
dsp::zero(y1);
}
template<class U>
inline void copy_coeffs(const onepole<U> &src)
{
a0 = src.a0;
a1 = src.a1;
b1 = src.b1;
}
/// Return the filter's gain at frequency freq
/// @param freq Frequency to look up
/// @param sr Filter sample rate (used to convert frequency to angular frequency)
float freq_gain(float freq, float sr)
{
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq));
return std::abs(h_z(z));
}
/// Return H(z) the filter's gain at frequency freq
/// @param z Z variable (e^jw)
cfloat h_z(const cfloat &z)
{
return (cfloat(a0) + double(a1) * z) / (cfloat(1.0) + double(b1) * z);
}
};
};
#endif

View File

@@ -0,0 +1,374 @@
/* Calf DSP Library
* Drawbar organ emulator.
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_ORGAN_H
#define __CALF_ORGAN_H
#include "synth.h"
#include "envelope.h"
#include "metadata.h"
#define ORGAN_KEYTRACK_POINTS 4
namespace dsp
{
struct organ_parameters {
enum { FilterCount = 2, EnvCount = 3 };
struct organ_filter_parameters
{
float cutoff;
float resonance;
float envmod[organ_parameters::EnvCount];
float keyf;
};
struct organ_env_parameters
{
float attack, decay, sustain, release, velscale, ampctl;
};
//////////////////////////////////////////////////////////////////////////
// these parameters are binary-copied from control ports (order is important!)
float drawbars[9];
float harmonics[9];
float waveforms[9];
float detune[9];
float phase[9];
float pan[9];
float routing[9];
float foldover;
float percussion_time;
float percussion_level;
float percussion_wave;
float percussion_harmonic;
float percussion_vel2amp;
float percussion_fm_time;
float percussion_fm_depth;
float percussion_fm_wave;
float percussion_fm_harmonic;
float percussion_vel2fm;
float percussion_trigger;
float percussion_stereo;
float filter_chain;
float filter1_type;
float master;
organ_filter_parameters filters[organ_parameters::FilterCount];
organ_env_parameters envs[organ_parameters::EnvCount];
float lfo_rate;
float lfo_amt;
float lfo_wet;
float lfo_phase;
float lfo_mode;
float global_transpose;
float global_detune;
float polyphony;
float quad_env;
float pitch_bend_range;
float bass_freq;
float bass_gain;
float treble_freq;
float treble_gain;
float dummy_mapcurve;
//////////////////////////////////////////////////////////////////////////
// these parameters are calculated
double perc_decay_const, perc_fm_decay_const;
float multiplier[9];
int phaseshift[9];
float cutoff;
unsigned int foldvalue;
float pitch_bend;
float percussion_keytrack[ORGAN_KEYTRACK_POINTS][2];
organ_parameters() : pitch_bend(1.0f) {}
inline int get_percussion_wave() { return dsp::fastf2i_drm(percussion_wave); }
inline int get_percussion_fm_wave() { return dsp::fastf2i_drm(percussion_fm_wave); }
};
#define ORGAN_WAVE_BITS 12
#define ORGAN_WAVE_SIZE 4096
#define ORGAN_BIG_WAVE_BITS 17
#define ORGAN_BIG_WAVE_SIZE 131072
/// 2^ORGAN_BIG_WAVE_SHIFT = how many (quasi)periods per sample
#define ORGAN_BIG_WAVE_SHIFT 5
class organ_voice_base: public calf_plugins::organ_enums
{
public:
typedef waveform_family<ORGAN_WAVE_BITS> small_wave_family;
typedef waveform_family<ORGAN_BIG_WAVE_BITS> big_wave_family;
public:
organ_parameters *parameters;
protected:
static small_wave_family (*waves)[wave_count_small];
static big_wave_family (*big_waves)[wave_count_big];
// dsp::sine_table<float, ORGAN_WAVE_SIZE, 1> sine_wave;
int note;
dsp::decay amp;
/// percussion FM carrier amplitude envelope
dsp::decay pamp;
/// percussion FM modulator amplitude envelope
dsp::decay fm_amp;
dsp::fixed_point<int64_t, 20> pphase, dpphase;
dsp::fixed_point<int64_t, 20> modphase, moddphase;
float fm_keytrack;
int &sample_rate_ref;
bool &released_ref;
/// pamp per-sample (linear) step during release stage (calculated on release so that it will take 30ms for it to go from "current value at release point" to 0)
float rel_age_const;
organ_voice_base(organ_parameters *_parameters, int &_sample_rate_ref, bool &_released_ref);
inline float wave(float *data, dsp::fixed_point<int, 20> ph) {
return ph.lerp_table_lookup_float(data);
}
inline float big_wave(float *data, dsp::fixed_point<int64_t, 20> &ph) {
// wrap to fit within the wave
return ph.lerp_table_lookup_float_mask(data, ORGAN_BIG_WAVE_SIZE - 1);
}
public:
static inline small_wave_family &get_wave(int wave) {
return (*waves)[wave];
}
static inline big_wave_family &get_big_wave(int wave) {
return (*big_waves)[wave];
}
static void precalculate_waves(calf_plugins::progress_report_iface *reporter);
void update_pitch()
{
float phase = dsp::midi_note_to_phase(note, 100 * parameters->global_transpose + parameters->global_detune, sample_rate_ref);
dpphase.set((long int) (phase * parameters->percussion_harmonic * parameters->pitch_bend));
moddphase.set((long int) (phase * parameters->percussion_fm_harmonic * parameters->pitch_bend));
}
// this doesn't really have a voice interface
void render_percussion_to(float (*buf)[2], int nsamples);
void perc_note_on(int note, int vel);
void perc_note_off(int note, int vel);
void perc_reset()
{
pphase = 0;
modphase = 0;
dpphase = 0;
moddphase = 0;
note = -1;
}
};
class organ_vibrato
{
protected:
enum { VibratoSize = 6 };
float vibrato_x1[VibratoSize][2], vibrato_y1[VibratoSize][2];
float lfo_phase;
dsp::onepole<float> vibrato[2];
public:
void reset();
void process(organ_parameters *parameters, float (*data)[2], unsigned int len, float sample_rate);
};
class organ_voice: public dsp::voice, public organ_voice_base {
protected:
enum { Channels = 2, BlockSize = 64, EnvCount = organ_parameters::EnvCount, FilterCount = organ_parameters::FilterCount };
union {
float output_buffer[BlockSize][Channels];
float aux_buffers[3][BlockSize][Channels];
};
dsp::fixed_point<int64_t, 52> phase, dphase;
dsp::biquad_d1<float> filterL[2], filterR[2];
adsr envs[EnvCount];
dsp::inertia<dsp::linear_ramp> expression;
organ_vibrato vibrato;
float velocity;
bool perc_released;
/// The envelopes have ended and the voice is in final fadeout stage
bool finishing;
dsp::inertia<dsp::exponential_ramp> inertia_pitchbend;
public:
organ_voice()
: organ_voice_base(NULL, sample_rate, perc_released)
, expression(dsp::linear_ramp(16))
, inertia_pitchbend(dsp::exponential_ramp(1))
{
inertia_pitchbend.set_now(1);
}
void reset() {
inertia_pitchbend.ramp.set_length(sample_rate / (BlockSize * 30)); // 1/30s
vibrato.reset();
phase = 0;
for (int i = 0; i < FilterCount; i++)
{
filterL[i].reset();
filterR[i].reset();
}
}
void note_on(int note, int vel) {
stolen = false;
finishing = false;
perc_released = false;
released = false;
reset();
this->note = note;
const float sf = 0.001f;
for (int i = 0; i < EnvCount; i++)
{
organ_parameters::organ_env_parameters &p = parameters->envs[i];
envs[i].set(sf * p.attack, sf * p.decay, p.sustain, sf * p.release, sample_rate / BlockSize);
envs[i].note_on();
}
update_pitch();
velocity = vel * 1.0 / 127.0;
amp.set(1.0f);
perc_note_on(note, vel);
}
void note_off(int /* vel */) {
// reset age to 0 (because decay will turn from exponential to linear, necessary because of error cumulation prevention)
perc_released = true;
if (pamp.get_active())
{
pamp.reinit();
}
rel_age_const = pamp.get() * ((1.0/44100.0)/0.03);
for (int i = 0; i < EnvCount; i++)
envs[i].note_off();
}
virtual float get_priority() { return stolen ? 20000 : (perc_released ? 1 : (sostenuto ? 200 : 100)); }
virtual void steal() {
perc_released = true;
finishing = true;
stolen = true;
}
void render_block();
virtual int get_current_note() {
return note;
}
virtual bool get_active() {
// printf("note %d getactive %d use_percussion %d pamp active %d\n", note, amp.get_active(), use_percussion(), pamp.get_active());
return (note != -1) && (amp.get_active() || (use_percussion() && pamp.get_active()));
}
void update_pitch();
inline bool use_percussion()
{
return dsp::fastf2i_drm(parameters->percussion_trigger) == perctrig_polyphonic && parameters->percussion_level > 0;
}
};
/// Not a true voice, just something with similar-ish interface.
class percussion_voice: public organ_voice_base {
public:
int sample_rate;
bool released;
percussion_voice(organ_parameters *_parameters)
: organ_voice_base(_parameters, sample_rate, released)
, released(false)
{
}
bool get_active() {
return (note != -1) && pamp.get_active();
}
bool get_noticable() {
return (note != -1) && (pamp.get() > 0.2 * parameters->percussion_level);
}
void setup(int sr) {
sample_rate = sr;
}
};
struct drawbar_organ: public dsp::basic_synth, public calf_plugins::organ_enums {
organ_parameters *parameters;
percussion_voice percussion;
organ_vibrato global_vibrato;
two_band_eq eq_l, eq_r;
drawbar_organ(organ_parameters *_parameters)
: parameters(_parameters)
, percussion(_parameters) {
}
void render_separate(float *output[], int nsamples);
dsp::voice *alloc_voice() {
block_voice<organ_voice> *v = new block_voice<organ_voice>();
v->parameters = parameters;
return v;
}
virtual void percussion_note_on(int note, int vel) {
percussion.perc_note_on(note, vel);
}
virtual void params_changed() = 0;
virtual void setup(int sr) {
basic_synth::setup(sr);
percussion.setup(sr);
parameters->cutoff = 0;
params_changed();
global_vibrato.reset();
}
void update_params();
void control_change(int controller, int value)
{
#if 0
if (controller == 11)
{
parameters->cutoff = value / 64.0 - 1;
}
#endif
dsp::basic_synth::control_change(controller, value);
}
void pitch_bend(int amt);
virtual bool check_percussion() {
switch(dsp::fastf2i_drm(parameters->percussion_trigger))
{
case organ_voice_base::perctrig_first:
return active_voices.empty();
case organ_voice_base::perctrig_each:
default:
return true;
case organ_voice_base::perctrig_eachplus:
return !percussion.get_noticable();
case organ_voice_base::perctrig_polyphonic:
return false;
}
}
};
};
#endif

View File

@@ -0,0 +1,306 @@
/* Calf DSP Library
* Oscillators
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_OSC_H
#define __CALF_OSC_H
#include "fft.h"
#include <map>
namespace dsp
{
/** Very simple, non-bandlimited saw oscillator. Should not be used for anything
* else than testing/prototyping. Unless get() function is replaced with something
* with "proper" oscillator code, as the frequency setting function is fine.
*/
struct simple_oscillator
{
/// Phase (from 0 to 0xFFFFFFFF)
uint32_t phase;
/// Per-sample phase delta (phase increment), equal to 2^32*freq/sr.
uint32_t phasedelta;
/// Reset oscillator phase to zero.
void reset()
{
phase = 0;
}
/// Set phase delta based on oscillator frequency and sample rate.
void set_freq(float freq, float sr)
{
phasedelta = (int)(freq * 65536.0 * 256.0 * 16.0 / sr) << 4;
}
/// Set phase delta based on oscillator frequency and inverse of sample rate.
void set_freq_odsr(float freq, double odsr)
{
phasedelta = (int)(freq * 65536.0 * 256.0 * 16.0 * odsr) << 4;
}
inline float get()
{
float value = (phase >> 16 ) / 65535.0 - 0.5;
phase += phasedelta;
return value;
}
};
/**
* FFT-based bandlimiting helper class. Allows conversion between time and frequency domains and generating brickwall filtered
* versions of a waveform given a pre-computed spectrum.
* Waveform size must be a power of two, and template argument SIZE_BITS is log2 of waveform size.
*/
template<int SIZE_BITS>
struct bandlimiter
{
enum { SIZE = 1 << SIZE_BITS };
static dsp::fft<float, SIZE_BITS> &get_fft()
{
static dsp::fft<float, SIZE_BITS> fft;
return fft;
}
std::complex<float> spectrum[SIZE];
/// Import time domain waveform and calculate spectrum from it
void compute_spectrum(float input[SIZE])
{
dsp::fft<float, SIZE_BITS> &fft = get_fft();
std::complex<float> *data = new std::complex<float>[SIZE];
for (int i = 0; i < SIZE; i++)
data[i] = input[i];
fft.calculate(data, spectrum, false);
delete []data;
}
/// Generate the waveform from the contained spectrum.
void compute_waveform(float output[SIZE])
{
dsp::fft<float, SIZE_BITS> &fft = get_fft();
std::complex<float> *data = new std::complex<float>[SIZE];
fft.calculate(spectrum, data, true);
for (int i = 0; i < SIZE; i++)
output[i] = data[i].real();
delete []data;
}
/// remove DC offset of the spectrum (it usually does more harm than good!)
void remove_dc()
{
spectrum[0] = 0.f;
}
/// Very basic bandlimiting (brickwall filter)
/// might need to be improved much in future!
void make_waveform(float output[SIZE], int cutoff, bool foldover = false)
{
dsp::fft<float, SIZE_BITS> &fft = get_fft();
std::vector<std::complex<float> > new_spec, iffted;
new_spec.resize(SIZE);
iffted.resize(SIZE);
// Copy original harmonics up to cutoff point
new_spec[0] = spectrum[0];
for (int i = 1; i < cutoff; i++)
new_spec[i] = spectrum[i],
new_spec[SIZE - i] = spectrum[SIZE - i];
// Fill the rest with zeros, optionally folding over harmonics over the
// cutoff point into the lower octaves while halving the amplitude.
// (I think it is almost nice for bell type waveforms when the original
// waveform has few widely spread harmonics)
if (foldover)
{
std::complex<float> fatt(0.5);
cutoff /= 2;
if (cutoff < 2)
cutoff = 2;
for (int i = SIZE / 2; i >= cutoff; i--)
{
new_spec[i / 2] += new_spec[i] * fatt;
new_spec[SIZE - i / 2] += new_spec[SIZE - i] * fatt;
new_spec[i] = 0.f,
new_spec[SIZE - i] = 0.f;
}
}
else
{
if (cutoff < 1)
cutoff = 1;
for (int i = cutoff; i < SIZE / 2; i++)
new_spec[i] = 0.f,
new_spec[SIZE - i] = 0.f;
}
// convert back to time domain (IFFT) and extract only real part
fft.calculate(new_spec.data(), iffted.data(), true);
for (int i = 0; i < SIZE; i++)
output[i] = iffted[i].real();
}
};
/// Set of bandlimited wavetables
template<int SIZE_BITS>
struct waveform_family: public std::map<uint32_t, float *>
{
enum { SIZE = 1 << SIZE_BITS };
using std::map<uint32_t, float *>::iterator;
using std::map<uint32_t, float *>::end;
using std::map<uint32_t, float *>::lower_bound;
float original[SIZE];
/// Fill the family using specified bandlimiter and original waveform. Optionally apply foldover.
/// Does not produce harmonics over specified limit (limit = (SIZE / 2) / min_number_of_harmonics)
void make(bandlimiter<SIZE_BITS> &bl, float input[SIZE], bool foldover = false, uint32_t limit = SIZE / 2)
{
memcpy(original, input, sizeof(original));
bl.compute_spectrum(input);
make_from_spectrum(bl, foldover);
}
/// Fill the family using specified bandlimiter and spectrum contained within. Optionally apply foldover.
/// Does not produce harmonics over specified limit (limit = (SIZE / 2) / min_number_of_harmonics)
void make_from_spectrum(bandlimiter<SIZE_BITS> &bl, bool foldover = false, uint32_t limit = SIZE / 2)
{
bl.remove_dc();
uint32_t base = 1 << (32 - SIZE_BITS);
uint32_t cutoff = SIZE / 2, top = SIZE / 2;
float vmax = 0;
for (unsigned int i = 0; i < cutoff; i++)
vmax = std::max(vmax, abs(bl.spectrum[i]));
float vthres = vmax / 1024.0; // -60dB
float cumul = 0.f;
while(cutoff > (SIZE / limit)) {
if (!foldover)
{
// skip harmonics too quiet to be heard, but measure their loudness cumulatively,
// because even if a single harmonic is too quiet, a whole bunch of them may add up
// to considerable amount of space
cumul = 0.f;
while(cutoff > 1 && cumul + abs(bl.spectrum[cutoff - 1]) < vthres)
{
cumul += abs(bl.spectrum[cutoff - 1]);
cutoff--;
}
}
float *wf = new float[SIZE+1];
bl.make_waveform(wf, cutoff, foldover);
wf[SIZE] = wf[0];
(*this)[base * (top / cutoff)] = wf;
cutoff = (int)(0.75 * cutoff);
}
}
/// Retrieve waveform pointer suitable for specified phase_delta
inline float *get_level(uint32_t phase_delta)
{
iterator i = upper_bound(phase_delta);
if (i == end())
return NULL;
// printf("Level = %08x\n", i->first);
return i->second;
}
/// Destructor, deletes the waveforms and removes them from the map.
~waveform_family()
{
for (iterator i = begin(); i != end(); i++)
delete []i->second;
clear();
}
};
#if 0
// cubic interpolation
static inline float cerp(float pm1, float p0, float p1, float p2, float t)
{
return (-t*(t-1)*(t-2) * pm1 + 3*(t+1)*(t-1)*(t-2) * p0 - 3*(t+1)*t*(t-2) * p1 + (t+1)*t*(t-1) * p2) * (1.0 / 6.0);
}
#endif
/**
* Simple table-based lerping oscillator. Uses waveform of size 2^SIZE_BITS.
* Combine with waveform_family if bandlimited waveforms are needed. Because
* of linear interpolation, it's usually a good idea to use large tables
* (2048-4096 points), otherwise aliasing may be produced.
*/
template<int SIZE_BITS>
struct waveform_oscillator: public simple_oscillator
{
enum { SIZE = 1 << SIZE_BITS, MASK = SIZE - 1, SCALE = 1 << (32 - SIZE_BITS) };
float *waveform;
inline float get()
{
uint32_t wpos = phase >> (32 - SIZE_BITS);
float value = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], (phase & (SCALE - 1)) * (1.0f / SCALE));
phase += phasedelta;
return value;
}
/// Add/substract two phase-shifted values
inline float get_phaseshifted(uint32_t shift, float mix)
{
uint32_t wpos = phase >> (32 - SIZE_BITS);
float value1 = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], (phase & (SCALE - 1)) * (1.0f / SCALE));
wpos = (phase + shift) >> (32 - SIZE_BITS);
float value2 = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], ((phase + shift) & (SCALE - 1)) * (1.0f / SCALE));
float value = value1 + mix * value2;
phase += phasedelta;
return value;
}
};
/**
* Simple triangle LFO without any smoothing or anything of this sort.
*/
struct triangle_lfo: public simple_oscillator
{
inline float get()
{
uint32_t phase2 = phase;
// start at 90 degrees point of the "/\" wave (-1 to +1)
phase2 += 1<<30;
// if in second half, invert the wave (so it falls back into 0..0x7FFFFFFF)
phase2 ^= ((int32_t)phase2)>>31;
float value = (phase2 >> 6) / 16777216.0 - 1.0;
phase += phasedelta;
return value;
}
};
/// Simple stupid inline function to normalize a waveform (by removing DC offset and ensuring max absolute value of 1).
static inline void normalize_waveform(float *table, unsigned int size)
{
float dc = 0;
for (unsigned int i = 0; i < size; i++)
dc += table[i];
dc /= size;
for (unsigned int i = 0; i < size; i++)
table[i] -= dc;
float thismax = 0;
for (unsigned int i = 0; i < size; i++)
thismax = std::max(thismax, fabsf(table[i]));
if (thismax < 0.000001f)
return;
double divv = 1.0 / thismax;
for (unsigned int i = 0; i < size; i++)
table[i] *= divv;
}
};
#endif

View File

@@ -0,0 +1,502 @@
/* Calf DSP Library
* Open Sound Control primitives
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_OSCTL_H
#define __CALF_OSCTL_H
#include <string>
#include <vector>
#include <string.h>
#include <errno.h>
#include <iostream>
#include <stdint.h>
namespace osctl
{
enum osc_type
{
osc_i32 = 'i',
osc_f32 = 'f',
osc_string = 's',
osc_blob = 'b',
// unsupported
osc_i64 = 'h',
osc_ts = 't',
osc_f64 = 'd',
osc_string_alt = 'S',
osc_char = 'c',
osc_rgba = 'r',
osc_midi = 'm',
osc_true = 'T',
osc_false = 'F',
osc_nil = 'N',
osc_inf = 'I',
osc_start_array = '[',
osc_end_array = ']'
};
extern const char *osc_type_name(osc_type type);
struct osc_exception: public std::exception
{
virtual const char *what() const throw() { return "OSC parsing error"; }
};
struct osc_read_exception: public std::exception
{
virtual const char *what() const throw() { return "OSC buffer underflow"; }
};
struct osc_write_exception: public std::exception
{
virtual const char *what() const throw() { return "OSC buffer overflow"; }
};
struct null_buffer
{
static bool read(uint8_t *dest, uint32_t bytes)
{
return false;
}
static bool write(uint8_t *dest, uint32_t bytes)
{
return true;
}
static void clear()
{
}
};
struct raw_buffer
{
uint8_t *ptr;
uint32_t pos, count, size;
raw_buffer()
{
ptr = NULL;
pos = count = size = 0;
}
raw_buffer(uint8_t *_ptr, uint32_t _count, uint32_t _size)
{
set(_ptr, _count, _size);
}
inline void set(uint8_t *_ptr, uint32_t _count, uint32_t _size)
{
ptr = _ptr;
pos = 0;
count = _count;
size = _size;
}
bool read(uint8_t *dest, uint32_t bytes)
{
if (pos + bytes > count)
return false;
memcpy(dest, ptr + pos, bytes);
pos += bytes;
return true;
}
bool write(const uint8_t *src, uint32_t bytes)
{
if (count + bytes > size)
return false;
memcpy(ptr + count, src, bytes);
count += bytes;
return true;
}
int read_left()
{
return count - pos;
}
int write_left()
{
return size - count;
}
inline int write_misalignment()
{
return 4 - (count & 3);
}
void clear()
{
pos = 0;
count = 0;
}
int tell()
{
return pos;
}
void seek(int _pos)
{
pos = _pos;
}
};
struct string_buffer
{
std::string data;
uint32_t pos, size;
string_buffer()
{
pos = 0;
size = 1048576;
}
string_buffer(std::string _data, int _size = 1048576)
{
data = _data;
pos = 0;
size = _size;
}
bool read(uint8_t *dest, uint32_t bytes)
{
if (pos + bytes > data.length())
return false;
memcpy(dest, &data[pos], bytes);
pos += bytes;
return true;
}
bool write(const uint8_t *src, uint32_t bytes)
{
if (data.length() + bytes > size)
return false;
uint32_t wpos = data.length();
data.resize(wpos + bytes);
memcpy(&data[wpos], src, bytes);
return true;
}
inline int read_left()
{
return data.length() - pos;
}
inline int write_left()
{
return size - data.length();
}
inline int write_misalignment()
{
return 4 - (data.length() & 3);
}
void clear()
{
data.clear();
pos = 0;
}
int tell()
{
return pos;
}
void seek(int _pos)
{
pos = _pos;
}
};
template<class Buffer, class TypeBuffer = null_buffer, bool Throw = true>
struct osc_stream
{
Buffer &buffer;
TypeBuffer *type_buffer;
bool error;
osc_stream(Buffer &_buffer) : buffer(_buffer), type_buffer(NULL), error(false) {}
osc_stream(Buffer &_buffer, TypeBuffer &_type_buffer) : buffer(_buffer), type_buffer(&_type_buffer), error(false) {}
inline void pad()
{
uint32_t zero = 0;
write(&zero, buffer.write_misalignment());
}
inline void read(void *dest, uint32_t bytes)
{
if (!buffer.read((uint8_t *)dest, bytes))
{
#if 0
if (Throw)
throw osc_read_exception();
else
#endif
{
error = true;
memset(dest, 0, bytes);
}
}
}
inline void write(const void *src, uint32_t bytes)
{
if (!buffer.write((const uint8_t *)src, bytes))
{
#if 0
if (Throw)
throw osc_write_exception();
else
#endif
error = true;
}
}
inline void clear()
{
buffer.clear();
if (type_buffer)
type_buffer->clear();
}
inline void write_type(char ch)
{
if (type_buffer)
type_buffer->write((uint8_t *)&ch, 1);
}
};
typedef osc_stream<string_buffer> osc_strstream;
typedef osc_stream<string_buffer, string_buffer> osc_typed_strstream;
struct osc_inline_strstream: public string_buffer, public osc_strstream
{
osc_inline_strstream()
: string_buffer(), osc_strstream(static_cast<string_buffer &>(*this))
{
}
};
struct osc_str_typed_buffer_pair
{
string_buffer buf_data, buf_types;
};
struct osc_inline_typed_strstream: public osc_str_typed_buffer_pair, public osc_typed_strstream
{
osc_inline_typed_strstream()
: osc_str_typed_buffer_pair(), osc_typed_strstream(buf_data, buf_types)
{
}
};
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, uint32_t val)
{
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, uint32_t &val)
{
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, int32_t &val)
{
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, float val)
{
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, float &val)
{
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, const std::string &str)
{
s.write(&str[0], str.length());
s.pad();
s.write_type(osc_string);
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, std::string &str)
{
// inefficient...
char five[5];
five[4] = '\0';
str.resize(0);
while(1)
{
s.read(five, 4);
if (five[0] == '\0')
break;
str += five;
if (!five[1] || !five[2] || !five[3])
break;
}
return s;
}
template<class Buffer, class TypeBuffer, class DestBuffer>
inline osc_stream<Buffer, TypeBuffer> &
read_buffer_from_osc_stream(osc_stream<Buffer, TypeBuffer> &s, DestBuffer &buf)
{
return s;
}
template<class Buffer, class TypeBuffer, class SrcBuffer>
inline osc_stream<Buffer, TypeBuffer> &
write_buffer_to_osc_stream(osc_stream<Buffer, TypeBuffer> &s, SrcBuffer &buf)
{
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, raw_buffer &str)
{
return read_buffer_from_osc_stream(s, str);
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, string_buffer &str)
{
return read_buffer_from_osc_stream(s, str);
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, raw_buffer &str)
{
return write_buffer_to_osc_stream(s, str);
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, string_buffer &str)
{
return write_buffer_to_osc_stream(s, str);
}
// XXXKF: I don't support reading binary blobs yet
#if 0
struct osc_net_bad_address: public std::exception
{
std::string addr, error_msg;
osc_net_bad_address(const char *_addr)
{
addr = _addr;
error_msg = "Incorrect OSC URI: " + addr;
}
virtual const char *what() const throw() { return error_msg.c_str(); }
virtual ~osc_net_bad_address() throw () {}
};
struct osc_net_exception: public std::exception
{
int net_errno;
std::string command, error_msg;
osc_net_exception(const char *cmd, int _errno = errno)
{
command = cmd;
net_errno = _errno;
error_msg = "OSC error in "+command+": "+strerror(_errno);
}
virtual const char *what() const throw() { return error_msg.c_str(); }
virtual ~osc_net_exception() throw () {}
};
struct osc_net_dns_exception: public std::exception
{
int net_errno;
std::string command, error_msg;
virtual const char *what() const throw() { return error_msg.c_str(); }
virtual ~osc_net_dns_exception() throw () {}
};
#endif
template<class OscStream>
struct osc_message_sink
{
virtual void receive_osc_message(std::string address, std::string type_tag, OscStream &buffer)=0;
virtual ~osc_message_sink() {}
};
template<class OscStream, class DumpStream>
struct osc_message_dump: public osc_message_sink<OscStream>
{
DumpStream &stream;
osc_message_dump(DumpStream &_stream) : stream(_stream) {}
virtual void receive_osc_message(std::string address, std::string type_tag, OscStream &buffer)
{
int pos = buffer.buffer.tell();
stream << "address: " << address << ", type tag: " << type_tag << std::endl;
for (unsigned int i = 0; i < type_tag.size(); i++)
{
stream << "Argument " << i << " is ";
switch(type_tag[i])
{
case 'i':
{
uint32_t val;
buffer >> val;
stream << val;
break;
}
case 'f':
{
float val;
buffer >> val;
stream << val;
break;
}
case 's':
{
std::string val;
buffer >> val;
stream << val;
break;
}
case 'b':
{
osctl::string_buffer val;
buffer >> val;
stream << "blob (" << val.data.length() << " bytes)";
break;
}
default:
{
stream << "unknown - cannot parse more arguments" << std::endl;
i = type_tag.size();
break;
}
}
stream << std::endl;
}
stream << std::flush;
buffer.buffer.seek(pos);
}
};
};
#endif

View File

@@ -0,0 +1,105 @@
/* Calf DSP Library
* Plugin introspection interface
*
* Copyright (C) 2008 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_PLUGININFO_H
#define __CALF_PLUGININFO_H
#include <string>
namespace calf_plugins {
/// A sink to send information about an audio port
struct plain_port_info_iface
{
/// Called if it's an input port
virtual plain_port_info_iface& input() { return *this; }
/// Called if it's an output port
virtual plain_port_info_iface& output() { return *this; }
virtual plain_port_info_iface& lv2_ttl(const std::string &text) { return *this; }
virtual ~plain_port_info_iface() {}
};
/// A sink to send information about a control port (very incomplete, missing stuff: units, integer, boolean, toggled, notAutomatic, notGUI...)
struct control_port_info_iface
{
/// Called if it's an input port
virtual control_port_info_iface& input() { return *this; }
/// Called if it's an output port
virtual control_port_info_iface& output() { return *this; }
/// Called to mark the port as using linear range [from, to]
virtual control_port_info_iface& lin_range(double from, double to) { return *this; }
/// Called to mark the port as using log range [from, to]
virtual control_port_info_iface& log_range(double from, double to) { return *this; }
virtual control_port_info_iface& toggle() { return *this; }
virtual control_port_info_iface& trigger() { return *this; }
virtual control_port_info_iface& integer() { return *this; }
virtual control_port_info_iface& lv2_ttl(const std::string &text) { return *this; }
virtual control_port_info_iface& polymorphic() { return lv2_ttl("a poly:PolymorphicPort ;"); }
virtual control_port_info_iface& poly_audio() { return lv2_ttl("poly:supportsType lv2:AudioPort ;"); }
virtual ~control_port_info_iface() {}
};
/// A sink to send information about a plugin
struct plugin_info_iface
{
/// Set plugin names (ID, name and label)
virtual void names(const std::string &name, const std::string &label, const std::string &category, const std::string &microname = std::string()) {}
/// Add an audio port (returns a sink which accepts further description)
virtual plain_port_info_iface &audio_port(const std::string &id, const std::string &name, const std::string &microname = std::string("N/A"))=0;
/// Add an event port (returns a sink which accepts further description)
virtual plain_port_info_iface &event_port(const std::string &id, const std::string &name, const std::string &microname = std::string("N/A"))=0;
/// Add a control port (returns a sink which accepts further description)
virtual control_port_info_iface &control_port(const std::string &id, const std::string &name, double def_value, const std::string &microname = "N/A")=0;
/// Add arbitrary TTL clauses
virtual void lv2_ttl(const std::string &text) {}
/// Add small plugin GUI
virtual void has_gui() { lv2_ttl("uiext:ui <http://calf.sourceforge.net/small_plugins/gui/gtk2-gui> ;"); }
/// Called after plugin has reported all the information
virtual void finalize() {}
virtual ~plugin_info_iface() {}
};
struct plugin_port_type_grabber: public plugin_info_iface, public control_port_info_iface
{
uint32_t &target;
uint32_t index;
plain_port_info_iface pp;
control_port_info_iface cp;
plugin_port_type_grabber(uint32_t &_target) : target(_target), index(0) { target = 0; }
virtual plain_port_info_iface &audio_port(const std::string &id, const std::string &name, const std::string &microname = std::string("N/A")) { target |= (1 << index); index++; return pp; }
virtual plain_port_info_iface &event_port(const std::string &id, const std::string &name, const std::string &microname = std::string("N/A")) { index++; return pp; }
virtual control_port_info_iface &control_port(const std::string &id, const std::string &name, double def_value, const std::string &microname = "N/A") { index++; return cp; }
};
/// A sink to send information about plugins
struct plugin_list_info_iface
{
/// Add an empty plugin object and return the sink to be filled with information
virtual plugin_info_iface &plugin(const std::string &id) = 0;
virtual ~plugin_list_info_iface() {}
};
};
#endif

View File

@@ -0,0 +1,142 @@
/* Calf DSP Library
* Preset management
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_PRESET_H
#define __CALF_PRESET_H
#include <vector>
#include <string>
#include <map>
#include <sstream>
#include <ostream>
#include <string.h>
#include "utils.h"
namespace calf_plugins {
class plugin_ctl_iface;
/// Contents of single preset
struct plugin_preset
{
/// Bank the preset belongs to (not used yet)
int bank;
/// Program number of the preset (not used yet)
int program;
/// Name of the preset
std::string name;
/// Name of the plugin the preset is for
std::string plugin;
/// Names of parameters in values array (for each item in param_names there should be a counterpart in values)
std::vector<std::string> param_names;
/// Values of parameters
std::vector<float> values;
/// DSSI configure-style variables
std::map<std::string, std::string> variables;
plugin_preset() : bank(0), program(0) {}
/// Export preset as XML
std::string to_xml();
/// "Upload" preset content to the plugin
void activate(plugin_ctl_iface *plugin);
/// "Download" preset content from the plugin
void get_from(plugin_ctl_iface *plugin);
std::string get_safe_name();
};
/// Exception thrown by preset system
struct preset_exception
{
std::string message, param, fulltext;
int error;
preset_exception(const std::string &_message, const std::string &_param, int _error)
: message(_message), param(_param), error(_error)
{
}
const char *what() {
if (error)
fulltext = message + " " + param + " (" + strerror(error) + ")";
else
fulltext = message + " " + param;
return fulltext.c_str();
}
~preset_exception()
{
}
};
/// A vector of presets
typedef std::vector<plugin_preset> preset_vector;
/// A single list of presets (usually there are two - @see get_builtin_presets(), get_user_presets() )
struct preset_list
{
/// Parser states
enum parser_state
{
START, ///< Beginning of parsing process (before root element)
LIST, ///< Inside root element
PRESET, ///< Inside preset definition
VALUE, ///< Inside (empty) param tag
VAR, ///< Inside (non-empty) var tag
} state;
/// Contained presets (usually for all plugins)
preset_vector presets;
/// Temporary preset used during parsing process
plugin_preset parser_preset;
/// Preset number counters for DSSI (currently broken)
std::map<std::string, int> last_preset_ids;
/// The key used in current <var name="key"> tag (for state == VAR)
std::string current_key;
/// Return the name of the built-in or user-defined preset file
static std::string get_preset_filename(bool builtin);
/// Load default preset list (built-in or user-defined)
bool load_defaults(bool builtin);
void parse(const std::string &data);
/// Load preset list from XML file
void load(const char *filename);
/// Save preset list as XML file
void save(const char *filename);
/// Append or replace a preset (replaces a preset with the same plugin and preset name)
void add(const plugin_preset &sp);
/// Get a sublist of presets for a given plugin (those with plugin_preset::plugin == plugin)
void get_for_plugin(preset_vector &vec, const char *plugin);
protected:
/// Internal function: start element handler for expat
static void xml_start_element_handler(void *user_data, const char *name, const char *attrs[]);
/// Internal function: end element handler for expat
static void xml_end_element_handler(void *user_data, const char *name);
/// Internal function: character data (tag text content) handler for expat
static void xml_character_data_handler(void *user_data, const char *data, int len);
};
/// Return the current list of built-in (factory) presets (these are loaded from system-wide file)
extern preset_list &get_builtin_presets();
/// Return the current list of user-defined presets (these are loaded from ~/.calfpresets)
extern preset_list &get_user_presets();
};
#endif

View File

@@ -0,0 +1,516 @@
/* Calf DSP Library
* DSP primitives.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_PRIMITIVES_H
#define __CALF_PRIMITIVES_H
#include <stack>
#include <map>
#include <cmath>
#include <cstdlib>
#include <stdint.h>
#include <stdio.h>
namespace dsp {
/// Set a float to zero
inline void zero(float &v) {
v = 0;
};
/// Set a double to zero
inline void zero(double &v) {
v = 0;
};
/// Set 64-bit unsigned integer value to zero
inline void zero(uint64_t &v) { v = 0; };
/// Set 32-bit unsigned integer value to zero
inline void zero(uint32_t &v) { v = 0; };
/// Set 16-bit unsigned integer value to zero
inline void zero(uint16_t &v) { v = 0; };
/// Set 8-bit unsigned integer value to zero
inline void zero(uint8_t &v) { v = 0; };
/// Set 64-bit signed integer value to zero
inline void zero(int64_t &v) { v = 0; };
/// Set 32-bit signed integer value to zero
inline void zero(int32_t &v) { v = 0; };
/// Set 16-bit signed integer value to zero
inline void zero(int16_t &v) { v = 0; };
/// Set 8-bit signed integer value to zero
inline void zero(int8_t &v) { v = 0; };
/// Set array (buffer or anything similar) to vector of zeroes
template<class T>
void zero(T *data, unsigned int size) {
T value;
dsp::zero(value);
for (unsigned int i=0; i<size; i++)
*data++ = value;
}
template<class T = float>struct stereo_sample {
T left;
T right;
/// default constructor - preserves T's semantics (ie. no implicit initialization to 0)
inline stereo_sample() {
}
inline stereo_sample(T _left, T _right) {
left = _left;
right = _right;
}
inline stereo_sample(T _both) {
left = right = _both;
}
template<typename U>
inline stereo_sample(const stereo_sample<U> &value) {
left = value.left;
right = value.right;
}
inline stereo_sample& operator=(const T &value) {
left = right = value;
return *this;
}
template<typename U>
inline stereo_sample& operator=(const stereo_sample<U> &value) {
left = value.left;
right = value.right;
return *this;
}
/*
inline operator T() const {
return (left+right)/2;
}
*/
inline stereo_sample& operator*=(const T &multiplier) {
left *= multiplier;
right *= multiplier;
return *this;
}
inline stereo_sample& operator+=(const stereo_sample<T> &value) {
left += value.left;
right += value.right;
return *this;
}
inline stereo_sample& operator-=(const stereo_sample<T> &value) {
left -= value.left;
right -= value.right;
return *this;
}
template<typename U> inline stereo_sample<U> operator*(const U &value) const {
return stereo_sample<U>(left*value, right*value);
}
/*inline stereo_sample<float> operator*(float value) const {
return stereo_sample<float>(left*value, right*value);
}
inline stereo_sample<double> operator*(double value) const {
return stereo_sample<double>(left*value, right*value);
}*/
inline stereo_sample<T> operator+(const stereo_sample<T> &value) {
return stereo_sample(left+value.left, right+value.right);
}
inline stereo_sample<T> operator-(const stereo_sample<T> &value) {
return stereo_sample(left-value.left, right-value.right);
}
inline stereo_sample<T> operator+(const T &value) {
return stereo_sample(left+value, right+value);
}
inline stereo_sample<T> operator-(const T &value) {
return stereo_sample(left-value, right-value);
}
inline stereo_sample<float> operator+(float value) {
return stereo_sample<float>(left+value, right+value);
}
inline stereo_sample<float> operator-(float value) {
return stereo_sample<float>(left-value, right-value);
}
inline stereo_sample<double> operator+(double value) {
return stereo_sample<double>(left+value, right+value);
}
inline stereo_sample<double> operator-(double value) {
return stereo_sample<double>(left-value, right-value);
}
};
/// Multiply constant by stereo_value
template<class T>
inline stereo_sample<T> operator*(const T &value, const stereo_sample<T> &value2) {
return stereo_sample<T>(value2.left*value, value2.right*value);
}
/// Add constant to stereo_value
template<class T>
inline stereo_sample<T> operator+(const T &value, const stereo_sample<T> &value2) {
return stereo_sample<T>(value2.left+value, value2.right+value);
}
/// Subtract stereo_value from constant (yields stereo_value of course)
template<class T>
inline stereo_sample<T> operator-(const T &value, const stereo_sample<T> &value2) {
return stereo_sample<T>(value-value2.left, value-value2.right);
}
/// Shift value right by 'bits' bits (multiply by 2^-bits)
template<typename T>
inline stereo_sample<T> shr(stereo_sample<T> v, int bits = 1) {
v.left = shr(v.left, bits);
v.right = shr(v.right, bits);
return v;
}
/// Set a stereo_sample<T> value to zero
template<typename T>
inline void zero(stereo_sample<T> &v) {
dsp::zero(v.left);
dsp::zero(v.right);
}
/// 'Small value' for integer and other types
template<typename T>
inline T small_value() {
return 0;
}
/// 'Small value' for floats (2^-24) - used for primitive underrun prevention. The value is pretty much arbitrary (allowing for 24-bit signals normalized to 1.0).
template<>
inline float small_value<float>() {
return (1.0/16777216.0); // allows for 2^-24, should be enough for 24-bit DACs at least :)
}
/// 'Small value' for doubles (2^-24) - used for primitive underrun prevention. The value is pretty much arbitrary.
template<>
inline double small_value<double>() {
return (1.0/16777216.0);
}
/// Convert a single value to single value = do nothing :) (but it's a generic with specialisation for stereo_sample)
template<typename T>
inline float mono(T v) {
return v;
}
/// Convert a stereo_sample to single value by averaging two channels
template<typename T>
inline T mono(stereo_sample<T> v) {
return shr(v.left+v.right);
}
/// Clip a value to [min, max]
template<typename T>
inline T clip(T value, T min, T max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
/// Clip a double to [-1.0, +1.0]
inline double clip11(double value) {
double a = fabs(value);
if (a<=1) return value;
return (a<0) ? -1.0 : 1.0;
}
/// Clip a float to [-1.0f, +1.0f]
inline float clip11(float value) {
float a = fabsf(value);
if (a<=1) return value;
return (a<0) ? -1.0f : 1.0f;
}
/// Clip a double to [0.0, +1.0]
inline double clip01(double value) {
double a = fabs(value-0.5);
if (a<=0.5) return value;
return (a<0) ? -0.0 : 1.0;
}
/// Clip a float to [0.0f, +1.0f]
inline float clip01(float value) {
float a = fabsf(value-0.5f);
if (a<=0.5f) return value;
return (a<0) ? -0.0f : 1.0f;
}
// Linear interpolation (mix-way between v1 and v2).
template<typename T, typename U>
inline T lerp(T v1, T v2, U mix) {
return v1+(v2-v1)*mix;
}
// Linear interpolation for stereo values (mix-way between v1 and v2).
template<typename T>
inline stereo_sample<T> lerp(stereo_sample<T> &v1, stereo_sample<T> &v2, float mix) {
return stereo_sample<T>(v1.left+(v2.left-v1.left)*mix, v1.right+(v2.right-v1.right)*mix);
}
/**
* decay-only envelope (linear or exponential); deactivates itself when it goes below a set point (epsilon)
*/
class decay
{
double value, initial;
unsigned int age, mask;
bool active;
public:
decay() {
active = false;
mask = 127;
initial = value = 0.0;
}
inline bool get_active() {
return active;
}
inline double get() {
return active ? value : 0.0;
}
inline void set(double v) {
initial = value = v;
active = true;
age = 0;
}
/// reinitialise envelope (must be called if shape changes from linear to exponential or vice versa in the middle of envelope)
inline void reinit()
{
initial = value;
age = 1;
}
inline void add(double v) {
if (active)
value += v;
else
value = v;
initial = value;
age = 0;
active = true;
}
static inline double calc_exp_constant(double times, double cycles)
{
if (cycles < 1.0)
cycles = 1.0;
return pow(times, 1.0 / cycles);
}
inline void age_exp(double constant, double epsilon) {
if (active) {
if (!(age & mask))
value = initial * pow(constant, (double)age);
else
value *= constant;
if (value < epsilon)
active = false;
age++;
}
}
inline void age_lin(double constant, double epsilon) {
if (active) {
if (!(age & mask))
value = initial - constant * age;
else
value -= constant;
if (value < epsilon)
active = false;
age++;
}
}
inline void deactivate() {
active = false;
value = 0;
}
};
class scheduler;
class task {
public:
virtual void execute(scheduler *s)=0;
virtual void dispose() { delete this; }
virtual ~task() {}
};
/// this scheduler is based on std::multimap, so it isn't very fast, I guess
/// maybe some day it should be rewritten to use heapsort or something
/// work in progress, don't use!
class scheduler {
std::multimap<unsigned int, task *> timeline;
unsigned int time, next_task;
bool eob;
class end_buf_task: public task {
public:
scheduler *p;
end_buf_task(scheduler *_p) : p(_p) {}
virtual void execute(scheduler *s) { p->eob = true; }
virtual void dispose() { }
} eobt;
public:
scheduler()
: time(0)
, next_task((unsigned)-1)
, eob(true)
, eobt (this)
{
time = 0;
next_task = (unsigned)-1;
eob = false;
}
inline bool is_next_tick() {
if (time < next_task)
return true;
do_tasks();
}
inline void next_tick() {
time++;
}
void set(int pos, task *t) {
timeline.insert(std::pair<unsigned int, task *>(time+pos, t));
next_task = timeline.begin()->first;
}
void do_tasks() {
std::multimap<unsigned int, task *>::iterator i = timeline.begin();
while(i != timeline.end() && i->first == time) {
i->second->execute(this);
i->second->dispose();
timeline.erase(i);
}
}
bool is_eob() {
return eob;
}
void set_buffer_size(int count) {
set(count, &eobt);
}
};
/**
* Force "small enough" float value to zero
*/
inline void sanitize(float &value)
{
if (std::abs(value) < small_value<float>())
value = 0.f;
}
/**
* Force "small enough" double value to zero
*/
inline void sanitize(double &value)
{
if (std::abs(value) < small_value<double>())
value = 0.f;
}
/**
* Force "small enough" stereo value to zero
*/
template<class T>
inline void sanitize(stereo_sample<T> &value)
{
sanitize(value.left);
sanitize(value.right);
}
inline float fract16(unsigned int value)
{
return (value & 0xFFFF) * (1.0 / 65536.0);
}
/**
* typical precalculated sine table
*/
template<class T, int N, int Multiplier>
class sine_table
{
public:
static bool initialized;
static T data[N+1];
sine_table() {
if (initialized)
return;
initialized = true;
for (int i=0; i<N+1; i++)
data[i] = (T)(Multiplier*sin(i*2*M_PI*(1.0/N)));
}
};
template<class T, int N, int Multiplier>
bool sine_table<T,N,Multiplier>::initialized = false;
template<class T, int N, int Multiplier>
T sine_table<T,N,Multiplier>::data[N+1];
/// fast float to int conversion using default rounding mode
inline int fastf2i_drm(float f)
{
#ifdef __X86__
volatile int v;
__asm ( "flds %1; fistpl %0" : "=m"(v) : "m"(f));
return v;
#else
return (int)nearbyintf(f);
#endif
}
/// Convert MIDI note to frequency in Hz.
inline float note_to_hz(double note, double detune_cents = 0.0)
{
return 440 * pow(2.0, (note - 69 + detune_cents/100.0) / 12.0);
}
/// Hermite interpolation between two points and slopes in normalized range (written after Wikipedia article)
/// @arg t normalized x coordinate (0-1 over the interval in question)
/// @arg p0 first point
/// @arg p1 second point
/// @arg m0 first slope (multiply by interval width when using over non-1-wide interval)
/// @arg m1 second slope (multiply by interval width when using over non-1-wide interval)
inline float normalized_hermite(float t, float p0, float p1, float m0, float m1)
{
float t2 = t*t;
float t3 = t2*t;
return (2*t3 - 3*t2 + 1) * p0 + (t3 - 2*t2 + t) * m0 + (-2*t3 + 3*t2) * p1 + (t3-t2) * m1;
}
/// Hermite interpolation between two points and slopes
/// @arg x point within interval (x0 <= x <= x1)
/// @arg x0 interval start
/// @arg x1 interval end
/// @arg p0 value at x0
/// @arg p1 value at x1
/// @arg m0 slope (steepness, tangent) at x0
/// @arg m1 slope at x1
inline float hermite_interpolation(float x, float x0, float x1, float p0, float p1, float m0, float m1)
{
float width = x1 - x0;
float t = (x - x0) / width;
m0 *= width;
m1 *= width;
float t2 = t*t;
float t3 = t2*t;
float ct0 = p0;
float ct1 = m0;
float ct2 = -3 * p0 - 2 * m0 + 3 * p1 - m1;
float ct3 = 2 * p0 + m0 - 2 * p1 + m1;
return ct3 * t3 + ct2 * t2 + ct1 * t + ct0;
//return (2*t3 - 3*t2 + 1) * p0 + (t3 - 2*t2 + t) * m0 + (-2*t3 + 3*t2) * p1 + (t3-t2) * m1;
}
};
#endif

View File

@@ -0,0 +1,228 @@
/* Calf DSP Library
* Framework for synthesizer-like plugins. This is based
* on my earlier work on Drawbar electric organ emulator.
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_SYNTH_H
#define __CALF_SYNTH_H
#include <list>
#include <stack>
#include <bitset>
#include "primitives.h"
#include "audio_fx.h"
namespace dsp {
/**
* A kind of set with fast non-ordered iteration, used for storing lists of pressed keys.
*/
class keystack {
private:
int dcount;
uint8_t active[128];
uint8_t states[128];
public:
keystack() {
memset(states, 0xFF, sizeof(states));
dcount = 0;
}
void clear() {
for (int i=0; i<dcount; i++)
states[active[i]] = 0xFF;
dcount = 0;
}
bool push(int key) {
assert(key >= 0 && key <= 127);
if (states[key] != 0xFF) {
return true;
}
states[key] = dcount;
active[dcount++] = key;
return false;
}
bool pop(int key) {
if (states[key] == 0xFF)
return false;
int pos = states[key];
if (pos != dcount-1) {
// reuse the popped item's stack position for stack top
int last = active[dcount-1];
active[pos] = last;
// mark that position's new place on stack
states[last] = pos;
}
states[key] = 0xFF;
dcount--;
return true;
}
inline bool has(int key) {
return states[key] != 0xFF;
}
inline int count() {
return dcount;
}
inline bool empty() {
return (dcount == 0);
}
inline int nth(int n) {
return active[n];
}
};
/**
* Convert MIDI note number to normalized UINT phase (where 1<<32 is full cycle).
* @param MIDI note number
* @param cents detune in cents (1/100 of a semitone)
* @param sr sample rate
*/
inline unsigned int midi_note_to_phase(int note, double cents, int sr) {
double incphase = 440*pow(2.0, (note-69)/12.0 + cents/1200.0)/sr;
if (incphase >= 1.0) incphase = fmod(incphase, 1.0);
incphase *= 65536.0*65536.0;
return (unsigned int)incphase;
}
// Base class for all voice objects
class voice {
public:
int sample_rate;
bool released, sostenuto, stolen;
voice() : sample_rate(-1), released(false), sostenuto(false), stolen(false) {}
/// reset voice to default state (used when a voice is to be reused)
virtual void setup(int sr) { sample_rate = sr; }
/// reset voice to default state (used when a voice is to be reused)
virtual void reset()=0;
/// a note was pressed
virtual void note_on(int note, int vel)=0;
/// a note was released
virtual void note_off(int vel)=0;
/// check if voice can be removed from active voice list
virtual bool get_active()=0;
/// render voice data to buffer
virtual void render_to(float (*buf)[2], int nsamples)=0;
/// very fast note off
virtual void steal()=0;
/// return the note used by this voice
virtual int get_current_note()=0;
virtual float get_priority() { return stolen ? 20000 : (released ? 1 : (sostenuto ? 200 : 100)); }
/// empty virtual destructor
virtual ~voice() {}
};
/// An "optimized" voice class using fixed-size processing units
/// and fixed number of channels. The drawback is that voice
/// control is not sample-accurate, and no modulation input
/// is possible, but it should be good enough for most cases
/// (like Calf Organ).
template<class Base>
class block_voice: public Base {
public:
// derived from Base
// enum { Channels = 2 };
using Base::Channels;
// enum { BlockSize = 16 };
using Base::BlockSize;
// float output_buffer[BlockSize][Channels];
using Base::output_buffer;
// void render_block();
using Base::render_block;
unsigned int read_ptr;
block_voice()
{
read_ptr = BlockSize;
}
virtual void reset()
{
Base::reset();
read_ptr = BlockSize;
}
virtual void render_to(float (*buf)[2], int nsamples)
{
int p = 0;
while(p < nsamples)
{
if (read_ptr == BlockSize)
{
render_block();
read_ptr = 0;
}
int ncopy = std::min<int>(BlockSize - read_ptr, nsamples - p);
for (int i = 0; i < ncopy; i++)
for (int c = 0; c < Channels; c++)
buf[p + i][c] += output_buffer[read_ptr + i][c];
p += ncopy;
read_ptr += ncopy;
}
}
};
/// Base class for all kinds of polyphonic instruments, provides
/// somewhat reasonable voice management, pedal support - and
/// little else. It's implemented as a base class with virtual
/// functions, so there's some performance loss, but it shouldn't
/// be horrible.
/// @todo it would make sense to support all notes off controller too
struct basic_synth {
protected:
/// Current sample rate
int sample_rate;
/// Hold pedal state
bool hold;
/// Sostenuto pedal state
bool sostenuto;
/// Voices currently playing
std::list<dsp::voice *> active_voices;
/// Voices allocated, but not used
std::stack<dsp::voice *> unused_voices;
/// Gate values for all 128 MIDI notes
std::bitset<128> gate;
/// Maximum allocated number of channels
unsigned int polyphony_limit;
void kill_note(int note, int vel, bool just_one);
public:
virtual void setup(int sr) {
sample_rate = sr;
hold = false;
sostenuto = false;
polyphony_limit = (unsigned)-1;
}
virtual void trim_voices();
virtual dsp::voice *give_voice();
virtual dsp::voice *alloc_voice()=0;
virtual dsp::voice *steal_voice();
virtual void render_to(float (*output)[2], int nsamples);
virtual void note_on(int note, int vel);
virtual void percussion_note_on(int note, int vel) {}
virtual void control_change(int ctl, int val);
virtual void note_off(int note, int vel);
/// amt = -8192 to 8191
virtual void pitch_bend(int amt) {}
virtual void on_pedal_release();
virtual bool check_percussion() { return active_voices.empty(); }
virtual ~basic_synth();
};
}
#endif

View File

@@ -0,0 +1,161 @@
/* Calf DSP Library
* Utilities
*
* Copyright (C) 2008 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_UTILS_H
#define __CALF_UTILS_H
#include <errno.h>
#include <pthread.h>
#include <map>
#include <string>
namespace calf_utils
{
/// Pthreads based mutex class
class ptmutex
{
public:
pthread_mutex_t pm;
ptmutex(int type = PTHREAD_MUTEX_RECURSIVE)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, type);
pthread_mutex_init(&pm, &attr);
pthread_mutexattr_destroy(&attr);
}
bool lock()
{
return pthread_mutex_lock(&pm) == 0;
}
bool trylock()
{
return pthread_mutex_trylock(&pm) == 0;
}
void unlock()
{
pthread_mutex_unlock(&pm);
}
~ptmutex()
{
pthread_mutex_destroy(&pm);
}
};
/// Exception-safe mutex lock
class ptlock
{
ptmutex &mutex;
bool locked;
public:
ptlock(ptmutex &_m) : mutex(_m), locked(true)
{
mutex.lock();
}
void unlock()
{
mutex.unlock();
locked = false;
}
void unlocked()
{
locked = false;
}
~ptlock()
{
if (locked)
mutex.unlock();
}
};
/// Exception-safe temporary assignment
template<class T>
class scope_assign
{
T &data, old_value;
public:
scope_assign(T &_data, T new_value)
: data(_data), old_value(_data)
{
data = new_value;
}
~scope_assign()
{
data = old_value;
}
};
struct text_exception: public std::exception
{
const char *text;
std::string container;
public:
text_exception(const std::string &t) : container(t) { text = container.c_str(); }
virtual const char *what() const throw () { return text; }
virtual ~text_exception() throw () {}
};
struct file_exception: public std::exception
{
const char *text;
std::string message, filename, container;
public:
file_exception(const std::string &f) : message(strerror(errno)), filename(f), container(filename + ":" + message) { text = container.c_str(); }
file_exception(const std::string &f, const std::string &t) : message(t), filename(f), container(filename + ":" + message) { text = container.c_str(); }
virtual const char *what() const throw () { return text; }
virtual ~file_exception() throw () {}
};
/// String-to-string mapping
typedef std::map<std::string, std::string> dictionary;
/// Serialize a dictonary to a string
extern std::string encode_map(const dictionary &data);
/// Deserialize a dictonary from a string
extern void decode_map(dictionary &data, const std::string &src);
/// int-to-string
extern std::string i2s(int value);
/// float-to-string
extern std::string f2s(double value);
/// float-to-string-that-doesn't-resemble-an-int
extern std::string ff2s(double value);
/// Escape a string to be used in XML file
std::string xml_escape(const std::string &src);
/// Load file from disk into a std::string blob, or throw file_exception
std::string load_file(const std::string &src);
/// Indent a string by another string (prefix each line)
std::string indent(const std::string &src, const std::string &indent);
};
#endif

View File

@@ -0,0 +1,325 @@
/* Calf DSP Library
* Module wrapper methods.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <assert.h>
#include <memory.h>
#include <calf/giface.h>
#include <stdio.h>
using namespace std;
using namespace calf_utils;
using namespace calf_plugins;
float parameter_properties::from_01(double value01) const
{
double value = dsp::clip(value01, 0., 1.);
switch(flags & PF_SCALEMASK)
{
case PF_SCALE_DEFAULT:
case PF_SCALE_LINEAR:
case PF_SCALE_PERC:
default:
value = min + (max - min) * value01;
break;
case PF_SCALE_QUAD:
value = min + (max - min) * value01 * value01;
break;
case PF_SCALE_LOG:
value = min * pow(double(max / min), value01);
break;
case PF_SCALE_GAIN:
if (value01 < 0.00001)
value = min;
else {
float rmin = std::max(1.0f / 1024.0f, min);
value = rmin * pow(double(max / rmin), value01);
}
break;
case PF_SCALE_LOG_INF:
assert(step);
if (value01 > (step - 1.0) / step)
value = FAKE_INFINITY;
else
value = min * pow(double(max / min), value01 * step / (step - 1.0));
break;
}
switch(flags & PF_TYPEMASK)
{
case PF_INT:
case PF_BOOL:
case PF_ENUM:
case PF_ENUM_MULTI:
if (value > 0)
value = (int)(value + 0.5);
else
value = (int)(value - 0.5);
break;
}
return value;
}
double parameter_properties::to_01(float value) const
{
switch(flags & PF_SCALEMASK)
{
case PF_SCALE_DEFAULT:
case PF_SCALE_LINEAR:
case PF_SCALE_PERC:
default:
return double(value - min) / (max - min);
case PF_SCALE_QUAD:
return sqrt(double(value - min) / (max - min));
case PF_SCALE_LOG:
value /= min;
return log((double)value) / log((double)max / min);
case PF_SCALE_LOG_INF:
if (IS_FAKE_INFINITY(value))
return max;
value /= min;
assert(step);
return (step - 1.0) * log((double)value) / (step * log((double)max / min));
case PF_SCALE_GAIN:
if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
return 0;
double rmin = std::max(1.0f / 1024.0f, min);
value /= rmin;
return log((double)value) / log(max / rmin);
}
}
float parameter_properties::get_increment() const
{
float increment = 0.01;
if (step > 1)
increment = 1.0 / (step - 1);
else
if (step > 0 && step < 1)
increment = step;
else
if ((flags & PF_TYPEMASK) != PF_FLOAT)
increment = 1.0 / (max - min);
return increment;
}
int parameter_properties::get_char_count() const
{
if ((flags & PF_SCALEMASK) == PF_SCALE_PERC)
return 6;
if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
char buf[256];
size_t len = 0;
sprintf(buf, "%0.0f dB", 6.0 * log(min) / log(2));
len = strlen(buf);
sprintf(buf, "%0.0f dB", 6.0 * log(max) / log(2));
len = std::max(len, strlen(buf)) + 2;
return (int)len;
}
return std::max(to_string(min).length(), std::max(to_string(max).length(), to_string(min + (max-min) * 0.987654).length()));
}
std::string parameter_properties::to_string(float value) const
{
char buf[32];
if ((flags & PF_SCALEMASK) == PF_SCALE_PERC) {
sprintf(buf, "%0.f%%", 100.0 * value);
return string(buf);
}
if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
return "-inf dB"; // XXXKF change to utf-8 infinity
sprintf(buf, "%0.1f dB", 6.0 * log(value) / log(2));
return string(buf);
}
switch(flags & PF_TYPEMASK)
{
case PF_STRING:
return "N/A";
case PF_INT:
case PF_BOOL:
case PF_ENUM:
case PF_ENUM_MULTI:
value = (int)value;
break;
}
if ((flags & PF_SCALEMASK) == PF_SCALE_LOG_INF && IS_FAKE_INFINITY(value))
sprintf(buf, "+inf"); // XXXKF change to utf-8 infinity
else
sprintf(buf, "%g", value);
switch(flags & PF_UNITMASK) {
case PF_UNIT_DB: return string(buf) + " dB";
case PF_UNIT_HZ: return string(buf) + " Hz";
case PF_UNIT_SEC: return string(buf) + " s";
case PF_UNIT_MSEC: return string(buf) + " ms";
case PF_UNIT_CENTS: return string(buf) + " ct";
case PF_UNIT_SEMITONES: return string(buf) + "#";
case PF_UNIT_BPM: return string(buf) + " bpm";
case PF_UNIT_RPM: return string(buf) + " rpm";
case PF_UNIT_DEG: return string(buf) + " deg";
case PF_UNIT_NOTE:
{
static const char *notes = "C C#D D#E F F#G G#A A#B ";
int note = (int)value;
if (note < 0 || note > 127)
return "---";
return string(notes + 2 * (note % 12), 2) + i2s(note / 12 - 2);
}
}
return string(buf);
}
void calf_plugins::plugin_ctl_iface::clear_preset() {
int param_count = get_param_count();
for (int i=0; i < param_count; i++)
{
parameter_properties &pp = *get_param_props(i);
if ((pp.flags & PF_TYPEMASK) == PF_STRING)
{
configure(pp.short_name, pp.choices ? pp.choices[0] : "");
}
else
set_param_value(i, pp.def_value);
}
}
const char *calf_plugins::load_gui_xml(const std::string &plugin_id)
{
#if 0
try {
return strdup(calf_utils::load_file((std::string(PKGLIBDIR) + "/gui-" + plugin_id + ".xml").c_str()).c_str());
}
catch(file_exception e)
#endif
{
return NULL;
}
}
bool calf_plugins::check_for_message_context_ports(parameter_properties *parameters, int count)
{
for (int i = count - 1; i >= 0; i--)
{
if (parameters[i].flags & PF_PROP_MSGCONTEXT)
return true;
}
return false;
}
bool calf_plugins::check_for_string_ports(parameter_properties *parameters, int count)
{
for (int i = count - 1; i >= 0; i--)
{
if ((parameters[i].flags & PF_TYPEMASK) == PF_STRING)
return true;
if ((parameters[i].flags & PF_TYPEMASK) < PF_STRING)
return false;
}
return false;
}
#if USE_DSSI
struct osc_cairo_control: public cairo_iface
{
osctl::osc_inline_typed_strstream &os;
osc_cairo_control(osctl::osc_inline_typed_strstream &_os) : os(_os) {}
virtual void set_source_rgba(float r, float g, float b, float a = 1.f)
{
os << (uint32_t)LGI_SET_RGBA << r << g << b << a;
}
virtual void set_line_width(float width)
{
os << (uint32_t)LGI_SET_WIDTH << width;
}
};
static void send_graph_via_osc(osctl::osc_client &client, const std::string &address, line_graph_iface *graph, std::vector<int> &params)
{
osctl::osc_inline_typed_strstream os;
osc_cairo_control cairoctl(os);
for (size_t i = 0; i < params.size(); i++)
{
int index = params[i];
os << (uint32_t)LGI_GRAPH;
os << (uint32_t)index;
for (int j = 0; ; j++)
{
float data[128];
if (graph->get_graph(index, j, data, 128, &cairoctl))
{
os << (uint32_t)LGI_SUBGRAPH;
os << (uint32_t)128;
for (int p = 0; p < 128; p++)
os << data[p];
}
else
break;
}
for (int j = 0; ; j++)
{
float x, y;
int size = 3;
if (graph->get_dot(index, j, x, y, size, &cairoctl))
os << (uint32_t)LGI_DOT << x << y << (uint32_t)size;
else
break;
}
for (int j = 0; ; j++)
{
float pos = 0;
bool vertical = false;
string legend;
if (graph->get_gridline(index, j, pos, vertical, legend, &cairoctl))
os << (uint32_t)LGI_LEGEND << pos << (uint32_t)(vertical ? 1 : 0) << legend;
else
break;
}
os << (uint32_t)LGI_END_ITEM;
}
os << (uint32_t)LGI_END;
client.send(address, os);
}
calf_plugins::dssi_feedback_sender::dssi_feedback_sender(const char *URI, line_graph_iface *_graph, calf_plugins::parameter_properties *props, int num_params)
{
graph = _graph;
client = new osctl::osc_client;
client->bind("0.0.0.0", 0);
client->set_url(URI);
for (int i = 0; i < num_params; i++)
{
if (props[i].flags & PF_PROP_GRAPH)
indices.push_back(i);
}
}
void calf_plugins::dssi_feedback_sender::update()
{
send_graph_via_osc(*client, "/lineGraph", graph, indices);
}
calf_plugins::dssi_feedback_sender::~dssi_feedback_sender()
{
// this would not be received by GUI's main loop because it's already been terminated
// client->send("/iQuit");
delete client;
}
#endif

Some files were not shown because too many files have changed in this diff Show More